BTC
ETH
HTX
SOL
BNB
Xem thị trường
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt

Tìm hiểu cách tạo bộ chia thanh toán mã thông báo ERC20 của riêng bạn trong một bài viết

星球君的朋友们
Odaily资深作者
Bài viết này có khoảng 11750 từ, đọc toàn bộ bài viết mất khoảng 17 phút
Hôm nay, chúng ta sẽ tìm hiểu cách tạo bộ chia thanh toán mã thông báo ERC20 của riêng mình, bộ chia này có thể được tích hợp vào bất kỳ dự án nào!
Tóm tắt AI
Hôm nay, chúng ta sẽ tìm hiểu cách tạo bộ chia thanh toán mã thông báo ERC20 của riêng mình, bộ chia này có thể được tích hợp vào bất kỳ dự án nào!

Thanh toán là một chủ đề định kỳ trong hầu hết mọi lĩnh vực của tiền điện tử, đặc biệt là cung cấp các khoản thanh toán cho nhiều bên đặt cược. Ví dụ: một DAO muốn tài trợ cho nhiều sáng kiến, một DEX muốn hợp nhất để phân phối phí giao dịch cho những người tham gia nhất định hoặc một nhóm muốn phân phối mã thông báo cho các thành viên trong nhóm dưới dạng tiền lương hàng tháng.

tiêu đề cấp đầu tiên

Hôm nay, chúng ta sẽ tìm hiểu cách tạo bộ chia thanh toán mã thông báo ERC20 của riêng mình, bộ chia này có thể được tích hợp vào bất kỳ dự án nào!

Điều kiện tiên quyết và thiết lập

tiêu đề phụ

cấu trúc dự án

Chúng tôi sẽ tạo hai hợp đồng. Đầu tiên sẽ là hợp đồng thông minh phân tách thanh toán bằng mã thông báo ERC20 và thứ hai sẽ là hợp đồng thông minh nhóm mô phỏng. Hợp đồng thông minh của bộ chia thanh toán mã thông báo ERC20 sẽ trừu tượng và chứa logic và dữ liệu để quản lý người gửi và người trả tiền cũng như các phần thanh toán tương ứng của họ. Nhóm mô hình sẽ kế thừa bộ tách thanh toán mã thông báo ERC20 để chúng tôi có thể tự động phân phối các khoản thanh toán cho nhiều bên đặt cược. Có hai lý do để tách chức năng thanh toán thành hai hợp đồng:

  • Chứng minh việc sử dụng hợp đồng phân chia thanh toán mã thông báo trong các trường hợp sử dụng trong thế giới thực

  • Đảm bảo hợp đồng tách thanh toán mã thông báo đủ linh hoạt để mọi người lựa chọn và tích hợp vào các dự án của riêng họ

tiêu đề phụ

Thiết lập môi trường phát triển

Các công cụ trong hướng dẫn này:

  • Hardhat - môi trường phát triển hợp đồng thông minh

  • OpenZeppelin - mẫu hợp đồng thông minh đã được kiểm toán

Bây giờ hãy bắt đầu một dự án NPM với NPM init -y trong một thư mục trống

Sau khi thiết lập dự án, hãy cài đặt Hardhat bằng lệnh sau:

Sau khi cài đặt Hardhat, gõ npx Hardhat và chọn tùy chọn để tạo một dự án ví dụ cơ bản. Điều này sẽ bao gồm một cấu trúc tệp thuận tiện để dễ dàng tạo, kiểm tra và triển khai các hợp đồng của riêng bạn.

Chọn tạo một dự án ví dụ cơ bản

Bạn có thể xóa tệp Greeter.sol trong thư mục hợp đồng và xóa tệp sample-test.js khỏi thư mục thử nghiệm.

Chúng tôi cũng sẽ cài đặt thư viện plugin hardhat, là các plugin Hardhat. Chúng cho phép chúng tôi thêm các công cụ để thử nghiệm và triển khai các hợp đồng thông minh.

Ở đầu tệp hardhat.config.js, hãy thêm

Một gói có tên chai cần được cài đặt để kiểm tra các hợp đồng thông minh của chúng tôi.

