web3学习系列——合约的部署与交互

1,010 阅读9分钟

经过一段时间的学习,我写了一些简单的solidity代码,然后我决定把它们部署到区块链上

测试环境部署

这里我们选择的环境是

image.png

会发现我们的钱包被自动注入了,然后点击确认部署合约

image.png

我理解的Injected Provider 就是从本地的 VM测试环境 切换到 网络的测试环境

image.png

部署成功咯

image.png

这没什么难的

合约部署合约

image.png

这个我理解的就是当我们在某个合约中引入另一个合约,并且这个实例化这个合约,那么我们在部署当前合约时就相当于一块部署引入的合约

部署好合约后,未初始化引入的合约实例时,可以看到,simpleStorage的实例全是0

image.png

初始化后

image.png

那我们就得到一个实际的地址

合约互相交互

对于一个程序员来说,这真的没啥哈哈,就是函数互相调用

image.png

继承和重载

关注一下写法,和两个关键字,可以被修改的标识是virtual,覆盖的标识是override

image.png

FundMe合约

通过一个简单的交易合约,我们再来学习一些新的内容

image.png

  1. payable

    payable是一个函数修饰符,用于指示函数可以接收以太币。在Solidity中,如果你想让一个函数接收以太币(即在交易中有ether被发送到这个函数),那么这个函数需要被标记为payable

  2. require

    require函数是一种用于条件检查的控制结构,常用于验证函数调用的前提条件。如果require的条件返回false,则当前函数执行会立即终止,所有状态更改都会回滚,且未花费的Gas会被退回。此外,require函数可以接受第二个参数作为错误信息,当条件不满足时,这个错误信息会被返回,便于调试。

  3. msg.value的含义

    在Solidity中,每个交易都有一个全局变量msg,它包含了交易的一些重要信息。msg.value是其中的一个字段,表示随交易发送的以太币(ETH)数量,以wei为单位(1 ETH = 10^18 wei)。msg.value常用于处理接收以太币的函数中,以确保正确的金额被发送。

设定的最小交易量是Usd,但是Eth的单位是wei,如何比较?

先来学一个新的概念

data feed

来自GPT

在区块链和智能合约的上下文中,"data feed"通常指的是从区块链外部世界(off-chain)到区块链内部(on-chain)的数据传输机制。因为智能合约运行在封闭的、去中心化的区块链网络上,它们无法直接访问或验证外部世界的数据(如股票价格、天气信息、货币汇率等)。数据馈送提供了一种解决方案,使得智能合约能够依据外部世界的真实信息来执行逻辑。

docs.chain.link/data-feeds/…

image.png

我们在Remix里面打开一下这段代码,这是最新的BTC和USD的转化,没找到ETH

image.png

image.png

看起来没问题,Solidity不支持浮点数或实数类型,这里的小数位数应该是8位

区块链是一个确定的系统,如果我们想要一些实时的、随机的东西,就需要预言机,chainLink是预言机网络

image.png 像我们刚刚实现的就是data Feed 获取外部数据

调用外部API

来自GPT

Chainlink允许智能合约安全地访问链下(off-chain)数据,包括通过HTTP请求从外部API获取数据。由于智能合约本身无法直接进行HTTP请求或访问外部服务器,Chainlink通过预言机节点(Oracles)提供了一种机制,使智能合约能够间接地发送HTTP请求并接收响应。这一功能极大地扩展了智能合约的应用场景,使其能够利用外部的世界数据,如天气信息、股票价格、飞行数据等。

Single Word Response | Chainlink Documentation

我们找到这个合约,然后在remix里面打开。这段Solidity代码定义了一个名为APIConsumer的智能合约,利用Chainlink网络从外部API(例如CryptoCompare)获取数据。

在部署这个合约之前,先学习一下Link和ETH之间的区别

LINK:LINK是Chainlink网络的原生代币。Chainlink是一个去中心化的预言机网络,它允许智能合约安全地访问链下数据、支付API调用和执行链下计算。LINK代币用于支付Chainlink网络中的节点操作员,这些操作员为智能合约提供外部数据和计算服务。

ETH:ETH是以太坊区块链的原生货币。以太坊是一个广泛使用的智能合约平台,允许开发者部署去中心化应用(DApps)和智能合约。ETH主要用于支付交易费用和计算服务费(称为gas),这是在以太坊网络上执行操作(如执行智能合约)的必要成本。

然后我们去Sepolia网络上获取一些link

image.png 我们需要去找到这个地址,然后导入我们的钱包中,这样就可以使用刚刚获取到的link币

image.png

拿到了

image.png 然后如果要运行这个合约,需要从钱包给这个合约发送一点link币,

fee = (1 * LINK_DIVISIBILITY) / 10; 
这段代码表明我们每次执行任务需要0.1Link

我们给部署好的合约发送0.2 Link image.png

