Web3 solidity writes exchange contracts writes ETH and custom token deposit logic and takes everyone to manually test

The above Web3 describes the concept of exchange authorization replacement and writes the transferFrom and approve functions. We have written a simple authorization exchange logic but have not tested it.
In fact, I don’t want to. The main reason is that the exchange has not been instantiated and now we can’t test it.
Let’s run ganache first. A virtual blockchain environment
insert image description here
is first released and executed on the terminal

truffle migrate

If you follow me step by step, the compilation should pass.
insert image description here
Then we need to create the contract of the exchange.
Create a file called Exchange.sol in the contracts directory under the root directory of the project
and write the most basic one first. structure

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract Exchange {
    
    
    using SafeMath for uint256;
}

Then here we need to designate a charging account because our exchange can be directly understood as an intermediary,
but the difference from the Internet intermediary is that our contract is purely open and transparent, but everyone exchanges tokens here. For example, what tokens may be expected to rise It is understandable to quickly transfer to some exchanges to obtain some benefits from the middle, and this is all open and transparent

Then there is the issue of rates. For example, some of our transaction rates are 6% for each transaction. For example, if you give an anchor a gift in Huya, in fact, the anchor can only get a small part. Most of them are from the platform. You can understand it as being accepted by the platform The part taken away is called the fee rate. Of course, the live broadcast platform estimates that it will be more than 50%.

We can directly write

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract Exchange {
    
    

    using SafeMath for uint256;

    //收费账号地址
    address public feeAccout;
    //费率
    uint256 public feePercent;
    //实例化合约交易所
    constructor(address _feeAccout,uint256 _feePercent) {
    
    
        //用接到的参数给账号地址和费率赋值
        feeAccout = _feeAccout;
        feePercent = _feePercent;
    }
}

Here we first define two variables address address type feeAccout and set public to indicate that this variable is publicly used to store the address of the charging account, then the uint256
digital type feePercent records the rate usage
and then accepts the account from the outside in the constructor instantiated by the contract The value of address and rate and then assign values ​​to the above two variables

But there is another problem. It is impossible for our exchange to store only one currency. Otherwise, why would we exchange it with others? Or what do you do with only one dialogue?
Then we can store such an object in the exchange,
let's first use the js structure to explain

{
    
    
    "A代币地址":{
    
    
        "A用户地址": 300,
        "B用户地址": 400
    },
    "B代币地址": {
    
    
        "A用户地址": 500
    }
}

It is probably such a structure. The first-level objects are all the token addresses that are currently stored. Their corresponding values ​​are the corresponding number of each user value under this token.

In solidity we can write like this

mapping(address=> mapping(address=>uint256)) public tokens;

We define an object and the key is an address address type value is an object key in an object value is also an address address type value is a uint256 number type and then set public to indicate that it is public and the name is tokens The name can be defined according to the
mood

But data alone cannot complete the logic. We also need a deposit method.
We can write it like this.
Here we define two methods. The first is used to recharge ETH, and the second is the recharge function of gerToken.
Then we use the public declaration function to disclose it.
Then payable declares that this is a recharge function

    //转入  ETH
    function depositEther() payable public {
    
    

    }
    //转入  gerToken
    function depositToken() payable public {
    
    

    }

insert image description here
Then we define a constant above

// ETH 代币地址
address constant ETHER = address(0);

insert image description here
Normally, the value of this should be an address, which is the address corresponding to our ETH token, but we just wrote an address (0) here just for simulation, which
also conforms to an address specification of address
, and then we have to write an event to act as a log Record the recharge operation.
Here I wrote Deposit. In fact, you can name it by the way. You can call it A or B. The
reference code is as follows

event Deposit(address token,address user,uint256 amount,uint256 balance);//存入ETH

Then we can start writing the function logic of depositEther into ETH

//转入  ETH
function depositEther() payable public {
    
    
    tokens[ETHER][msg.sender] = tokens[ETHER][msg.sender].add(msg.value);
    emit Deposit(ETHER,msg.sender, msg.value, tokens[ETHER][msg.sender]);
}

ETHER is the token we just created. The identification value is address(0). This is just an address we scribbled. Because we are only testing at present, we assume that
our exchange 0 represents ETH, and msg.sender represents the address of the current user, and then msg .value is the amount that needs to be operated.
We said before that tokens are objects that store the token address and the amount held by the user

Here we operate add to add the value of msg.value to the corresponding ETH in the token object of the user,
and then we call the Deposit we just wrote to record this transaction.
The first parameter of Deposit is ETHER token address msg.sender current user msg.value Operand value tokens[ETHER][msg.sender] Find the value corresponding to the current user under the object corresponding to the token address in tokens. Simply put, the last thing you want is the total number of tokens for the user

Then the recharge operation of ETH has been completed
, so grToken also needs a recharge logic
, but we should not operate directly in the exchange but through the contract of our token.
We introduce the contract file of grToken
insert image description here
and write the depositToken code as follows

