使用 Polygon 创建全栈 DeFi 应用
核心要点
- See how LogRocket's Galileo AI surfaces the most severe issues for you No signup required DeFi is now a major topic of discussion in the cryptocurrenc

See how LogRocket's Galileo AI surfaces the most severe issues for you No signup required
DeFi is now a major topic of discussion in the cryptocurrency space. DeFi stands for “Decentralized finance,” which means that there’s no central authority keeping an eye on and controlling the transfer of funds. This also means that transactions in DeFi are P2P (peer to peer), which means that no central authority is responsible for transferral, and funds are sent directly from one entity to another.
In this article we will learn how to get started with DeFi by making a full-stack DeFi app on the Polygon chain using Next.js as the frontend. This app will sell and purchase OKToken (a fictional token) from the user. However, every purchase transaction reduces one token from the amount of tokens you can get per MATIC (selling increases this number by one). This is not an ideal demonstration, but this way you can understand how to use your own logic in Solidity smart contracts and learn to create your own full-stack DeFi app using Polygon.
Contents
Requirements
To get started with this tutorial, make sure you have the following:
Node.js installed
VS Code installed
Working knowledge of React and Next.js
Working knowledge of Solidity and tools like Hardhat
Now that you have checked the requirements, let’s proceed with creating our Hardhat project to work with our Solidity smart contracts.
Creating a Hardhat project
Navigate to a safe directory and run the following command in the terminal to initialize your Hardhat project:
npx hardhat
Once you run the command, you should see the following Hardhat initialization wizard in your terminal.
From the list, choose Create an advanced sample project. Then you will be asked where you want to initialize the Hardhat project; don’t change the field, just press Enter so that the project gets initialized in the current directory.
Then you will be asked whether or not you want to install dependencies required for your Hardhat project to run. Press y because we will be needing these dependencies, and installing them right now is the best idea.
Installation of dependencies will start, and might take a few seconds or minutes depending upon the machine you’re running. Now, run the following command in the terminal to install another dependency we will need to ease our Solidity contract development:
npm install @openzeppelin/contracts
OpenZeppelin provides smart contract standards that we can use in our own smart contracts to easily create an Ownable, ERC-20 and ERC-721 contracts, and more.
Once the dependencies are successfully installed, open the directory in a code editor. I’ll be using VS Code for this tutorial.
We will be creating two smart contracts: the first one will be our ERC-20 token itself and the second will be a vendor contract, which will facilitate buying and selling of these tokens.
Creating our smart contracts
Now, go to the contracts folder and create a new Solidity file named OKToken.sol , which will contain our ERC-20 token contract.
Use the following code for this file:
// SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract OKToken is ERC20 { constructor() ERC20("OKT", "OKToken"){ _mint(msg.sender, 10000 * 10 ** 18); } }
In the above code, we are importing the ERC20.sol file from @openzeppelin/contracts which will help us get started with an ERC-20 token easily. Then, in the constructor, we are providing the symbol "OKT" and name "OKToken" for our token.
That’s all for the token contract! Now, let’s work on the vendor contract. Under the contracts folder, create a new file named OKVendor.sol with the following code:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "./OKToken.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract OKVendor is Ownable { OKToken yourToken; uint256 public tokensPerNativeCurrency = 100; event BuyTokens(address buyer, uint256 amountOfNativeCurrency, uint256 amountOfTokens); constructor(address tokenAddress) { yourToken = OKToken(tokenAddress); } function buyTokens() public payable returns (uint256 tokenAmount) { require(msg.value > 0, "You need to send some NativeCurrency to proceed"); uint256 amountToBuy = msg.value * tokensPerNativeCurrency; uint256 vendorBalance = yourToken.balanceOf(address(this)); require(vendorBalance >= amountToBuy, "Vendor contract has not enough tokens to perform transaction"); (bool sent) = yourToken.transfer(msg.sender, amountToBuy); require(sent, "Failed to transfer token to user"); tokensPerNativeCurrency = tokensPerNativeCurrency - 1; emit BuyTokens(msg.sender, msg.value, amountToBuy); return amountToBuy; } function sellTokens(uint256 tokenAmountToSell) public { require(tokenAmountToSell > 0, "Specify an amount of token greater than zero"); uint256 userBalance = yourToken.balanceOf(msg.sender); require(userBalance >= tokenAmountToSell, "You have insufficient tokens"); uint256 amountOfNativeCurrencyToTransfer = tokenAmountToSell / tokensPerNativeCurrency; uint256 ownerNativeCurrencyBalance = address(this).balance; require(ownerNativeCurrencyBalance >= amountOfNativeCurrencyToTransfer, "Vendor has insufficient funds"); (bool sent) = yourToken.transferFrom(msg.sender, address(this), tokenAmountToSell); require(sent, "Failed to transfer tokens from user to vendor"); (sent,) = msg.sender.call{value: amountOfNativeCurrencyToTransfer}(""); tokensPerNativeCurrency = tokensPerNativeCurrency + 1; require(sent, "Failed to send NativeCurrency to the user"); } function getNumberOfTokensInNativeCurrency() public view returns(uint256) { return tokensPerNativeCurrency; } function withdraw() public onlyOwner { uint256 ownerBalance = address(this).balance; require(ownerBalance > 0, "No NativeCurrency present in Vendor"); (bool sent,) = msg.sender.call{value: address(this).balance}(""); require(sent, "Failed to withdraw"); } }
This will help us facilitate the buying and selling of tokens.
In the above contract, first we are importing our token contract, which we need in order to interact with our token contract using the vendor contract and call functions.
We are also importing Ownable.sol from @openzeppelin/contracts . This means that the owner of the smart contract can transfer its ownership and have access to owners-only functions.
After initializing the smart contract, we define the variable tokensPerNativeCurrency which states the number of tokens which can be purchased using 1 MATIC. We will be altering this number based on the transactions made.
We then have a constructor which will take OKToken’s contract address so that we can communicate with the deployed contract and perform functions on them.
In the buyTokens() function, we are performing checks to ensure the proper amount of MATIC is sent to the smart contract, and that the vendor contract has the required amount of tokens. Then we call the function transfer() from the OKToken instance we previously created to transfer the tokens to the request sender.
In the sellTokens() function, we are performing checks to ensure that the request sender has enough tokens and if the vendor contract has enough MATIC to send back to the request sender. Then, we use the transferFrom() function from the OKToken instance we previously created to transfer the tokens from the request sender’s wallet to the smart contract. However, the sender needs to approve this transaction; we perform this approval on the client side before making the request. We will cover this part when we make the front end of this application.
Finally, we have the withdraw() function, which is only accessible by the owner of the contracts. It allows them to withdraw all the MATIC present on the contract.
Now that we have the smart contracts ready, let’s deploy them to Polygon Mumbai testnet!
Deploying our smart contracts
We will be creating a script to deploy our contract to Polygon Mumbai. Once the contracts are deployed, we will programmatically send all the tokens stored on the deployer’s wallet to the vendor contract.