tiêu đề phụ

Tạo bộ chia thanh toán mã thông báo

Hợp đồng thông minh phân tách thanh toán mã thông báo này sẽ cung cấp logic để thiết lập và lưu trữ dữ liệu liên quan đến danh sách người được trả tiền và phần chia sẻ của mỗi người được trả tiền. Mỗi người được thanh toán nắm giữ một số cổ phần bằng với tỷ lệ phần trăm số tiền mà họ sẽ nhận được (ví dụ: nếu có 4 người được thanh toán, mỗi người nắm giữ 5 cổ phần, thì mỗi người sẽ nhận được 25% của bất kỳ khoản thanh toán nào).

Để bắt đầu hợp đồng này, chúng tôi sẽ tạo một tệp mới trong thư mục hợp đồng của mình và đặt tên là TokenPaymentSplitter.sol.

Đặt dòng pragma và vỏ hợp đồng.

pragma solidity ^0.8.0;
abstract contract TokenPaymentSplitter {

}

Lưu ý rằng đây là một hợp đồng trừu tượng, chúng tôi sẽ nhập nó vào hợp đồng nhóm mô phỏng sau. Làm cho nó trở nên trừu tượng cũng cho phép chúng tôi dễ dàng nhập hợp đồng này vào bất kỳ dự án thực tế nào khác trong tương lai.

Bây giờ hãy nhập một công cụ hữu ích từ OpenZeppelin.

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract TokenPaymentSplitter {
   using SafeERC20 for IERC20;

}

SafeERC20.sol cung cấp giao diện ERC20 cho phép chúng tôi gọi các chức năng tiêu chuẩn từ bất kỳ hợp đồng thông minh ERC20 nào và đưa các lệnh gọi này vào các chức năng bổ sung để cung cấp cách chuyển mã thông báo an toàn hơn.

Bây giờ, chúng ta sẽ tạo các biến để lưu trữ dữ liệu hợp đồng.

abstract contract TokenPaymentSplitter {
   using SafeERC20 for IERC20;
   
   address internal paymentToken;
   uint256 internal _totalShares;
   uint256 internal _totalTokenReleased;
   address[] internal _payees;
   mapping(address => uint256) internal _shares;
   mapping(address => uint256) internal _tokenReleased;
   

}

paymentToken là địa chỉ của mã thông báo ERC20 mà chúng tôi sử dụng để thanh toán.

_totalShares cung cấp tổng số cổ phần từ tất cả những người được trả tiền.

_totalTokenReleased là tổng số mã thông báo thanh toán đã được thanh toán cho tất cả người nhận thanh toán.

_payees cung cấp một mảng tất cả các địa chỉ người nhận thanh toán hiện tại.

_shares là ánh xạ địa chỉ người nhận với số lượng chia sẻ được chỉ định cho họ.

_tokenRelease là ánh xạ của địa chỉ người nhận với số lượng mã thông báo thanh toán.

Bây giờ hãy đặt một hàm tạo chấp nhận ba tham số. Tham số đầu tiên là một mảng người được trả tiền mà chúng tôi muốn khởi tạo khi triển khai hợp đồng. Tham số thứ hai là một mảng cổ phiếu cho mỗi người được trả tiền. Thứ ba là địa chỉ của mã thông báo ERC20 sẽ được sử dụng để thanh toán.

pragma solidity 0.8.0
constructor(
   address[] memory payees,
   uint256[] memory shares_,
   address _paymentToken
) {
   require(
       payees.length == shares_.length,
       "TokenPaymentSplitter: payees and shares length mismatch"
   );
   require(payees.length > 0, "TokenPaymentSplitter: no payees");
   for (uint256 i = 0; i < payees.length; i++) {
       _addPayee(payees[i], shares_[i]);
   }
   paymentToken = _paymentToken;

}

Hàm tạo chứa một câu lệnh yêu cầu để đảm bảo rằng cả hai mảng có cùng độ dài để mỗi người được trả tiền được chỉ định phần của họ. Có một tuyên bố yêu cầu khác để đảm bảo rằng hợp đồng được khởi tạo với ít nhất một người được trả tiền.

