链接是: https://remix.ethereum.org/
需要注意的是,Remix 网站的连接是否是https , 如果是使用http 切换成为https 的时候, 代码文件可能全部消失。
// SPDX-License-Identifier: GPL-3.0 -> 许可证
pragma solidity 0.8.14 : 编译器版本
memory 关键字是干嘛的?在 Solidity 中,memory 关键字用于声明临时内存变量。与存储变量不同,内存变量在函数执行期间存在,但在函数执行完毕后会被清除。
简单的hello world 代码
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12 <0.9.0;
contract HelloWorld {
/**
* @dev Prints Hello World string
*/
uint public myuint = 10000;
bool public is_dely = true;
function print() public pure returns (string memory) {
return "Hello World!";
}
}
代码的基本结构就是
unchecked 关键字可以用来标记一个代码块,指示编译器在该代码块内不进行整数溢出和下溢检查。这样,如果在 unchecked 块内发生溢出或下溢,Solidity 不会抛出异常,而是继续执行代码。开发人员应该非常谨慎地使用 unchecked 关键字,并确保在其内部的代码不会导致不良后果。
view : view 是一个函数修饰符,用于声明函数不会修改合约的状态。也就是说,被标记为 view 的函数只能读取合约的数据,而不能修改它们。这使得 Solidity 编译器能够进行优化,并允许在不向区块链写入数据的情况下调用这些函数。
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract ExampleStrings {
string public myString = "Hello World";
function setMyString(string memory _myString) public {
myString = _myString;
}
function compareTwoStrings(string memory _myString) public view returns(bool) {
return keccak256(abi.encodePacked(myString)) == keccak256(abi.encodePacked(_myString));
}
}
adress 存在一些属性, 例如balance,其他包括: - transfer:address 类型的变量可以通过 .transfer() 函数向另一个地址发送以太币 - send:与 transfer 类似,address 类型的变量也可以通过 .send() 函数向另一个地址发送以太币。不过,.send() 函数会返回一个布尔值,表示转账是否成功。 - call:address 类型的变量也可以通过 .call() 函数向另一个地址发送消息(调用函数)。 - type conversion:address 类型可以进行类型转换,将其转换为 payable address 类型,以便于进行转账操作。
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract ExampleAddress {
address public someAddress;
function setSomeAddress(address _someAddress) public {
someAddress = _someAddress;
}
function getAddressBalance() public view returns(uint) {
return someAddress.balance;
}
}
在 Solidity 中,msg 是一个全局变量,用于访问有关当前交易或消息的信息。msg 变量具有多个属性,常用的属性包括:
在 Solidity 中,constructor 是一种特殊的函数,用于在合约部署时执行初始化操作。constructor 函数有以下特点:
pragma solidity ^0.8.0;
contract MyContract {
uint256 public myNumber;
constructor(uint256 _initialNumber) {
myNumber = _initialNumber;
}
}
修改message , 当地址是本人的时候
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract TheBlockchainMessenger {
uint public changeCounter;
address public owner;
string public theMessage;
constructor() {
owner = msg.sender;
}
function updateTheMessage(string memory _newMessage) public {
if(msg.sender == owner) {
theMessage = _newMessage;
changeCounter++;
}
}
}
MetaMask 如何工作
EtherScan
↓↑
↓↑
Blockchain
↓↑
↓↑
BloackChain -> Bloackchain Node -> Infura -> MetaMask
↓↑
↓↑
Bacckend
↓↑
↓↑
Browser
以太坊交易(Ethereum transactions)是在以太坊区块链上发生的操作,可以涉及发送以太币(Ether)或调用智能合约等操作。每笔交易都会被打包到区块中,并且需要经过网络中的矿工进行验证和确认。以下是以太坊交易的一些重要特点和组成部分:
前三是必须的, 后面不是必须得
Transaction + Privete Key = Signed Transacction
↓ ECRECOVER
Private Key --------> ECDSA --------> Publick Key -------> Ethereum Account
ECDSA
ECDSA 是椭圆曲线数字签名算法(Elliptic Curve Digital Signature Algorithm)的缩写。它是一种基于椭圆曲线数学原理的数字签名算法,用于在公开密钥密码系统中对数据进行签名和验证。
ECDSA 的主要原理涉及到椭圆曲线上的点的加法和乘法运算。它利用了椭圆曲线上的离散对数难题,使得在已知公钥的情况下,无法有效地获取私钥。这种数学性质使得 ECDSA 在数字签名中具有很高的安全性。
ECDSA 算法包括以下几个关键步骤:
密钥生成:生成一对公钥和私钥,其中公钥用于验证签名,私钥用于生成签名。 签名生成:使用私钥对消息进行哈希,然后对哈希值进行数字签名生成。 签名验证:使用公钥对签名进行验证,确保签名的正确性和消息的完整性。 ECDSA 签名算法在加密货币领域被广泛使用,例如在比特币和以太坊等区块链网络中用于对交易进行签名和验证,确保交易的安全和不可篡改性。
ECRECOVER
ECRECOVER 是以太坊智能合约中的一个函数,用于从数字签名中恢复签名者的公钥。它通常用于验证以太坊交易中的数字签名,并从签名中提取签名者的地址。ECRECOVER 函数的主要作用是验证签名的有效性,并提取签名者的地址以进行后续的逻辑处理。
在以太坊智能合约中,ECRECOVER 函数接收四个参数:
ECRECOVER 函数在以太坊智能合约中经常用于验证交易的签名,确保交易的发送者是有效的,并且交易数据没有被篡改。
Cryptographic Hashing
library(digest)
## 原始数据
data <- "Hello, World!"
## 使用MD5哈希算法对数据进行哈希
md5_hash <- digest(data, algo = "md5")
## 使用SHA-256哈希算法对数据进行哈希
sha256_hash <- digest(data, algo = "sha256")
## 输出MD5哈希值
print(md5_hash)
## [1] "90db1978c9472c71314717beeb40ea8d"
## 输出SHA-256哈希值
print(sha256_hash)
## [1] "540daddd0ea64da95b229e7fc26e1499147002d863227db4b3ec8ae34f4bc30d"
哈希函数(Hash Function)是一种将任意长度的输入数据映射为固定长度的输出数据的函数。哈希函数通常被用于密码学、数据完整性验证、数据检索等领域。
哈希函数具有以下特性:
固定长度输出:哈希函数的输出具有固定的长度,不受输入数据长度的影响。 易计算性:对于给定的输入数据,哈希函数应该能够高效地计算出对应的哈希值。 不可逆性:理论上,不同的输入数据应该产生不同的哈希值;同时,从哈希值推导出原始输入数据应该是非常困难的,即使是微小的输入数据变化也应该导致输出哈希值的巨大变化。 雪崩效应:即使输入数据的微小变化也应该导致输出哈希值的巨大变化,使得哈希值的修改难以被察觉。 哈希函数在密码学中被广泛应用于密码哈希函数、数字签名、消息认证码、随机数生成等方面。
不同hash 函数的区别
MD5(Message Digest Algorithm 5)、SHA-1(Secure Hash Algorithm 1)和SHA-256(Secure Hash Algorithm 256)都是常见的哈希函数算法,它们之间有以下区别:
Hashing of Hashes , 对hash 的结果进行hashing
hashing of Blockchain , 都对区块链结果进行hashing。
总结
在以太坊中,Gas 是一种计算单位,用于衡量执行智能合约或交易所需的计算资源。Gas 被用来支付以太坊网络中的计算费用,以确保网络的安全性和可靠性。Gas 的作用如下:
Remix 链接 metamask
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract SampleContract {
string public myString = "Hello World";
function updateString(string memory _newString) public payable {
if(msg.value == 1 ether) {
myString = _newString;
} else {
payable(msg.sender).transfer(msg.value);
}
}
}
//payable(msg.sender).transfer(msg.value);:如果传入的以太币量不等于 1 ether,则将传入的以太币退还给发送交易的地址 msg.sender。
这个代码表示如果有一个以太币, 则修改字符串
Fallback 和 receive 函数
fallback
,并且不带任何参数或函数修饰符。receive
。总之,Fallback 函数和 receive 函数都用于处理合约接收到以太币的情况,但它们的使用方式和声明方式略有不同。Fallback 函数在 Solidity 0.6.0 版本之前存在,而 receive 函数是在 Solidity 0.6.0 版本中引入的新特性。
Receive()是一个在calldata为空时优先于fallback()的函数。但是,当calldata不符合有效的函数签名时,回退优先于接收。 ######## receive()
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract SampleFallback {
uint public lastValueSent;
string public lastFunctionCalled;
receive() external payable {
lastValueSent = msg.value;
lastFunctionCalled = "receive";
}
}
receive() external payable
是 Solidity
中用于接收以太币的特殊函数。它具有以下含义:
receive
函数是 Solidity 0.6.0 版本引入的新特性。external
关键字表示该函数只能通过外部调用触发,即只能由其他合约或外部账户向合约发送以太币来触发。payable
关键字表示该函数可以接收以太币。receive
函数没有参数和返回值。receive
函数,则会触发回滚。receive
函数通常用于处理合约接收到以太币后的具体逻辑,例如更新合约的状态、记录日志等操作。总之,receive() external payable
函数声明告诉编译器,当合约接收到以太币时,可以调用这个函数来处理接收到的以太币。
Fallback函数
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract SampleFallback {
uint public lastValueSent;
string public lastFunctionCalled;
receive() external payable {
lastValueSent = msg.value;
lastFunctionCalled = "receive";
}
fallback() external payable {
lastValueSent = msg.value;
lastFunctionCalled = "fallback";
}
}
写一个存款取款的智能合约
// SPDX-License-Identifier: MIT
pragma solidity >0.8.16;
contract SendWithdrawMoney {
uint public balanceReceived; // 用于记录合约收到的以太币数量
// 存款函数,接收以太币
function deposit() public payable {
// 将接收到的以太币数量累加到合约的余额中
balanceReceived += msg.value;
}
// 查看合约当前余额的函数
function getContractBalance() public view returns(uint) {
return address(this).balance; // 返回合约的当前余额
}
// 提取全部余额到调用者的函数
function withdrawAll() public {
// 将调用者的地址转换为可支付地址
address payable to = payable(msg.sender);
// 将合约的全部余额转账给调用者
to.transfer(getContractBalance());
}
// 将全部余额提取到指定地址的函数
function withdrawToAddress(address payable to) public {
// 将合约的全部余额转账给指定的地址
to.transfer(getContractBalance());
}
}
mapping
是 Solidity
中用于创建键值对映射的一种数据结构。它类似于其他编程语言中的字典或关联数组。
在 Solidity 中,mapping
通常用于将一个值与另一个值关联起来,类似于键值对的概念,其中一个值称为键,另一个值称为对应的值。mapping
的语法如下:
mapping(KeyType => ValueType) public myMapping;
KeyType
是键的数据类型,可以是任何合法的 Solidity
数据类型,如
uint
、address
、string
等。ValueType
是值的数据类型,也可以是任何合法的 Solidity
数据类型。public
是修饰符,指定了映射的可见性,表示可以通过合约的外部调用访问该映射。mapping
在区块链智能合约开发中经常用于以下情况:
管理账户余额: 在代币合约中,可以使用
mapping
来存储每个账户的余额。例如,mapping(address => uint256) public balances;
可以用来记录每个账户的余额。
存储状态信息: 在分布式应用程序中,可以使用
mapping
存储各种状态信息,如用户信息、商品信息、交易信息等。例如,mapping(address => User) public users;
可以用来存储用户信息。
权限管理: 在多方参与的合约中,可以使用
mapping
存储各种权限信息,如管理员权限、投票权限等。例如,mapping(address => bool) public isAdmin;
可以用来记录管理员权限。
索引和查询: mapping
可以用于构建索引,以便快速查询数据。例如,可以使用
mapping(uint256 => address) public index;
来记录地址的索引,以便快速查找地址对应的数据。
事件日志: 在合约中触发事件时,可以使用
mapping
将事件关联到相应的处理函数。例如,mapping(address => function) public eventHandlers;
可以用来存储不同事件对应的处理函数。
总的来说,mapping
是一种非常灵活和强大的数据结构,在区块链智能合约开发中具有广泛的应用场景,可以用于存储各种类型的数据和管理各种类型的状态信息。
使用mapping 追踪地址和余额
// SPDX-License-Identifier: MIT
// 声明合约的 Solidity 版本
pragma solidity ^0.8.14;
// 声明一个名为 MappingsStructExample 的智能合约
contract MappingsStructExample {
// 声明了一个名为 balanceReceived 的公共 mapping,用于存储地址对应的收到的金额
mapping(address => uint) public balanceReceived;
// 定义了一个公共视图函数 getBalance(),用于查看合约的余额
function getBalance() public view returns(uint) {
return address(this).balance;
}
// 定义了一个公共可支付函数 sendMoney(),用于向合约发送以太币,并将发送者的地址与收到的金额关联起来
function sendMoney() public payable {
balanceReceived[msg.sender] += msg.value;
}
// 定义了一个公共提款函数 withdrawMoney(),用于从合约中提取指定数量的以太币并转账给指定地址。如果发送者的余额不足,则会触发 require 断言。
function withdrawMoney(address payable _to, uint _amount) public {
require(_amount <= balanceReceived[msg.sender], "not enough funds");
balanceReceived[msg.sender] -= _amount;
_to.transfer(_amount);
}
// 定义了一个公共提款函数 withdrawAllMoney(),用于将发送者的全部余额提取并转账给指定地址
function withdrawAllMoney(address payable _to) public {
uint balanceToSend = balanceReceived[msg.sender];
balanceReceived[msg.sender] = 0;
_to.transfer(balanceToSend);
}
}
require 语句,用于在 Solidity合约中实现断言。它的作用是确保某个条件成立,否则将终止函数执行并回滚交易。
Solidity 中的 structs 是一种自定义的复合数据类型,它允许你定义包含不同数据类型的数据结构。structs 允许你将多个相关的变量组合在一起,形成一个逻辑单元,方便在合约中使用和管理。
// 定义一个名为 Person 的 struct,包含两个属性:name 和 age
struct Person {
string name;
uint age;
}
// 声明一个存储 Person 结构的数组
Person[] public people;
// 创建一个新的 Person 实例并添加到数组中
function addPerson(string memory _name, uint _age) public {
people.push(Person(_name, _age));
}
另外一个实际例子
// SPDX-License-Identifier: MIT
// Solidity版本声明,指定代码遵循MIT许可证
pragma solidity 0.8.15;
// PaymentReceived合约定义
contract PaymentReceived {
// 存储发送者地址和发送的金额
address public from;
uint public amount;
// 构造函数,接受发送者地址和金额作为参数
constructor(address _from, uint _amount) {
// 初始化from和amount变量
from = _from;
amount = _amount;
}
}
// Wallet合约定义
contract Wallet {
// PaymentReceived类型的公共状态变量payment
PaymentReceived public payment;
// payContract函数,用于向合约付款
function payContract() public payable {
// 创建新的PaymentReceived实例,传入发送者地址和发送的以太币数量
payment = new PaymentReceived(msg.sender, msg.value);
}
}
// 等价于
//SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract Wallet2 {
struct PaymentReceivedStruct {
address from;
uint amount;
}
PaymentReceivedStruct public payment;
function payContract() public payable {
payment = PaymentReceivedStruct(msg.sender, msg.value);
}
}
使用struct 比较节约gas,相比于合约嵌套
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;
contract MappingsStructExample {
struct Transaction {
uint amount;
uint timestamp;
}
struct Balance {
uint totalBalance;
uint numDeposits;
mapping(uint => Transaction) deposits;
uint numWithdrawals;
mapping(uint => Transaction) withdrawals;
}
mapping(address => Balance) public balanceReceived;
function getBalance(address _addr) public view returns(uint) {
return balanceReceived[_addr].totalBalance;
}
function depositMoney() public payable {
balanceReceived[msg.sender].totalBalance += msg.value;
Transaction memory deposit = Transaction(msg.value, block.timestamp);
balanceReceived[msg.sender].deposits[balanceReceived[msg.sender].numDeposits] = deposit;
balanceReceived[msg.sender].numDeposits++;
}
function withdrawMoney(address payable _to, uint _amount) public {
balanceReceived[msg.sender].totalBalance -= _amount; //reduce the balance by the amount ot withdraw
//record a new withdrawal
Transaction memory withdrawal = Transaction(msg.value, block.timestamp);
balanceReceived[msg.sender].withdrawals[balanceReceived[msg.sender].numWithdrawals] = withdrawals;
balanceReceived[msg.sender].numWithdrawals++;
//send the amount out.
_to.transfer(_amount);
}
}
require 是 Solidity 中的一个关键字,用于在执行智能合约函数时检查条件是否满足,如果不满足,则终止函数执行并回滚状态。通常情况下,require 语句用于验证函数参数、状态变量的值或者合约当前状态是否符合预期,如果不符合预期,则会抛出异常并终止函数执行。
require(condition, errorMessage);
其中,condition 是需要验证的条件,如果为 false,则会触发异常,终止函数执行;errorMessage 是可选的错误信息,用于指示发生了什么错误。
assert 是 Solidity 中的一个关键字,用于在智能合约中执行断言(assertion)。它与 require 关键字类似,但在语义上略有不同。
assert 语句用于在代码执行过程中验证一些不可变的条件,如果这些条件不满足,则会触发异常并导致交易失败。与 require 不同的是,assert 通常用于检查不可变的内部错误,如编程错误或者异常情况,而不是用于验证外部输入或者合约状态。
assert 语句的一般格式如下:
assert(condition);
其中,condition 是需要验证的条件,如果为 false,则会触发异常,终止函数执行并回滚状态。
try/catch 是 Solidity 0.8.0 版本引入的异常处理机制。它允许在智能合约中进行异常处理,提供了一种处理异常情况的方法,以便在出现错误时进行适当的处理而不导致交易失败。
try {
// 可能会引发异常的代码块
} catch Error(string memory reason) {
// 处理异常的代码块
} catch (bytes memory lowLevelData) {
// 处理底层异常的代码块
}
External Function Calls 和 Low-Level Calls 是 Solidity 中用于与其他智能合约或以太坊地址进行交互的两种主要方法。它们允许智能合约调用其他合约的函数或执行底层的 EVM 指令。下面是它们的详细解释:
定义:
语法:
external
或
public
。特点:
try/catch
语句来捕获异常。示例:
// 合约 A
contract ContractA {
function foo(uint256 x) external returns (uint256) {
return x * 2;
}
}
// 合约 B
contract ContractB {
ContractA public contractA;
constructor(address _contractA) {
contractA = ContractA(_contractA);
}
function bar(uint256 y) external returns (uint256) {
return contractA.foo(y);
}
}
定义:
语法:
address.call
或 address.delegatecall
方法来执行低级调用。特点:
示例:
// 合约 A
contract ContractA {
function foo(uint256 x) external returns (uint256) {
return x * 2;
}
}
// 合约 B
contract ContractB {
address public contractAAddr;
constructor(address _contractA) {
contractAAddr = _contractA;
}
function bar(uint256 y) external returns (uint256) {
// 使用低级调用执行合约 A 的 foo 函数
(bool success, bytes memory data) = contractAAddr.call(abi.encodeWithSignature("foo(uint256)", y));
require(success, "Call failed");
// 解析返回值
uint256 result;
assembly {
result := mload(add(data, 0x20))
}
return result;
}
}
比较和选择
一些要求:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
contract SampleWallet {
// 合约拥有者
address payable owner;
// 记录每个地址的转账额度
mapping(address => uint) public allowance;
// 记录哪些地址被允许发送交易
mapping(address => bool) public isAllowedToSend;
// 记录谁是合约的守护者
mapping(address => bool) public guardian;
// 下一个拥有者
address payable nextOwner;
// 守护者重置次数
uint guardiansResetCount;
// 守护者重置所需的确认次数
uint public constant confirmationsFromGuardiansForReset = 3;
// 构造函数,设置合约的初始拥有者为部署合约的账户
constructor() {
owner = payable(msg.sender);
}
// 提议设定新的合约拥有者
function proposeNewOwner(address payable newOwner) public {
// 需要调用者是守护者
require(guardian[msg.sender], "You are no guardian, aborting");
// 如果新的拥有者和当前拥有者不同,重置守护者确认次数
if(nextOwner != newOwner) {
nextOwner = newOwner;
guardiansResetCount = 0;
}
// 增加守护者重置次数
guardiansResetCount++;
// 如果守护者确认次数达到要求,更新合约拥有者
if(guardiansResetCount >= confirmationsFromGuardiansForReset) {
owner = nextOwner;
nextOwner = payable(address(0));
}
}
// 设置地址的转账额度
function setAllowance(address _from, uint _amount) public {
// 需要调用者是合约拥有者
require(msg.sender == owner, "You are not the owner, aborting!");
// 设置地址的转账额度并标记为允许发送交易
allowance[_from] = _amount;
isAllowedToSend[_from] = true;
}
// 拒绝某个地址发送交易
function denySending(address _from) public {
// 需要调用者是合约拥有者
require(msg.sender == owner, "You are not the owner, aborting!");
// 标记地址为不允许发送交易
isAllowedToSend[_from] = false;
}
// 发起转账交易
function transfer(address payable _to, uint _amount, bytes memory payload) public returns (bytes memory) {
// 检查转账金额不能大于合约的余额
require(_amount <= address(this).balance, "Can't send more than the contract owns, aborting.");
// 如果调用者不是合约拥有者,检查是否被允许发送交易以及转账额度是否足够
if(msg.sender != owner) {
require(isAllowedToSend[msg.sender], "You are not allowed to send any transactions, aborting");
require(allowance[msg.sender] >= _amount, "You are trying to send more than you are allowed to, aborting");
allowance[msg.sender] -= _amount;
}
// 调用外部合约的payable函数进行转账
(bool success, bytes memory returnData) = _to.call{value: _amount}(payload);
// 检查转账是否成功
require(success, "Transaction failed, aborting");
return returnData;
}
// 接收以太币的fallback函数
receive() external payable {}
}
学习目标:
Web3.js是一个JavaScript库,用于与以太坊区块链进行交互。它允许开发人员通过JavaScript代码与以太坊智能合约和节点进行通信,从而创建基于以太坊的去中心化应用(DApp)。
Web3.js提供了一系列API,可以用于执行以下操作:
连接到以太坊节点:通过Web3.js,可以连接到本地或远程的以太坊节点,以便与区块链网络进行通信。
与智能合约交互:可以使用Web3.js部署、调用和与以太坊智能合约进行交互,包括读取合约状态和调用合约方法。
发送交易:可以使用Web3.js创建和发送以太币交易或调用智能合约方法的交易。
监听区块链事件:Web3.js允许开发人员监听以太坊区块链上的事件,例如新块的生成、交易的发送和智能合约的状态变化。
管理以太坊钱包:可以使用Web3.js生成新的以太坊地址、签名交易、导入钱包等操作。
通过这些功能,Web3.js为开发人员提供了构建以太坊DApp所需的一切工具和功能。
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x407d73d8a49eeb85d32cf465507dd71d507100c1", "latest"],"id":1}'
// Result
{
"id":1,
"jsonrpc": "2.0",
"result": "0x0234c8a3397aab58" // 158972490234375000
}
Web3 Providers是Web3.js用来与以太坊节点进行通信的接口。在Web3.js中,可以使用不同类型的提供者(Providers)来连接到以太坊节点。常见的提供者类型包括HTTP、WebSocket和IPC(Inter-Process Communication)。下面是它们的主要特点和用途:
选择合适的提供者取决于应用的具体需求。对于需要实时数据更新或双向通信的应用,WebSocket Provider可能是更好的选择。而对于简单的读取数据或发送交易的应用,HTTP Provider通常足够了。 IPC Provider则适用于与本地节点进行通信的场景。
ABI 代表应用二进制接口。在 Remix 中,转到 Plugins 并启用 Debugger 插件。添加如下代码:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.14;
contract MyContract {
uint public myUint = 123;
function setMyUint(uint newUint) public {
myUint = newUint;
}
}
打开调试,
Web3js 试图提供与智能合同交互的良好功能
ABI Array 包含所有函数、输入和输出,以及智能契约中的所有变量及其类型。如果在 artifacts/MyContract.json 文件,则可以一直滚动到底部。在这里你可以找到 ABI 数组:
https://ethereum-blockchain-developer.com/2022-05-erc20-token/images/20220813133213.webp
添加以下脚本并从 Deploy & SendTransactions 插件添加的合同地址
(async() => {
const address = "ENTER_ADDRESS_HERE_FROM_RUN_TX_PLUGIN";
const abi = [
{
"inputs": [],
"name": "myUint",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newUint",
"type": "uint256"
}
],
"name": "setMyUint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
];
let contractInstance = new web3.eth.Contract(abi, address);
console.log(await contractInstance.methods.myUint().call());
let accounts = await web3.eth.getAccounts();
await contractInstance.methods.setMyUint(345).send({from: accounts[0]});
console.log(await contractInstance.methods.myUint().call());
})()
然后右键单击该文件并运行该脚本。
现在已经准备好了一些更高级的契约交互!
让我们从一个返回值的简单智能合同开始:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
contract EventExample {
mapping(address => uint) public tokenBalance;
constructor() {
tokenBalance[msg.sender] = 100;
}
function sendToken(address _to, uint _amount) public returns(bool) {
require(tokenBalance[msg.sender] >= _amount, "Not enough tokens");
tokenBalance[msg.sender] -= _amount;
tokenBalance[_to] += _amount;
return true;
}
}
使用虚拟机部署,让我们看看如果使用 JavaScriptVM
部署智能契约会发生什么。
现在让我们实际使用“ sendToken”函数。将 Account # 2的地址复制到“ _ to”字段中,并在 _ amount 字段中输入“1”,然后启动事务:
观察“事务”窗口中发生的情况!
通常,没有解码输出。发送事务是通常没有返回值的并发操作。有一些讨论是为了实际返回一些东西,但是在编写这些行时,事件在这里是为了从编写事务中发出值。让我们用一个真正的区块链测试这一点!
但是, 使用MetaMask 部署是没有输出结果的。
修改合同代码如下:
//SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
contract EventExample {
mapping(address => uint) public tokenBalance;
event TokensSent(address _from, address _to, uint _amount);
constructor() {
tokenBalance[msg.sender] = 100;
}
function sendToken(address _to, uint _amount) public returns(bool) {
require(tokenBalance[msg.sender] >= _amount, "Not enough tokens");
tokenBalance[msg.sender] -= _amount;
tokenBalance[_to] += _amount;
emit TokensSent(msg.sender, _to, _amount);
return true;
}
}
再使用MetaMask 部署, 则能够看到交易结果。
ERC 721
opensea : https://opensea.io/
OpenZeppelin 是一个广泛使用的开源框架,专门为以太坊和其他区块链平台上的智能合约开发提供安全、可靠的库和工具。它的目标是帮助开发者编写更安全的智能合约,并加速开发过程。以下是 OpenZeppelin 的主要特性和组件:
是以太坊智能合约开发中的三个主要框架和工具集,它们为开发、测试、部署和管理智能合约提供了强大的支持。以下是对每个工具的详细介绍:
Truffle 是一个开发框架,专为以太坊智能合约和去中心化应用(dApps)设计。它提供了全面的开发环境和工具集,使得智能合约的开发和管理更加容易。
Hardhat 是一个灵活的以太坊开发环境,专注于智能合约的编译、部署和测试。它提供了一个可扩展的插件系统,允许开发者根据自己的需求定制开发环境。
Foundry 是一个快速、可扩展的智能合约开发工具集,专注于高性能和易用性。它采用了 Rust 语言开发,并且针对速度和效率进行了优化。
ERC20 和 ERC777 是两种不同的以太坊代币标准。它们都有自己的特性和应用场景,下面是它们之间的主要区别:
totalSupply()
, balanceOf(address)
,
transfer(address, uint256)
,
transferFrom(address, address, uint256)
,
approve(address, uint256)
和
allowance(address, address)
,这些接口为代币的转账和授权操作提供了标准化的方法。send
和
receive
钩子,允许代币在转移时触发合约内的逻辑。通过这些钩子,开发者可以在代币转账时执行额外的逻辑。ERC721和ERC1155是以太坊上用于创建和管理非同质化代币(NFT)的两种主要标准。它们各自有不同的特性和应用场景。以下是它们之间的主要区别和特性:
balanceOf
,
ownerOf
, transferFrom
, approve
,
setApprovalForAll
和safeTransferFrom
,用于管理代币的所有权和转移。Transfer
和Approval
事件,用于通知代币转移和授权操作。balanceOf
,
balanceOfBatch
, safeTransferFrom
,
safeBatchTransferFrom
,
setApprovalForAll
和isApprovedForAll
,用于管理和转移代币。TransferSingle
和TransferBatch
事件,用于通知单个和批量代币转移操作。OpenZeppelin Wizard : https://wizard.openzeppelin.com/