经济下行阶段,一个 37 岁失业程序员的独白(经历/经验分享)
警告:区块链投资高风险,需要谨慎,谨慎,再谨慎!
初识 Solidity 和 OpenZeppelin
Solidity 是一种面向对象的高级静态语言,用于实现智能合约,运行于 以太坊虚拟机,它支持继承,库和自定义类型等。pragma solidity ^0.8.0; contract HelloWorld { } Solidity 有三种类型的变量,熟悉它是因为变量的范围是由它们声明的位置所决定的:Local在函数内部声明且不存储在区块链上State存储在区块链上Global提供区块链相关的信息,它在运行时由以太坊虚拟机注入包括交易发送者,区块时间戳,区块哈希等全局变量语法知识,请阅读:https://docs.soliditylang.org/en/v0.8.9/index.html初识 OpenZeppelin说明:OpenZeppelin 是一家以太坊安全公司,其为流行的智能合约标准开发了一组合约,这些合约经过了大量的测试和安全审查,所以如果我们需要实现这些标准合约时,应该尝试找到 OpenZeppelin 提供的合约,而不是重头开始重写整个标准。https://github.com/OpenZeppelin/openzeppelin-contracts在 useweb3 ...
实战案例三:ICO 初始代币销售与空投
最近 APE 空投这么火,是时候说到如何为我们之前做的 UseWeb3NFT 发行代币,在进入编码之前,我们先梳理一下具体的需求:假设令牌的最大数量为 10000 个每一个 UseWeb3NFT 持有者可以免费领取 10个ICO 时每一个令牌的初始价格为 0.0001 ethERC-20https://docs.openzeppelin.com/contracts/2.x/api/token/erc20由于空投部分需要依赖 UseWeb3NFT,所以我们需要写一个接口,使用外部调用的方式获取一些数据interface IUseWeb3NFT { function balanceOf(address owner) external view returns (uint256 balance); function ownerOf(uint256 tokenId) external view returns (address ownerAddress); } 首先,我们需要去定义价格uint256 public constant tokenPrice = 0.0001 ether; u...
Dev
经济下行阶段,一个 37 岁失业程序员的独白(经历/经验分享)
警告:区块链投资高风险,需要谨慎,谨慎,再谨慎!
初识 Solidity 和 OpenZeppelin
Solidity 是一种面向对象的高级静态语言,用于实现智能合约,运行于 以太坊虚拟机,它支持继承,库和自定义类型等。pragma solidity ^0.8.0; contract HelloWorld { } Solidity 有三种类型的变量,熟悉它是因为变量的范围是由它们声明的位置所决定的:Local在函数内部声明且不存储在区块链上State存储在区块链上Global提供区块链相关的信息,它在运行时由以太坊虚拟机注入包括交易发送者,区块时间戳,区块哈希等全局变量语法知识,请阅读:https://docs.soliditylang.org/en/v0.8.9/index.html初识 OpenZeppelin说明:OpenZeppelin 是一家以太坊安全公司,其为流行的智能合约标准开发了一组合约,这些合约经过了大量的测试和安全审查,所以如果我们需要实现这些标准合约时,应该尝试找到 OpenZeppelin 提供的合约,而不是重头开始重写整个标准。https://github.com/OpenZeppelin/openzeppelin-contracts在 useweb3 ...
实战案例三:ICO 初始代币销售与空投
最近 APE 空投这么火,是时候说到如何为我们之前做的 UseWeb3NFT 发行代币,在进入编码之前,我们先梳理一下具体的需求:假设令牌的最大数量为 10000 个每一个 UseWeb3NFT 持有者可以免费领取 10个ICO 时每一个令牌的初始价格为 0.0001 ethERC-20https://docs.openzeppelin.com/contracts/2.x/api/token/erc20由于空投部分需要依赖 UseWeb3NFT,所以我们需要写一个接口,使用外部调用的方式获取一些数据interface IUseWeb3NFT { function balanceOf(address owner) external view returns (uint256 balance); function ownerOf(uint256 tokenId) external view returns (address ownerAddress); } 首先,我们需要去定义价格uint256 public constant tokenPrice = 0.0001 ether; u...
Dev