Ngoài ra còn có một vòng lặp for chỉ định từng người được trả tiền và phần chia sẻ của họ cho các biến mà chúng tôi đã tạo ở trên. Điều này được thực hiện với một chức năng gọi là _addPayee mà chúng tôi sẽ tạo ngay sau đây.

Khi hàm tạo đã sẵn sàng, hãy thêm một vài hàm khác để gọi và nhận các biến hợp đồng.

pragma solidity 0.8.0
function totalShares() public view returns (uint256) {
   return _totalShares;
}
function shares(address account) public view returns (uint256) {
   return _shares[account];
}
function payee(uint256 index) public view returns (address) {
   return _payees[index];

}

Bây giờ chúng ta sẽ tạo chức năng thêm người nhận thanh toán.

pragma solidity 0.8.0;
function _addPayee(address account, uint256 shares_) internal {
   require(
       account != address(0),
       "TokenPaymentSplitter: account is the zero address"
   );
   require(shares_ > 0, "TokenPaymentSplitter: shares are 0");
   require(
       _shares[account] == 0,
       "TokenPaymentSplitter: account already has shares"
   );
   _payees.push(account);
   _shares[account] = shares_;
   _totalShares = _totalShares + shares_;

}

_addPayee là hàm chúng ta gọi trong hàm tạo để thiết lập mảng người nhận thanh toán. Hàm này nhận hai tham số, tài khoản của người nhận thanh toán và số lượng cổ phiếu được liên kết với tài khoản đó. Sau đó, nó sẽ kiểm tra xem liệu tài khoản có địa chỉ bằng 0 hay không, nếu phần chia sẻ lớn hơn 0 và liệu tài khoản đã được đăng ký với tư cách là người nhận thanh toán hay chưa. Nếu tất cả các kiểm tra đều vượt qua, thì chúng tôi sẽ thêm dữ liệu vào các biến tương ứng.

Bây giờ, hãy thêm chức năng hỗ trợ phân phối mã thông báo cho người nhận thanh toán.

pragma solidity 0.8.0;
function release(address account) public virtual {
   require(
       _shares[account] > 0, "TokenPaymentSplitter: account has no shares"
   );
   
   uint256 tokenTotalReceived = IERC20(paymentToken).balanceOf(address(this)) + _totalTokenReleased;
   
   uint256 payment = (tokenTotalReceived * _shares[account]) / _totalShares - _tokenReleased[account];
   
   require(payment != 0, "TokenPaymentSplitter: account is not due payment");
   _tokenReleased[account] = _tokenReleased[account] + payment;
   _totalTokenReleased = _totalTokenReleased + payment;
   IERC20(paymentToken).safeTransfer(account, payment);
}

Phát hành là một chức năng mà bất kỳ ai cũng có thể gọi, lấy đối số của tài khoản người nhận thanh toán hiện có. Hãy phân tích những gì xảy ra trong chức năng này. Đầu tiên, nó kiểm tra xem tài khoản có cổ phần được gán cho nó hay không. Sau đó, nó tạo một biến có tên tokenTotalReceived để thêm số dư mã thông báo hiện tại của hợp đồng vào tổng số mã thông báo đã phát hành trước đó. Tạo một biến khác có tên là thanh toán xác định số lượng trong tổng số lượng mã thông báo nhận được thuộc về tài khoản, sau đó trừ đi số tiền đã được giải phóng vào tài khoản. Sau đó, một câu lệnh yêu cầu sẽ kiểm tra xem liệu số tiền thanh toán hiện tại có lớn hơn 0 hay không (nghĩa là hiện đang nợ nhiều mã thông báo hơn). Nếu kiểm tra thành công, hãy cập nhật mã thông báoReleased của tài khoản và cập nhật totalTokenReleased. Cuối cùng, số tiền mã thông báo đã trả cho tài khoản sẽ được chuyển.

Bây giờ chức năng đã sẵn sàng! Nhưng hợp đồng này còn một điều nữa phải làm...sự kiện!

Chúng tôi sẽ thêm hai sự kiện vào hợp đồng, thông lệ tốt là thêm các sự kiện ở đầu hợp đồng.