调用API查看一下,发现有结果了

image.png

requestVolumeData是一个公共函数,任何人都可以调用它来触发一个Chainlink请求,从指定的URL获取ETH的24小时交易量。

调用外部接口

直接粘贴版

获取ABI代码 github.com/smartcontra…

获取部署在当前网络的合约的地址

image.png

切换到测试网部署运行函数,成功了

image.png

GitHub引入版

image.png

Solidity中的浮点数


// 定义一个public的函数getPrice,这个函数不接受任何参数,返回一个uint256类型的值,表示资产的价格。
// 由于使用了view修饰符,这个函数不会修改合约状态,仅仅是读取数据。
function getPrice() public view returns(uint256) {
    // 创建一个AggregatorV3Interface接口的实例,指向Chainlink预言机合约的地址。
    // 这个地址是Chainlink网络中特定资产价格预言机的地址。
    AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);
    
    // 调用priceFeed实例的latestRoundData函数获取最新的价格信息。
    // latestRoundData函数返回多个值,但这里我们只关心第二个返回值,即当前的价格(price)。
    // Chainlink预言机返回的价格是一个int256类型的值,可以表示负值,虽然价格通常不会是负的。
    (,int256 price,,,) = priceFeed.latestRoundData();

    // 将获取到的价格price转换为uint256类型,并且由于Chainlink返回的价格精度是8位小数,
    // 而这里我们希望返回的价格精度是18位小数(符合以太坊的习惯),所以将价格乘以10的10次方进行转换。
    // 最后返回调整精度后的价格。
    return uint256(price * 1e10);
}

// 定义一个public的view函数getConversionRate,接受一个参数ethAmount(以太坊的数量),
// 并返回一个uint256类型的值,表示按当前ETH到USD的汇率计算出的ethAmount的美元价值。
// 由于使用了view修饰符,这个函数不会修改合约状态,仅仅是读取数据。
function getConversionRate(uint256 ethAmount) public view returns (uint256){
    // 调用getPrice函数获取当前1 ETH等于多少美元(USD)。
    // getPrice函数返回的价格已经根据Chainlink预言机调整为18位小数的格式。
    uint256 ethPrice = getPrice();
    
    // 计算提供的ethAmount按当前ETH到USD的汇率转换后的美元价值。
    // 首先将ethPrice乘以ethAmount,结果是以wei为单位的ETH的美元价值(因为ethAmount和ethPrice都是以18位小数表示的)。
    // 然后,将结果除以1e18来转换回标准的美元价值(不带小数)。
    // 这个操作实质上是将wei单位的ETH转换为标准单位的ETH,并计算其USD价值。
    uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1e18;
    
    // 返回计算出的美元价值。
    return ethAmountInUsd;
}

我们设定了需要返回的最小美元数是50,1ETH大概是3000美元,50/3000差不多等于0.016是我们所需要的eth数,然后再转化成Wei。为了保证交易顺利进行,我们在这里设定value值为20000000000000000,也就是0.021e18

image.png

image.png

Library

在Solidity中,using A for B;语句是一种特殊的语法,它允许你将库A中的函数附加到任何类型B的变量上。这意味着,一旦声明了这种使用方式,类型B的每个实例就能调用库A中为它定义的函数,就好像这些函数是它自己的方法一样。这使得库的函数可以写得像是作用于这个类型的实例方法。

我们新建一个priceConverter的合约,将前面写的函数迁移过来,封装成一个库。 函数的关键词要改成internal

image.png

将priceConverter这个函数附加到uint256这个类型上,msg.value就是uint256类型,所以它可以使用priceConverter,并且msg.value作为第一个参数隐式地传递给了getConversionRate函数。

image.png

transfer和send

image.png 这两段代码展示了在Solidity中将以太币(Ether)从智能合约转移给调用者(msg.sender)的两种不同方法:transfersend

  • 安全性transfersend都通过限制gas的使用来提高安全性,防止在资金转移操作中可能发生的重入攻击。
  • 错误处理transfer在失败时自动回滚,不需要额外的错误处理;而send在失败时返回false,需要开发者检查这个返回值并手动处理失败情况。
  • 选择使用哪一个:选择transfer还是send取决于你是否想在资金发送失败时让整个交易回滚(使用transfer),还是想手动处理这种失败情况(使用send)。

image.png 使用call来发送以太币是Solidity 0.6.x版本之后推荐的做法,因为它相较于sendtransfer提供了更高的灵活性。

修饰器

在Solidity中,修饰器(Modifiers)是一种特殊的声明,用于修改智能合约函数的行为。通过预先定义一些条件,修饰器可以在函数执行前或执行后添加自定义逻辑,例如验证条件、修改状态或事件记录等。这使得代码更加模块化、可重用,同时提高了智能合约的安全性。

image.png