//转入  gerToken
function depositToken(address _token, uint256 _amount) payable public {
    
    
    require(grToken(_token).transferFrom(msg.sender,address(this),_amount));
    tokens[_token][msg.sender] = tokens[_token][msg.sender].add(_amount);
    emit Deposit(_token,msg.sender, _amount, tokens[_token][msg.sender]);
}

Here we have two parameters _token token address_amount that needs to be recharged,
and then our next sentence may have a problem, so we put require to call it. We said before that if the code is wrong, it will stop the program immediately and record the error in the area. Then we instantiate the address of the grToken contract object on the block chain
and pass in our _token to call the transferFrom function in it. The deduction user is msg.sender. The current user who operates this function receives the address address(this) The amount of our exchange itself is the method Received the _amount parameter
and after the operation is completed, we operate the token value of the user in the object through tokens again,
then call the Deposit we just defined to record the transaction
, and then we compile it and try
the terminal input

truffle compile

insert image description here
There is no problem

But before we deploy, we still need to write a script. Don’t worry, the contract is finally written but no script is written to use it.
We create a 2_contract.js in the migrations directory and
write the code as follows

const grToken = artifacts.require("grToken.sol")
const Exchange = artifacts.require("Exchange.sol")
module.exports = async  function(deployer) {
    
    
    const accounts = await web3.eth.getAccounts();
    await deployer.deploy(grToken);
    await deployer.deploy(Exchange,accounts[0],3);
}

Here we import the grToken token contract and the Exchange exchange contract
, then call the getAccounts of web3 to get the user list,
and then publish two contracts.
Exchange itself needs two parameters. One is the receiving user, which user is the tip obtained by the exchange, and the following The rate
here is a little less than 3%,
and then we directly find the user whose subscript is 0 in the user list, because the first user who consumes fuel at the time of release, the tip should naturally go to him

Our terminal executes

truffle migrate --reser

Update and release the smart contract.
insert image description here
The release is successful here, but we still have to test it.

First, we use MetaMask to import the first user in our ganache.
insert image description here
We create test.js in the scripts directory under the project root directory.
The reference code is as follows

const GrToken = artifacts.require("grToken.sol")
const Exchange = artifacts.require("Exchange.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();
    const exchage = await Exchange.deployed();
    const accounts= await web3.eth.getAccounts()
    await exchage.depositEther({
    
    
        from: accounts[0],
        value: inWei(10)
    });
    callback()
}

We first imported the token grToken and the contract of the exchange Exchange
, and then called the depositEther of the Exchange to recharge ETH. The specific users who want to deposit still get the user list through getAccounts, and then operate the first one to store,
and then our terminal executes

truffle exec .\scripts\test.js

insert image description here
Then check MetaMask, you will find that the user’s payment has indeed been deducted, and
insert image description here
it will be reduced by 10 after running it once.
insert image description here
Of course, this is just a deposit, but the most intuitive thing for users is to see that ETH is less
, and it should be even less because there is still fuel to consume.
It's pretty shitty to be honest

Then we can check the deposit
scripts directory to create test.js
and write the code as follows

const GrToken = artifacts.require("grToken.sol")
const Exchange = artifacts.require("Exchange.sol")
const ETHER_ADDRESS = '0x0000000000000000000000000000000000000000';

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();
    const exchage = await Exchange.deployed();
    const accounts= await web3.eth.getAccounts()
    await exchage.depositEther({
    
    
        from: accounts[0],
        value: inWei(10)
    });
    let res = await exchage.tokens(ETHER_ADDRESS,accounts[0])
    console.log(toWei(res));
    callback()
}

Here we directly write 0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.

truffle exec .\scripts\test.js

Because we have tested and operated many times, this value is no problem.
insert image description here
At worst, let’s do it again
insert image description here
and deposit 10, which becomes 130.
insert image description here
Our account is actually much less
, so ETH will be fine.

Then we will operate grToken

But for this we need to authorize and allow the exchange to operate our grToken

We directly create test.js in the scripts directory and write it like this

const GrToken = artifacts.require("grToken.sol")
const Exchange = artifacts.require("Exchange.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();
    const exchage = await Exchange.deployed();
    const accounts= await web3.eth.getAccounts()
    await grTokenDai.approve(exchage.address,inWei(100000),{
    
    
        from: accounts[0]
    })
    await exchage.depositToken(grTokenDai.address,inWei(10000),{
    
    
        from: accounts[0]
    })
    let res = await exchage.tokens(grTokenDai.address,accounts[0])
    console.log(toWei(res));
    callback()
}

Here we first call grTokenDai, the exchange authorization function we wrote above. The first parameter is the exchange address. We take the address of the exchange. The authorized number is 100,000. The authorized account is the first account. Then we call the depositToken written by our exchange and deposit it
. grToken token
and then the first parameter is the address address of grTokenDai and then the number is 10000. The deposited user is still our first user
and then we check the number of grtoken
because grTokenDai.address
we run again

truffle exec .\scripts\test.js

You can see this effect
insert image description here

Guess you like

Origin blog.csdn.net/weixin_45966674/article/details/132130783