pragma solidity 0.8.0;
event PayeeAdded(address account, uint256 shares);

event PaymentReleased(address to, uint256 amount);

Sau khi các sự kiện này được đưa vào hợp đồng, chúng tôi sẽ đưa chúng vào các chức năng thích hợp.

pragma solidity 0.8.0;
function _addPayee(address account, uint256 shares_) internal {
   ///existingFunctionCode
   emit PayeeAdded(account, shares_);
}
function release(address account) public virtual {
   ///existingFunctionCode
   emit PaymentReleased(account, payment);

}

tiêu đề phụ

Tạo một hợp đồng pool mô phỏng

Hợp đồng này sẽ không quá phức tạp vì chúng tôi chỉ muốn trình bày cách tích hợp bộ chia thanh toán mã thông báo. Hợp đồng này nhận định kỳ mã thông báo ERC20 cụ thể mà chúng tôi muốn phân phối cho danh sách người nhận thanh toán. Mã thông báo ERC20 này có thể đến thông qua các tình huống khác nhau, chẳng hạn như tiền gửi của người dùng hoặc phí chuyển hướng từ một hợp đồng thông minh khác. Trong cuộc sống thực, tùy thuộc vào dự án, có thể có một hợp đồng phức tạp hơn chứa nhiều chức năng hơn để đáp ứng trường hợp sử dụng của người dùng.

Trong thư mục hợp đồng, tạo một tệp mới có tên MockPool.sol. Sau đó thêm mã sau đây.

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./TokenPaymentSplitter.sol";
contract MockPool is Ownable, TokenPaymentSplitter {
   using SafeERC20 for IERC20;
   constructor(
       address[] memory _payees,
       uint256[] memory _shares,
       address _paymentToken
   ) TokenPaymentSplitter(_payees, _shares, _paymentToken) {}
   
   function drainTo(address _transferTo, address _token) public onlyOwner {
       require(
       _token != paymentToken,
       "MockPool: Token to drain is PaymentToken"
       );
       uint256 balance = IERC20(_token).balanceOf(address(this));
       require(balance > 0, "MockPool: Token to drain balance is 0");
       IERC20(_token).safeTransfer(_transferTo, balance);
   }

}

Trong hợp đồng này, nhập khẩu ba điều. Đầu tiên là tiện ích Ownable của OpenZeppelin, sử dụng công cụ sửa đổi onlyOwner duy nhất trên một số chức năng. Thứ hai là SafeERC20, cho phép chuyển mã thông báo ERC20 an toàn, như sẽ thấy trong hợp đồng. Thứ ba là hợp đồng TokenPaymentSplitter của chúng tôi.

Trong hàm tạo MockPool, chúng tôi cần ba tham số giống nhau từ TokenPaymentSplitter, chúng tôi chỉ chuyển chúng vào hợp đồng kế thừa của mình.

tiêu đề phụ

hợp đồng thử nghiệm

Thử nghiệm hợp đồng thông minh cũng quan trọng như việc tạo ra chúng. Tài sản mà các hợp đồng này xử lý thường thuộc về người khác, vì vậy, với tư cách là nhà phát triển, chúng tôi có trách nhiệm đảm bảo rằng những tài sản đó hoạt động theo cách chúng phải hoạt động và các thử nghiệm của chúng tôi bao gồm hầu hết mọi trường hợp cạnh.

Các thử nghiệm sẽ được thực hiện ở đây là một số ví dụ để cho thấy rằng hợp đồng thông minh TokenPaymentSplitter hoạt động như chúng ta mong đợi. Khi làm việc trên các dự án của riêng bạn, bạn có thể muốn tạo các thử nghiệm được thiết kế riêng cho trường hợp sử dụng của riêng bạn.

Để hỗ trợ thử nghiệm của chúng tôi, chúng tôi muốn bao gồm mã thông báo ERC20, vì điều này, chúng tôi sẽ tạo một tệp solididity mới nhập mẫu OpenZepplin ERC20 để sử dụng trong thử nghiệm của chúng tôi. Trong thư mục hợp đồng, tạo một tệp mới có tên Imports.sol và bao gồm mã sau:

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";