Subscribe to icepy

Subscribe to icepy
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
现实情况是期望代币可以在去中心化的交易场所中交换,这篇文章就是从一个简单案例来说明交换,流动性该如何实现。
我们需要先梳理一下,期望这个应用具备哪些功能:
只用一个代币对建立交易场所
交易收取 1% 的费用
用户可以为 UseWeb3Token 添加或删除流动性
为用户提供 LP 代币
说明:实现会比这个例子复杂的多
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract UseWeb3Exchange is ERC20 {
address public useweb3TokenAddress;
constructor(address useweb3TokenContract) ERC20("LP Token", "LP") {
useweb3TokenAddress = useweb3TokenContract;
}
function getReserve() public view returns(uint256){
return ERC20(useweb3TokenAddress).balanceOf(address(this));
}
}
因为要提供 LP 所以直接继承 ERC20
同时定义 getReserve 获取当前合约持有的 UseWeb3Token 数量
function getReserve() public view returns(uint256){
return ERC20(useweb3TokenAddress).balanceOf(address(this));
}
接下来,我们开始编写添加流动性方法
function addLiquidity(uint256 _amount) public payable returns(uint256){
uint256 liquidity;
// 用当前合约的 eth 做初始值
uint256 ethBalance = address(this).balance;
// 查询当前合约持有 useweb3Token 的数量
uint256 currentContractHoldUseweb3TokenReserve = getReserve();
ERC20 useweb3Token = ERC20(useweb3TokenAddress);
// currentContractHoldUseweb3TokenReserve = 0 表示第一次添加流动性
if (currentContractHoldUseweb3TokenReserve == 0){
// 将 _amount 数量 从发起者地址转移到当前合约地址
useweb3Token.transferFrom(msg.sender, address(this), _amount);
liquidity = ethBalance;
// 给发起者地址 LP 代币
_mint(msg.sender, liquidity);
} else {
// 已经存在流动性时的处理逻辑
// 当前合约的 eth 数量 - 用户发送来的 eth 数量
uint256 ethReserve = ethBalance - msg.value;
// (用用户发送来的 eth 数量乘以当前合约持有的 useweb3Token的数量)除以(已经减过的eth储备) ,获取一定比率的 useweb3Token
uint256 useweb3TokenAmount = (msg.value * currentContractHoldUseweb3TokenReserve)/(ethReserve);
// 检查 _amount 参数必须大于 计算出来的 useweb3Token 的数量
require(_amount > useweb3TokenAmount, "Amount of tokens sent is less than the minimum tokens required");
useweb3Token.transferFrom(msg.sender, address(this), useweb3TokenAmount);
// (LP 代币的总量 * 用户发送来的 eth 数量)除以(已经减过的eth储备)
liquidity = (totalSupply() * msg.value)/ethReserve;
// 给发送者地址 LP 代币
_mint(msg.sender, liquidity);
}
return liquidity;
}
删除流动性方法
function removeLiquidity(uint256 _amount) public returns(uint256, uint256){
require(_amount > 0, "amount > 0");
// 查询当前合约持有的 eth 数量
uint256 ethReserve = address(this).balance;
uint256 lpTotal = totalSupply();
uint256 ethAmount = (ethReserve * _amount)/lpTotal;
uint256 useweb3TokenAmount = (getReserve() * _amount)/lpTotal;
// 烧毁用户的 lp 代币
_burn(msg.sender, _amount);
// 将 eth 和 useweb3Token 代币还给用户
payable(msg.sender).transfer(ethAmount);
ERC20(useweb3TokenAddress).transfer(msg.sender, useweb3TokenAmount);
return (ethAmount, useweb3TokenAmount);
}
交易收取费用的方法
function getAmountOfTokens(uint256 inputAmount, uint256 inputReserve, uint256 outputReserve) public pure returns(uint256){
require(inputReserve > 0 && outputReserve > 0, "invalid reserves");
uint256 inputAmountWithFee = inputAmount * 99;
uint256 numerator = inputAmountWithFee * outputReserve;
uint256 denominator = (inputReserve * 100) + inputAmountWithFee;
return numerator / denominator;
}
交换的方法
function ethToUseWeb3Token(uint256 minTokens) public payable{
uint256 currentContractHoldUseweb3TokenReserve = getReserve();
uint256 tokensBought = getAmountOfTokens(msg.value, address(this).balance - msg.value, currentContractHoldUseweb3TokenReserve);
require(tokensBought >= minTokens, "insufficient output amount");
ERC20(useweb3TokenAddress).transfer(msg.sender, tokensBought);
}
function useWeb3TokenToEth(uint256 tokenSold, uint256 minEth) public{
uint256 currentContractHoldUseweb3TokenReserve = getReserve();
uint256 ethBought = getAmountOfTokens(tokenSold, currentContractHoldUseweb3TokenReserve, address(this).balance);
require(ethBought >= minEth, "insufficient output amount");
ERC20(useweb3TokenAddress).transferFrom(msg.sender, address(this), tokenSold);
payable(msg.sender).transfer(ethBought);
}
我们编写一个简单的前端代码来看一看合约运行的结果,需要注意的是,我们需要为交易合约授权。

