Web3 solidity编写交易所合约 编写ETH和自定义代币存入逻辑 并带着大家手动测试

news2024/11/28 19:00:17

上文 Web3 叙述交易所授权置换概念 编写transferFrom与approve函数我们写完一个简单授权交易所的逻辑 但是并没有测试
其实也不是我不想 主要是 交易所也没实例化 现在也测试不了
我们先运行 ganache 启动一个虚拟的区块链环境
在这里插入图片描述
先发布 在终端执行

truffle migrate

如果你跟着我一步一步来的 那编译应该就会通过的
在这里插入图片描述
然后的话 我们要将交易所的合约也创建一下
在项目根目录下的 contracts 目录下 创建一个文件叫 Exchange.sol
然后 先编写出最基本的结构

// 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;
}

然后 这里 我们需要指定一个收费账号 因为 我们交易所大家可以直接理解为中介
但与互联网中介不同的在于 我们这个合约是纯公开透明的 但大家在这里交换代币 比如 预料到什么代币可能要涨了 赶紧转入一些 交易所从中间获取部分利益自然也是无可厚非的 而且 这个都是公开透明的

然后 还有一个费率的问题 例如 有些 我们每次交易 费率是百分之六 就比如你在虎牙送主播礼物其实主播只能拿到一小部分,大部分是平台的 你可以理解为被平台拿走的部分就叫费率 当然直播平台估计要到百分之五十以上 平台拿到的会比主播多 一般交易平台费率应该就会少一点 大概在百分之十以内

我们可以直接这样写

// 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;
    }
}

这里 我们先定义了两个变量 address 地址类型 feeAccout 设置了public表示这个变量是公开的 用于存储收费账号的地址
然后uint256数字类型的feePercent 记录费率使用
然后在合约实例化的constructor中从外面接受账号地址和费率的值 然后给上面两个变量赋值

但还有一个问题 我们的交易所不可能只存一种货币 不然我们那什么去跟别人兑换呢 是不是 或者是 你只存一种 对话来干嘛呢?
那么 我们交易所中就可以存一个这样的对象
我们先用js结构阐述

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

大概就是这样的一个结构 第一层对象是目前有存储的所有代币地址 他们对应的值 是 这个代币下 每个用户 值对应拥有的数量

在solidity中 我们可以这样写

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

我们定义了一个对象 然后 键是一个address地址类型 值是一个对象 值中的对象 键也是address地址类型 值是uint256数字类型
然后设置public表示它是公开的 然后名字叫 tokens 名字可以看心情去定义

但是 光有数据自然是无法完成逻辑 我们还需要一个存款的方法
我们可以这样写
这里 我们定义了两个方法 第一个用来充值ETH 第二个则是gerToken的充值函数
然后我们用public声明函数公开
然后payable则声明这是个充值的函数

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

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

    }

在这里插入图片描述
然后 我们在上面定义一个常量

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

在这里插入图片描述
正常来讲 这个的值应该是一个地址 是我们 ETH代币对应的地址 但是 我们这里只是模拟 就随便写了一个 address(0)
这样也符合address的一个地址规范
然后 我们还得写一个事件来充当日志 记录充值操作
这里 我写的 叫 Deposit 这个 其实大家可以顺便取名字 你叫 A 叫B都可以
参考代码如下

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

然后 我们的depositEther 存入 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 就是我们刚刚创建的代币标识 值是address(0) 这个只是我们乱写的一个地址 因为我们目前只是测试
我们就假设我们的交易所 0就代表ETH 然后msg.sender 代表当前用户的地址 然后msg.value是需要操作的金额
之前我们说过 这个tokens是存储 代币地址 然后 用户持有数量的对象

这里 我们操作 add 给用户在token对象中的对应ETH 加上msg.value的数值
然后 我们调用自己刚刚写的Deposit来记录这次交易
Deposit 第一个参数 ETHER 代币地址 msg.sender 当前用户 msg.value 操作数值 tokens[ETHER][msg.sender] 在tokens中找到代币地址对应的对象下的对应当前用户对应的数值 简单说 最后一个要的是用户该代币的总数

然后 ETH的充值操作已经完成了
那么 grToken也需要一个充值逻辑
但是 我们不应该直接在交易所中操作 而是要通过我们代币的合约 去操作
我们引入 grToken的合约文件
在这里插入图片描述
然后 depositToken 代码编写如下

//转入  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]);
}

这里 我们两个参数 _token代币地址 _amount需要充入金额
然后 我们的下一句可能有问题 所以套上了require来调用 之前我们讲过 require 如果代码错误 他会立刻停止程序 并将错误记录在区块链上
然后我们实例化grToken 合约对象 地址就 传入我们的_token调用其中的transferFrom函数 扣款用户就是msg.sender 当前操作这个函数的用户 收款地址address(this)我们交易所本身 数额就是方法接到的_amount参数
然后 操作完成之后 我们再次通过tokens 操作用户在对象中的代币数值
然后调用 我们刚刚定义的 Deposit 记录交易
然后 我们编译一下试试
终端输入

truffle compile