contract Imports {}

Bây giờ, hãy tạo một tệp có tên test.js trong thư mục kiểm tra. Ở đầu tệp này, chúng tôi sẽ nhập các gói hỗ trợ các thử nghiệm của chúng tôi.

const { expect } = require('chai')

const { ethers } = require('hardhat')

Bây giờ, để thiết lập các bài kiểm tra, trước tiên chúng ta sẽ tạo các biến cần thiết, tạo hàm beforeEach, được gọi trước mỗi bài kiểm tra và tạo một hàm mô tả trống, hàm này sẽ sớm chứa các bài kiểm tra của chúng ta.

describe('TokenPaymentSplitter Tests', () => {
let deployer
let account1
let account2
let account3
let account4
let testPaymentToken
let mockPool
beforeEach(async () => {
   [deployer, account1, account2, account3, account4] = await ethers.getSigners()
   const TestPaymentToken = await ethers.getContractFactory('ERC20PresetMinterPauser')
   testPaymentToken = await TestPaymentToken.deploy('TestPaymentToken', 'TPT')
   await testPaymentToken.deployed()
})
 
describe('Add payees with varying amounts and distribute payments', async () => {}

}

tiêu đề cấp đầu tiên

Mã thông báo thanh toán được phân phối đồng đều giữa nhiều người được thanh toán

Trong thử nghiệm đầu tiên của chúng tôi, chúng tôi muốn xem điều gì sẽ xảy ra khi chúng tôi triển khai một hợp đồng có chứa danh sách những người được trả tiền với phần chia đều. Dưới đây là mã kiểm tra.

it('payment token is distributed evenly to multiple payees', async () => {
   payeeAddressArray = [account1.address, account2.address, account3.address, account4.address]
   payeeShareArray = [10, 10, 10, 10]
   const MockPool = await ethers.getContractFactory('MockPool')
   mockPool = await MockPool.deploy(
       payeeAddressArray,
       payeeShareArray,
       testPaymentToken.address
   )
   await mockPool.deployed()
   await testPaymentToken.mint(mockPool.address, 100000)
   await mockPool
       .connect(account1)
       .release(account1.address)
   await mockPool
       .connect(account2)
       .release(account2.address)
   await mockPool
       .connect(account3)
       .release(account3.address)
   await mockPool
       .connect(account4)
       .release(account4.address)
   const account1TokenBalance = await testPaymentToken.balanceOf(account1.address)
   const account2TokenBalance = await testPaymentToken.balanceOf(account2.address)
   const account3TokenBalance = await testPaymentToken.balanceOf(account3.address)
   const account4TokenBalance = await testPaymentToken.balanceOf(account4.address)
   expect(account1TokenBalance).to.equal(25000)
   expect(account2TokenBalance).to.equal(25000)
   expect(account3TokenBalance).to.equal(25000)
   expect(account4TokenBalance).to.equal(25000)

})

tiêu đề cấp đầu tiên

Mã thông báo thanh toán được phân phối không đồng đều giữa nhiều người được thanh toán

Trong thử nghiệm thứ hai, chúng tôi muốn đảm bảo rằng toán học hoạt động ngay cả khi phần của mỗi người nhận được phân bổ không đều.

it('payment token is distributed unevenly to multiple payees', async () => {
   payeeAddressArray = [account1.address, account2.address, account3.address, account4.address]
   payeeShareArray = [10, 5, 11, 7]
   const MockPool = await ethers.getContractFactory('MockPool')
   mockPool = await MockPool.deploy(
       payeeAddressArray,
       payeeShareArray,
       testPaymentToken.address
   )
   await mockPool.deployed()
   await testPaymentToken.mint(mockPool.address, 100000)
   await mockPool
       .connect(account1)
       .release(account1.address)
   await mockPool
       .connect(account2)
       .release(account2.address)
   await mockPool
       .connect(account3)
       .release(account3.address)
   await mockPool
       .connect(account4)
       .release(account4.address)
   const mockPoolTestPaymentTokenBalance = await testPaymentToken.balanceOf(
       mockPool.address
   )
   const account1TokenBalance = await testPaymentToken.balanceOf(account1.address)
   const account2TokenBalance = await testPaymentToken.balanceOf(account2.address)
   const account3TokenBalance = await testPaymentToken.balanceOf(account3.address)
   const account4TokenBalance = await testPaymentToken.balanceOf(account4.address)
   expect(mockPoolTestPaymentTokenBalance).to.equal(1)
   expect(account1TokenBalance).to.equal(30303)
   expect(account2TokenBalance).to.equal(15151)
   expect(account3TokenBalance).to.equal(33333)
   expect(account4TokenBalance).to.equal(21212)

})

tiêu đề cấp đầu tiên

Mã thông báo thanh toán được phân phối không đồng đều cho nhiều người được trả tiền và mã thông báo thanh toán bổ sung được gửi đến nhóm

Điều này tương tự như thử nghiệm trước đó, nhưng với việc bổ sung thêm nhiều mã thông báo thanh toán được gửi đến nhóm trước khi tiền được chuyển cho người nhận. Điều này cho thấy rằng khi mã thông báo thanh toán tiếp tục chảy vào hợp đồng nhóm mô phỏng, toán học vẫn có thể đảm bảo rằng người được thanh toán nhận được số tiền chính xác.

it('payment token is distributed unevenly to multiple payees with additional payment token sent to pool', async () => {
   payeeAddressArray = [account1.address, account2.address, account3.address, account4.address]
   payeeShareArray = [10, 5, 11, 7]
   const MockPool = await ethers.getContractFactory('MockPool')
   mockPool = await MockPool.deploy(
       payeeAddressArray,
       payeeShareArray,
       testPaymentToken.address
   )
   await mockPool.deployed()
   await testPaymentToken.mint(mockPool.address, 100000)
   await mockPool
       .connect(account1)
       .release(account1.address)
   await mockPool
       .connect(account2)
       .release(account2.address)
   await testPaymentToken.mint(mockPool.address, 100000)
   await mockPool
       .connect(account3)
       .release(account3.address)
   await mockPool
       .connect(account4)
       .release(account4.address)
   await mockPool
       .connect(account1)
       .release(account1.address)
   await mockPool
       .connect(account2)
       .release(account2.address)
   const mockPoolTestPaymentTokenBalance = await testPaymentToken.balanceOf(
       mockPool.address
           )
   const account1TokenBalance = await testPaymentToken.balanceOf(account1.address)
   const account2TokenBalance = await testPaymentToken.balanceOf(account2.address)
   const account3TokenBalance = await testPaymentToken.balanceOf(account3.address)
   const account4TokenBalance = await testPaymentToken.balanceOf(account4.address)
   expect(mockPoolTestPaymentTokenBalance).to.equal(1)
   expect(account1TokenBalance).to.equal(60606)
   expect(account2TokenBalance).to.equal(30303)
   expect(account3TokenBalance).to.equal(66666)
   expect(account4TokenBalance).to.equal(42424)

})

Bây giờ tất cả các bài kiểm tra đã sẵn sàng, đã đến lúc chạy chúng và xem chúng có hoạt động không!Trong thư mục gốc của dự án, hãy bắt đầu các bài kiểm tra với npx hardhat test . Nếu mọi thứ đều chính xác, thì bạn sẽ thấy tất cả các ô vuông màu xanh lá cây như trong hình bên dưới.

tóm tắt

tóm tắt

Bài viết này đến từ cộng đồng tài chính phi tập trung và được sao chép với sự cho phép.

Source:https://medium.com/coinmonks/create-an-erc20-token-payment-splitting-smart-contract-c79436470ccc

Bài viết này đến từ cộng đồng tài chính phi tập trung và được sao chép với sự cho phép.

ETH
Chào mừng tham gia cộng đồng chính thức của Odaily
Nhóm đăng ký:
https://t.me/Odaily_News
Nhóm trò chuyện:
https://t.me/Odaily_GoldenApe
Tài khoản chính thức:
https://twitter.com/OdailyChina
Mục lục bài viết
Tải ứng dụng Odaily Nhật Báo Hành Tinh
Hãy để một số người hiểu Web3.0 trước
IOS
Android