const web3Provider = getWeb3Provider();
const signer = web3Provider.getSigner();
const exchangeContract = new ethers.Contract(USEWEB3_EXCHANGE_ADDRESS, USEWEB3_EXCHANGE_ABI, signer);
const tokenContract = new ethers.Contract(USEWEB3_TOKEN_ADDRESS, USEWEB3_TOKEN_ABI, signer);
const tokenTx = await tokenContract.approve(USEWEB3_EXCHANGE_ADDRESS, ethers.utils.parseEther("10"));
await tokenTx.wait();
然后初始添加流动性
const exchangeTx = await exchangeContract.addLiquidity(ethers.utils.parseEther("10"), {value: ethers.utils.parseEther("0.01")});
await exchangeTx.wait();

通过 https://rinkeby.etherscan.io/ 来观察最后的结果


现实情况是期望代币可以在去中心化的交易场所中交换,这篇文章就是从一个简单案例来说明交换,流动性该如何实现。
我们需要先梳理一下,期望这个应用具备哪些功能:
只用一个代币对建立交易场所
交易收取 1% 的费用
用户可以为 UseWeb3Token 添加或删除流动性
为用户提供 LP 代币
说明:实现会比这个例子复杂的多
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract UseWeb3Exchange is ERC20 {
address public useweb3TokenAddress;
constructor(address useweb3TokenContract) ERC20("LP Token", "LP") {
useweb3TokenAddress = useweb3TokenContract;
}
function getReserve() public view returns(uint256){
return ERC20(useweb3TokenAddress).balanceOf(address(this));
}
}
因为要提供 LP 所以直接继承 ERC20
同时定义 getReserve 获取当前合约持有的 UseWeb3Token 数量
function getReserve() public view returns(uint256){
return ERC20(useweb3TokenAddress).balanceOf(address(this));
}
接下来,我们开始编写添加流动性方法
function addLiquidity(uint256 _amount) public payable returns(uint256){
uint256 liquidity;
// 用当前合约的 eth 做初始值
uint256 ethBalance = address(this).balance;
// 查询当前合约持有 useweb3Token 的数量
uint256 currentContractHoldUseweb3TokenReserve = getReserve();
ERC20 useweb3Token = ERC20(useweb3TokenAddress);
// currentContractHoldUseweb3TokenReserve = 0 表示第一次添加流动性
if (currentContractHoldUseweb3TokenReserve == 0){
// 将 _amount 数量 从发起者地址转移到当前合约地址
useweb3Token.transferFrom(msg.sender, address(this), _amount);
liquidity = ethBalance;
// 给发起者地址 LP 代币
_mint(msg.sender, liquidity);
} else {
// 已经存在流动性时的处理逻辑
// 当前合约的 eth 数量 - 用户发送来的 eth 数量
uint256 ethReserve = ethBalance - msg.value;
// (用用户发送来的 eth 数量乘以当前合约持有的 useweb3Token的数量)除以(已经减过的eth储备) ,获取一定比率的 useweb3Token
uint256 useweb3TokenAmount = (msg.value * currentContractHoldUseweb3TokenReserve)/(ethReserve);
// 检查 _amount 参数必须大于 计算出来的 useweb3Token 的数量
require(_amount > useweb3TokenAmount, "Amount of tokens sent is less than the minimum tokens required");
useweb3Token.transferFrom(msg.sender, address(this), useweb3TokenAmount);
// (LP 代币的总量 * 用户发送来的 eth 数量)除以(已经减过的eth储备)
liquidity = (totalSupply() * msg.value)/ethReserve;
// 给发送者地址 LP 代币
_mint(msg.sender, liquidity);
}
return liquidity;
}
删除流动性方法
function removeLiquidity(uint256 _amount) public returns(uint256, uint256){
require(_amount > 0, "amount > 0");
// 查询当前合约持有的 eth 数量
uint256 ethReserve = address(this).balance;
uint256 lpTotal = totalSupply();
uint256 ethAmount = (ethReserve * _amount)/lpTotal;
uint256 useweb3TokenAmount = (getReserve() * _amount)/lpTotal;
// 烧毁用户的 lp 代币
_burn(msg.sender, _amount);
// 将 eth 和 useweb3Token 代币还给用户
payable(msg.sender).transfer(ethAmount);
ERC20(useweb3TokenAddress).transfer(msg.sender, useweb3TokenAmount);
return (ethAmount, useweb3TokenAmount);
}
交易收取费用的方法
function getAmountOfTokens(uint256 inputAmount, uint256 inputReserve, uint256 outputReserve) public pure returns(uint256){
require(inputReserve > 0 && outputReserve > 0, "invalid reserves");
uint256 inputAmountWithFee = inputAmount * 99;
uint256 numerator = inputAmountWithFee * outputReserve;
uint256 denominator = (inputReserve * 100) + inputAmountWithFee;
return numerator / denominator;
}
交换的方法
function ethToUseWeb3Token(uint256 minTokens) public payable{
uint256 currentContractHoldUseweb3TokenReserve = getReserve();
uint256 tokensBought = getAmountOfTokens(msg.value, address(this).balance - msg.value, currentContractHoldUseweb3TokenReserve);
require(tokensBought >= minTokens, "insufficient output amount");
ERC20(useweb3TokenAddress).transfer(msg.sender, tokensBought);
}
function useWeb3TokenToEth(uint256 tokenSold, uint256 minEth) public{
uint256 currentContractHoldUseweb3TokenReserve = getReserve();
uint256 ethBought = getAmountOfTokens(tokenSold, currentContractHoldUseweb3TokenReserve, address(this).balance);
require(ethBought >= minEth, "insufficient output amount");
ERC20(useweb3TokenAddress).transferFrom(msg.sender, address(this), tokenSold);
payable(msg.sender).transfer(ethBought);
}
我们编写一个简单的前端代码来看一看合约运行的结果,需要注意的是,我们需要为交易合约授权。

const web3Provider = getWeb3Provider();
const signer = web3Provider.getSigner();
const exchangeContract = new ethers.Contract(USEWEB3_EXCHANGE_ADDRESS, USEWEB3_EXCHANGE_ABI, signer);
const tokenContract = new ethers.Contract(USEWEB3_TOKEN_ADDRESS, USEWEB3_TOKEN_ABI, signer);
const tokenTx = await tokenContract.approve(USEWEB3_EXCHANGE_ADDRESS, ethers.utils.parseEther("10"));
await tokenTx.wait();
然后初始添加流动性
const exchangeTx = await exchangeContract.addLiquidity(ethers.utils.parseEther("10"), {value: ethers.utils.parseEther("0.01")});
await exchangeTx.wait();

通过 https://rinkeby.etherscan.io/ 来观察最后的结果


No activity yet