在这里插入图片描述
没有任何问题

但 我们部署之前 还需要写个脚本 你别合约好不容易写完了 但没有写脚本去使用它
我们在migrations目录下创建一个2_contract.js
编写代码如下

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);
}

这里 我们导入了grToken代币合约和Exchange交易所合约
然后 调用web3的getAccounts拿到用户列表
然后发布两个合约
其中 Exchange本身需要两个参数 一个是 收款用户 就是交易所得到的小费给哪个用户 以及后面的费率
我们这里费率少一点 写个百分之三吧
然后 地址 我们直接在用户列表中找到下标为0的用户 因为发布时 燃料消耗的第一个用户 那么 小费自然也应该归他

我们终端执行

truffle migrate --reser

更新发布一下智能合约
在这里插入图片描述
这里 发布成功了 但是 我们还是得测试一下

首先 我们用MetaMask导入一下 我们ganache中的第一个用户
在这里插入图片描述
我们在项目根目录下的scripts 目录下创建 test.js
参考代码如下

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()
}

我们先导入了 代币 grToken 和 交易所 Exchange的合约
然后 调用了Exchange的depositEther 来充值ETH 具体要存入的用户 依旧通过getAccounts拿取用户列表 然后操作第一个去存储
然后 我们终端执行

truffle exec .\scripts\test.js

在这里插入图片描述
然后查看 MetaMask 会发现用户的款确实扣了
在这里插入图片描述
在运行一次 就又少 10
在这里插入图片描述
当然 这里只是存入 但对用于来讲 最直观的就是看ETH少了
而且应该还会更少一些 因为 还有燃料需要消耗
还挺坑的 老实说

然后 我们可以查一下存款
scripts 目录下创建 test.js
编写代码如下

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()
}

我们这里直接写死了0x0000000000000000000000000000000000000000 这个就是我们设置以太坊的地址
然后 通过交易所定义的 tokens 定义的get特性 通过ETH 的地址和账号地址查看存入的代币
然后 我们再次运行

truffle exec .\scripts\test.js

因为我们测试操作了很多次 所以 这个数值是没问题的
在这里插入图片描述
大不了 我们再来一次
在这里插入图片描述
又存进去10 变成了 130
在这里插入图片描述
我们账号也其实少了很多
那么 ETH就好了

然后 我们来操作 grToken

但是 这个 我们需要先授权 允许交易所来操作我们的grToken

我们直接给scripts 目录下创建 test.js 写成这样

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()
}

这里 我们先调用grTokenDai我们上文中写的 交易所授权函数approve 第一个参数 交易所地址 我们取exchage的address 授权的数量 是 100000授权账号是 第一个账号
然后 我们调用我们交易所写的depositToken 存入grToken代币
然后 第一个参数是grTokenDai的address地址 然后数量10000 存入的用户还是我们的第一个用户
然后 我们查看grtoken数量
因为grTokenDai.address
我们再次运行

truffle exec .\scripts\test.js

可以看到这个效果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/843663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++初阶缺省参数

目录 缺省参数全缺省半缺省有意义的使用场景注意点 缺省参数 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时&#xff0c;如果没有指定实参则采用该形参的缺省值&#xff0c;否则使用指定的实参。 举例&#xff1a;Func(2),那么a就是2&#xff0c;Fu…

LeetCode[210]课程表II

难度&#xff1a;Medium 题目&#xff1a; 现在你总共有 numCourses 门课需要选&#xff0c;记为 0 到 numCourses - 1。给你一个数组 prerequisites &#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示在选修课程 ai 前 必须 先选修 bi 。 例如&#xff0c;想要学…

8.7面经

1.字符串是怎么实现不可变的&#xff0c;不可变类&#xff0c;字符串不可变的好处 &#xff08;1&#xff09;保存字符串的数组被 final 修饰且为私有的&#xff0c;并且String 类没有提供/暴露修改这个字符串的方法。 (2)String 类被 final 修饰导致其不能被继承&#xff0c…

2023上半年京东烘干机行业品牌销售排行榜(京东商品数据)

随着人们消费水平的提高&#xff0c;追求健康品质消费的用户越来越多&#xff0c;这样的消费升级为市场的发展带来很大的动力。同时&#xff0c;随着洗衣机市场趋向饱和&#xff0c;增长趋于平缓&#xff0c;更新换代和结构升级成为行业的主旋律。 在这一市场背景下&#xff0…

【VUE】项目本地开启https访问模式(vite4)

在实际开发中&#xff0c;有时候需要项目以https形式进行页面访问/调试&#xff0c;下面介绍下非vue-cli创建的vue项目如何开启https 环境 vue: ^3.2.47vite: ^4.1.4 根据官方文档&#xff1a;开发服务器选项 | Vite 官方中文文档 ps&#xff1a;首次操作&#xff0c;不要被类…

Crond计划任务和用户权限提升(三十七)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、一次性任务 二、周期性任务 ​编辑 三、用户权限提升 3.1 su 3.2 sudo提权 总结 前言 计划任务是在指定的时间间隔内自动执行的任务。在Linux系统中&#xff…

