DAPP开发初探

2023-11-01

前言

最近DAPP的开发貌似很火,学习了区块链的一些知识之后,相信有很多人和我一样,也想了解开发一个DAPP是一个怎样的流程。

下面将通过一个简单的栗子来初识一下DAPP的开发流程,届时,我们也将开发出第一个DAPP应用–《永存的留言》。

在线体验(Ludis):http://words.ldsun.com/

项目介绍

《永存的留言》是一个基于以太坊的在线留言平台。它的功能十分简单–用户可以在平台上进行留言,平台每10s随机的展示留言内容。
但是它的特点在于,利用区块链的特性,保证了数据的真实性、完整性和安全性。
这里写图片描述

  • 使用Solidity开发后端方法
  • 使用Truffle框架
  • 基于unbox react脚手架项目
  • 部署在以太坊测试网络上Ropoetn Test Network
  • 使用MetaMask钱包插件发布交易

开发步骤

下载react项目模板

确保本地已经准备好Truffle所需的环境,准备以下命令,下载react项目模板。
$ mkdir a && cd a
truffle unbox react
当看到 Unbox successful. Sweet!提示时,表明下载成功。

编写智能合约

这是我们唯一的实现合约,包含的发送留言和读取留言的方法。

pragma solidity ^0.4.19;

contract SimpleStorage {
    // 留言结构体
    struct Message {
        string word; // 留言
        address from; // 留言者地址
        string timestamp ; // 留言unix时间戳
    }

    Message[] private wordArr;

    /**
     * 写入留言的方法
     */
    function setWord(string s, string t) public {
        wordArr.push(Message({
            word: s,
            from: msg.sender,
            timestamp: t
        }));
    }

    /**
     * 获取随机留言的方法
     */
    function getRandomWord(uint random) public view returns (uint, string, address, string) {
        if(wordArr.length==0){
            return (0, "", msg.sender, "");
        }else{
            Message storage result = wordArr[random];
            return (wordArr.length, result.word, result.from, result.timestamp);
        }
    }
}

编译、部署合约

修改发布的脚本。
var SimpleStorage = artifacts.require("./SimpleStorage.sol");
//var Words = artifacts.require("Words");

module.exports = function(deployer) {
  deployer.deploy(SimpleStorage);
  //deployer.deploy(Words);
};
执行truffle compile进行合约的编译。
获取合约地址。
  • 这里我们打开MetaMask钱包插件,左上角选择Ropoetn Test Network网络.
    这里写图片描述
  • 利用Remix编译,发布合约到以太坊测试环境。
    这里写图片描述
  • 通过MetaMask的交易hash查询,获取已经部署到以太坊测试网络中的合约地址。
    这里写图片描述

编写前端页面

这个部分主要是编写前端的展示效果和与合约交互的逻辑,这一部分最难编写,也最耗时间。

  • 主要逻辑代码
const contractAddress = "0x39e5196750dcddb1aaf6dda7d6e8dbb633482905" // 合约地址(以太坊测试网络)
var simpleStorageInstance // 合约实例

class App extends Component {
  // 初始化构造
  constructor(props) {
    super(props)
    this.state = {
        word: null,
        from: null,
        timestamp: null,
        random: 0,
        count: 0,
        input: '',
        web3: null,
        emptyTip: "还没有留言,快来创建全世界第一条留言吧~",
        firstTimeLoad: true,
        loading: false,
        loadingTip: "留言正在写入,请耐心等待~",
        waitingTip: "留言正在写入,请耐心等待~",
        successTip: "留言成功",
        animate: "",
        in: css(styles.in),
        out: css(styles.out)
    }
  }

  // 获取Web3实例
  componentWillMount() {
    // Get network provider and web3 instance.
    getWeb3
    .then(results => {
      this.setState({
        web3: results.web3
      })
      // Instantiate contract once web3 provided.
      this.instantiateContract()
    })
    .catch(() => {
      console.log('Error finding web3.')
    })
  }

