Above the basic token concept of Web3 tokens, we briefly described the concept of tokens,
and also talked about the concept of the ERC-20 protocol.
The official document address of ERC-20 is as follows https://github.com/ethereum/EIPs/blob/master/EIPS /eip-20.md
does not feel very formal to be honest, even the address is placed on github, but there is no way for the official to do this, so we can only look at it like this
Enter the document first
, flip through the document and you will find that there are actually not many
The following will tell us that we need a name method.
Use the view tag to indicate that he wants to read the state variable returns and return a value of string type.
The name is the name of our own token.
Well, don’t just talk about it, start the ganache environment first
, then open a Truffle project environment
and create a file called grToken.sol in the contracts directory
The reference code is as follows
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
contract grToken{
//因为绑定了public 会自动生成get方法
string public name = "gerToken";
}
In this way, a token called gerToken is generated
, and it can even be tested like this
We create a file in the migrations directory.
Remember that the file under migrations must start with a number to read this script.
We create a file called 2_contract.js.
The reference code is as follows
const Contacts = artifacts.require("grToken.sol")
module.exports = function(deployer) {
deployer.deploy(Contacts)
}
We imported the grToken under contracts
and then we run the project on the terminal
and then we execute
truffle migrate
Deploy the contract
Some people here will worry about whether our previous files will be deployed after truffle migrate. In fact, there is no need to worry
because truffle migrate is still a bit smart, but there are not many
files that you have already deployed. It will not deploy again But
if you change the content and go to truffle migrate, it will not be redeployed,
so if you want to redeploy the changed content, you need
truffle migrate --reser
In this way, the files you have changed will be redeployed to our blockchain
Then we terminal run
truffle console
run to the truffle console
Then we execute
const token = await grToken.deployed()
In this way, the constant token gets our grToken contract object
and then we try to call its name
token.name
After the output, you will find that this is a function
This is the first name function we have implemented in this document.
Then there is a symbol after the name in the document.
You can understand that we used to be ETH. You can also identify your own token.
Just add a line
and name all the same
string public symbol = "GRY";
The one I have here is called the GRY proxy symbol
Then there is a decimals below,
as we said before, we have a lot of units for the convenience of processing, the most basic of which is wei,
which is the conversion unit of your tokens.
We can write it like this here
uint public decimals = 18;
There is also totalSupply below. This is over.
The total amount shared
can be written like this
uint public totalSupply;
constructor(){
totalSupply = 1000000 *(19 * decimals);
}
Anyway, we have the final say on how much we have here, just start with one million, the total amount, and then because it is the wei unit, we will deal with it and calculate it to really turn it into the same unit as ETH. The total amount is multiplied by one million ten
times ten to the eighteenth power
Then we re-run the terminal and enter
truffle migrate --reser
Because grToken.sol has been deployed before, it has been recorded on the blockchain, so in order to upload the modified content, we need to use –reser
and then we execute
truffle console
Open the truffle console
and then still
const token = await grToken.deployed()
Create a token object again to connect to this grToken object
Then we can pass
token.name()
You can access the token name and
then
token.symbol()
You can see the symbols we defined
Then
token.decimals()
It converted to this uint content for us
and then
token.totalSupply()
But the content will change a bit
but don't worry it's ok
This is actually almost the same, but in fact there is a balanceOf
thing behind it, which is really troublesome.
That is, what is the corresponding balance under each of our accounts?
The method passes in the address of the account,
and the following is the returned balance value.
In fact, we can simulate a clearing
Object public userList = {
"admin": 5000,
"gangdan": 1000,
"zhanglei": 2000
}
We set up an object here so that when he token.userList( " user address")
will return the
balance
We can write like this and first declare a variable of mapping type
mapping(address => uint256) public balanceOf;
This is more like java's hash map collection
key-value correspondence Here we declare that the value corresponding to the name key is of type uint256
and the name is balanceOf
Then assign a value when the constructor object is initialized
balanceOf[msg.sender] = totalSupply;
Here, the msg.sender
has appeared in our web3 operation smart contract before. At that time, we can get the first one in the user list. This is almost the same as we said that deploying a smart contract needs to consume fuel value. If
we don’t set it, the first one will be deducted by default.
This msg.sender also gets this user.
Then we said before that totalSupply is the total amount. Direct writing in this way is equivalent to the publisher owning all the tokens.
Then there is another transfer method below
, because you can’t keep it with the publisher all the time, right?
we write like this
function transfer(address _to, uint256 _value) public returns (bool success) {
balanceOf[msg.sender] = balanceOf[msg.sender].sub(_value);
balanceOf[_to] = balanceOf[_to].add(_value);
}
But in fact, when I just wrote this place, an error will be reported
because there are no such functions in our module for the time being.
We need to import a library
type
npm install @openzeppelin/contracts
This library is very powerful, you can understand it yourself
Then we import at the top of the file
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
using SafeMath for uint256;
In this way, we will not report an error when we use those two functions. The business
of transfer here is not complicated . _to token We need to operate the user address to him plus the corresponding number of tokens of _value
There may be a misunderstanding here.
When we use msg.sender in the constructor, we get the user who issued the contract,
but the msg.sender we get in the transfer is the user who called this function
, but we must use our first user here. Because we have no operation to switch the login user
But what we need to pay attention to here is that if none of our current users has a value equal to _value, how can we deal with this business, right?
So we want to add an if outside
, which is to simply add a greater than or equal judgment on the outer layer
, but compared with if in solidity, there is a more suitable syntax called require
The business function of require is that if the condition you give him is true (true), it will continue to go down, if not, it will directly throw an exception and this exception will be recorded by our blockchain
Then let's be more rigorous
require(_to != address(0));
Because this address is passed from the front end, we still need to make a simple judgment. Although it is not certain whether it is a valid address, it is still useful. You can understand it as !=
null
Then here we will also notice that this transfer has returns of
bool success. It is required to return a Boolean value.
If it can go through all the business logic, we return a true to indicate success.
But the document says here that we must trigger a Transfer event.
We pull down the document to find an Events.
We first copy the event interface on this document into our own code.
event Transfer(address indexed _from, address indexed _to, uint256 _value)
You can understand this as calling the log to record the operations we sent,
and all operation records will be visible in the blockchain log
In our previous centralization, some people may be able to record their own operations to him, and even say that some Internet companies have ghosts in their platform operations, but
our blockchain is really recorded, so it is difficult to remove this record, and it can be recalled at any time.
Then we call it in the transfer
or it can be said that as long as the blockchain is still in this record
emit Transfer(msg.sender,_to,_value);
In this way, our business will be fine. Here we will do a small test.
We will not operate the terminal. It is too troublesome.
We found the scripts in the root directory. If there is no scripts, create a test.js below.
Our terminal will execute it first.
truffle migrate --reser
Redeploy the smart contract
and then our test.js representative writes as follows
const GrToken = artifacts.require("grToken.sol")
module.exports = async function(callback) {
const grTokenDai = await GrToken.deployed();
let res1 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
console.log(res1);
callback()
}
Here everyone needs to pay attention to 0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974 is the public key address of the first account in my ganache environment.
You also have to fill it in according to the situation. Just check the getoken number under this account.
We run the terminal
truffle exec .\scripts\test.js
Here is the total amount of token.totalSupply() that we output earlier
It means that the logic in our constructor is completed,
but this unit obviously looks a bit of a headache
We can change it to this
const GrToken = artifacts.require("grToken.sol")
const towei = (bn)=> {
return web3.utils.fromwei(bn,"ether");
}
module.exports = async function(callback) {
const grTokenDai = await GrToken.deployed();
let res1 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
console.log(towei(res1));
callback()
}
Many people will say that web3 has not been introduced?
You can rest assured that the method of web3 in the test environment can be used by the way
we run again
truffle exec .\scripts\test.js
This time the unit is fine
, so now we can be sure that balanceOf is really easy to use
Next test the transfer
But there is a terrible point here.
We have not logged in to authorize transfer. It cannot get the currently logged in user.
We can write like this
Here we change the test.js code as follows
const GrToken = artifacts.require("grToken.sol")
const toWei = (bn) => {
return web3.utils.fromWei(bn, "ether");
}
const inWei = (bn) => {
return web3.utils.toWei(bn.toString(), "ether");
}
module.exports = async function(callback) {
const grTokenDai = await GrToken.deployed();
let res1 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
console.log(toWei(res1));
await grTokenDai.transfer("0x096A90463E48723d3631b3291Dd76b6BA425eD4e",inWei(10000),{
from: "0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974"
});
let res2 = await grTokenDai.balanceOf("0x096A90463E48723d3631b3291Dd76b6BA425eD4e");
console.log(toWei(res2),"第二个用户");
let res3 = await grTokenDai.balanceOf("0x323E82B10DCd0E5fB100BcC3F0ABAB561AA04974");
console.log(toWei(res3),"第一个用户");
callback()
}
inWei is a reverse conversion transfer. In the test environment, we use the first parameter to whom to send to, the second to send how much, and the third is an object. Note that 0x096A90463E48723d3631b3291Dd76b6BA425eD4e is also a user address simulated by my ganache. After all, this must require
two Only the user can operate
{ from: current login user address } and then we run the terminal again
truffle exec .\scripts\test.js
take off, take off