开源项目如何贡献代码

以腾讯犀牛鸟开源项目ncnn为例 目录 fork项目仓库 添加远程仓库 同步更新仓库 贡献代码提交新PR PR未merge更新PR fork项目仓库 只需要做一次 到仓库页面点击fork&#xff0c;然后create forkTencent/ncnn: ncnn is a high-performance neural network inference frame…

Grafana 曲线图报错“parse_exception: Encountered...”

问题现象 配置的Grafana图报错如下&#xff1a; 原因分析 点开报错&#xff0c;可以看到报错详细信息&#xff0c;是查询语句的语法出现了异常。 变量pool的取值为None 解决方案 需要修改变量pool的查询SQL&#xff0c;修改效果如下&#xff1a; 修改后&#x…

Gym 0.26.2版本简明教程【1】

Gym 0.26.2版本简明教程【1】 文章目录 Gym 0.26.2版本简明教程【1】1. 构建gym环境2. gym.Env常用method3. 关于 gym 0.26.2新版本的更改4. Reference 1. 构建gym环境 使用gym.make(id,render_mode)我们可以注册一个环境&#xff0c;然后我们可以查看一下这个环境的一些信息&…

自动化处理,web自动化测试处理多窗口+切换iframe框架页总结(超细整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 web 自动化之处理…

TVS二极管能替代稳压二极管吗?有什么区别?

经常有应用工程师询问&#xff1a;TVS二极管能否替代替换稳压二极管&#xff1f;接下来&#xff0c;TVS厂家东我电子针对TVS二极管和稳压二极管这个话题&#xff0c;详细讲解下这两者的相同点和不同点。 TVS二极管英文名是&#xff1a;Transient voltage suppression diode&am…

3.01 用户在确认订单页收货地址操作

用户在确认订单页面&#xff0c;可以针对收货地址做如下操作&#xff1a; 1. 查询用户的所有收货地址列表 2. 新增收货地址 3. 删除收货地址 4. 修改收货地址 5. 设置默认地址步骤1&#xff1a;创建对应用户地址BO public class AddressBO {private String addressId;private…

.NET SqlSuger 简单介绍,超快开发数据库

文章目录 前言SqlSugar使用我的环境Nuget 安装新建连接串DB First 和 Code First使用增删改查 总结 前言 我之前介绍过EFCore 怎么使用Nuget快速创建数据库&#xff0c;我之后发现SqlSugar更快。这里简单再说一下SqlSugar如何使用 .NET Core 数据库DB First自动生成&#xff0…

3.核心抽象

3.1 环境对象 StreamExecutionEnvironment是Flink应用开发时的概念&#xff0c;表示流计算作业的执行环境&#xff0c;是作业开发的入口、数据源接口、生成和转换DataStream的接口、数据Sink的接口、作业配置接口、作业启动执行的入口。Environment 是 运 行 时 作 业 级 别 的…

如何使用自己域名进行远程访问内网群晖NAS 6.X

使用自己的域名远程访问内网群晖NAS 6.X【内网穿透】 文章目录 使用自己的域名远程访问内网群晖NAS 6.X【内网穿透】 在之前的文章中&#xff0c;我们向大家演示了如何使用cpolar&#xff0c;创建一条固定的、能够在公共互联网登录内网群晖NAS的数据隧道。这条隧道已经能够应对…

TextBrewer:融合并改进了NLP和CV中的多种知识蒸馏技术、提供便捷快速的知识蒸馏框架、提升模型的推理速度,减少内存占用

TextBrewer:融合并改进了NLP和CV中的多种知识蒸馏技术、提供便捷快速的知识蒸馏框架、提升模型的推理速度&#xff0c;减少内存占用 TextBrewer是一个基于PyTorch的、为实现NLP中的知识蒸馏任务而设计的工具包&#xff0c; 融合并改进了NLP和CV中的多种知识蒸馏技术&#xff0…

Bean基本注解开发和Bean依赖注入注解开发

目录 1.Bean基本注解开发 Component Scorelazy PostConstruct和PreDestroy RepositoryServiceController 2.Bean依赖注入注解开发 Value Autowired Qualifier Resource 扩展AutoWired 1.Bean基本注解开发 基本Bean注解&#xff0c;主要是使用注释的方式替代原有xml的…

记录--说一说css的font-size: 0

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 平常我们说的font-size&#xff1a;0&#xff1b;就是设置字体大小为0对吧&#xff0c;但是它的用处不仅仅如此哦&#xff0c;它还可以消除子行内元素间额外多余的空白&#xff01; 问题描述&#xff…

Android前沿技术?Jetpack如何?

Jetpack Compose是Android开发领域的一项前沿技术&#xff0c;它提供了一种全新的方式来构建用户界面。近年来&#xff0c;Jetpack Compose在各大招聘等网站上的招聘岗位逐渐增多&#xff0c;薪资待遇也相应提高。本文将从招聘岗位的薪资与技术要求入手&#xff0c;分析Jetpack…

《Java-SE-第三十章》之哲学家就餐问题

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水平很有限&…