  // 获取合约实例
  instantiateContract() {
     /*
     * SMART CONTRACT EXAMPLE
     *
     * Normally these functions would be called in the context of a
     * state management library, but for convenience I've placed them here.
     */
    const contract = require('truffle-contract')
    const simpleStorage = contract(SimpleStorageContract)
    simpleStorage.setProvider(this.state.web3.currentProvider)

    // Get accounts.
    this.state.web3.eth.getAccounts((error, accounts) => {
      simpleStorage.at(contractAddress).then(instance => {
        simpleStorageInstance = instance

        /*simpleStorage.deployed().then((instance) => {
        simpleStorageInstance = instance // 部署本地Ganache*/
        console.log("合约实例获取成功")
      })
      .then(result => {
        return simpleStorageInstance.getRandomWord(this.state.random)
      })
      .then(result => {
                console.log("读取成功", result)
                if(result[1]!=this.setState.word){
                    this.setState({
                        animate: this.state.out
                    })
                    setTimeout(() => {
                        this.setState({
                            count: result[0].c[0],
                            word: result[1],
                            from: result[2],
                            timestamp: result[3],
                            animate: this.state.in,
                            firstTimeLoad: false
                        })
                    }, 2000)
                }else{
                    this.setState({
                        firstTimeLoad: false
                    })
                }
        this.randerWord()
      })

    })
  }

  // 循环从区块上随机读取留言
  randerWord() {
    setInterval(() => {
      let random_num = Math.random() * (this.state.count? this.state.count: 0)
      this.setState({
        random: parseInt(random_num)
      })
      console.log("setInterval读取", this.state.random)
      simpleStorageInstance.getRandomWord(this.state.random)
      .then(result => {
                console.log("setInterval读取成功", result)
                if(result[1]!=this.setState.word){
                    this.setState({
                        animate: this.state.out
                    })
                    setTimeout(() => {
                        this.setState({
                            count: result[0].c[0],
                            word: result[1],
                            from: result[2],
                            timestamp: result[3],
                            animate: this.state.in
                        })
                    }, 2000)
                }
      })
    }, 10000)
  }

   // 写入区块链
  setWord(){
        if(!this.state.input) return
        this.setState({
            loading: true
        })
    let timestamp = new Date().getTime()
    simpleStorageInstance.setWord(this.state.input, String(timestamp), {from: this.state.web3.eth.accounts[0]})
    .then(result => {
            this.setState({
                loadingTip: this.state.successTip
            })
            setTimeout(() => {
                this.setState({
                    loading: false,
                    input: '',
                    loadingTip: this.state.waitingTip
                })
            }, 1500)

        })
        .catch(e => {
            // 拒绝支付
            this.setState({
                loading: false
            })
        })
  }
  // 时间戳转义
  formatTime(timestamp) {
      let date = new Date(Number(timestamp))
      let year = date.getFullYear()
      let month = date.getMonth() + 1
      let day = date.getDate()
      let hour = date.getHours()
      let minute = date.getMinutes()
      let second = date.getSeconds()
      let fDate = [year, month, day, ].map(this.formatNumber)
      return fDate[0] + '年' + fDate[1] + '月' + fDate[2] + '日' + ' ' + [hour, minute, second].map(this.formatNumber).join(':') 
  }
  /** 小于10的数字前面加0 */
  formatNumber(n) {
      n = n.toString()
      return n[1] ? n : '0' + n
  }

}

运行项目

使用npm start启动项目,浏览器的3000端口运行。
效果如下(一定要连接到Ropoetn Test Network网络才可以)。
这里写图片描述

总结

这样我们就开发出了我们的第一个DAPP,体会了开发的基本流程。

  • 明确项目需求
  • 确定开发环境
  • 编写、测试、部署合约
  • 设计前端页面,使前后端交互
  • 发布测试

项目源码

GitHub

参考文章

Ludis的博文

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DAPP开发初探 的相关文章

  • Unity移动端Input---触控

    void Update Input GetKeyDown KeyCode Space Input GetAxis Horizontal 按左 右箭头或A D键 Input GetAxis Vertical 按上 下箭头或w s键 Input

随机推荐

  • Selenium 官网打不开怎么办?

    Selenium 官网打不开怎么办 刚接触Selenium的时候发现官网打不开 很是奇怪 我以为网站挂了 后来才知道这个网站也被屏蔽了 那就不上了呗 可是学习资料哪里找 我找了一下 github啊 selenium在github上开源 上面
  • 单链表实现(C++)

    C 实现单链表数据结构 myList h ifndef MYLIST H define MYLIST H typedef struct node int data struct node next Node class myList pub
  • 使用Sequelize模块操作数据库之增删改查

    前面我们已经了解了Sequelize中模型的创建 现在我们去了解一下通过 Sequelize 对数据库进行增删改查操作 在操作数据库时 增删改操作比较简单 我们会侧重去讲解查询数据库 简单 INSERT 语句 添加语句是通过Model cr
  • 牛客小白月赛61 F.选座椅(双指针)

    牛客小白月赛61 F 选座椅 双指针 显然 l r l r l r 满足 l
  • OpenAI-ChatGPT最新官方接口《嵌入向量式文本转换》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(五)(附源码)

    Embeddings 嵌入向量式文本转换 前言 Overview 概述 What are embeddings 什么是嵌入 How to get embeddings 如何获取嵌入 python代码示例 cURL代码示例 Embedding
  • Jenkins通过kubernetes plugin连接K8s集群

    一 Jenkins安装kubernetes plugin插件 1 1 点击左侧系统管理 1 2 点击插件管理 1 3 安装插件Kubernetes plugin 1 4 安装好后重启Jenkins 浏览器输入http 192 168 0 1
  • 嵌入式硬件系统的基本组成

    嵌入式硬件系统的基本组成 嵌入式系统的硬件是以包含嵌入式微处理器的SOC为核心 主要由SOC 总线 存储器 输入 输出接口和设备组成 嵌入式微处理器 每个嵌入式系统至少包含一个嵌入式微处理器 嵌入式微处理器体系结构可采用冯 诺依曼 Von
  • Jmeter@场景负载加压

    目录 性能测试Jmeter 常用的主流场景 场景一 Thread Group 场景二 jp gc Stepping Thread Group 场景三 jp gc Ultimate Thread Group 场景四 bzm Concurren
  • 吴博:京东应用架构设计与治理

    吴博 京东应用架构设计与治理 经过十年的业务快速发展 京东信息系统复杂度越来越高 一般电商系统只需关心 进销存 中的 销 京东系统需要管理采购 进 销售 销 和库存 存 三个环节 系统做水平垂直拆分后 需要解决系统间如何解藕 如何保证高效通
  • python 基于异步的编程

    背景 除了顺序执行和并行执行的模型之外 还有第三种模型 叫做异步模型 这是事件驱动模型的基础 异步活动的执行模型可以只有一个单一的主控制流 能在单核心系统和多核心系统中运行 在并发执行的异步模型中 许多任务被穿插在同一时间线上 所有的任务都
  • 数字时序:时钟信号、抖动、迟滞和眼图

    转载地址 https www mr wu cn digital timing clock signals jitter hystereisis and eye diagrams 时钟信号 Clock Signals 发送数字信号其实发送的就
  • python-相似度计算的三种常用方法

    协同过滤就是通过将用户和其他用户的数据进行对比来实现推荐的 1 相似度计算 用欧氏距离来计算 相似度用距离来衡量 距离越大 相似度越小 距离越小 相似度越大 2 皮尔逊相关系数 这个参数用来度量两个向量之间的相似度 corroef 进行计算
  • 【Linux技术】linux连接mysql错误解决方案

    1 源码 connect mysql c include
  • C# Socket.Connect连接请求超时机制

    作者 RazanPaul 译者 Todd Wei 原文 http www codeproject com KB IP TimeOutSocket aspx 转自 http hi baidu com wf studio blog item d
  • Nginx配置及linux系统内存高并发多方面优化

    原文地址 http blog csdn net qq 23598037 article details 79505398 nginx的优化 1 gzip压缩优化 2 expires缓存有还 3 网络IO事件模型优化 4 隐藏软件名称和版本号
  • 基于数据挖掘的社交网络情感分析研究

    基于数据挖掘的社交网络情感分析研究 摘 要 随着近年来国内诸如微博 微信 国外诸如推特 Facebook 等知名社交网络平台及在线社交网络平台访问量的呈爆发式快速增长 人们已越来越地倾向于直接在各类社交网络平台基础上来表达其自己表达的各种观
  • JavaWebMyBatis中文写入数据库变问号解决方式

    首先感谢大佬给我思路 有同学也会这样 在学习javaweb时中文插入数据库变成了问号 如果你没用框架 那就在链接数据库的url后面加上 characterEncoding utf8 useUnicode true即可 具体可参考这篇文章 如
  • 16瓶药水一瓶有毒,去小白鼠测试哪一瓶水有毒?

    16瓶药水一瓶有毒 去小白鼠测试哪一瓶水有毒 面试的时候有个面试官问我 有16瓶药水 其中一瓶有毒 一只小白鼠喝过之后 一天之后会死亡 要求在少于15只小白鼠的情况下判断出哪一瓶有毒 药水可以兑在一起 小白鼠也可以喝多瓶药水 我在面试的时候
  • Filter——实现权限拦截

    创建Login jsp success jsp error jsp login jsp
  • DAPP开发初探

    前言 最近DAPP的开发貌似很火 学习了区块链的一些知识之后 相信有很多人和我一样 也想了解开发一个DAPP是一个怎样的流程 下面将通过一个简单的栗子来初识一下DAPP的开发流程 届时 我们也将开发出第一个DAPP应用 永存的留言 在线体验