1、SimpleStorage|一些基础语法
// SPDX-License-Identifier: MIT
pragma solidity 0.8.8; // 定义使用的solidity版本
// EVM:Ethereum Virtual Machine:向以太坊区块链上部署的一个标准
contract SimpleStorage { // 定义一个合约,类似于Java中的class关键字
// --------------------------------基本数据机构、函数等--------------------------------
// Solidity中的四种基础数据类型:bool、uint、int、address、bytes:另一种底层类型
bool hasFavoriteNumber = false;
// 表示一个256位的无符号整数,不带修饰符时,默认是private,
//public:合约内、外部都可见,会给该变量添加一个getter函数
// private:合约内部可见,合约外部不可见
// external:合约外部可见
// internal:表示该合约以及继承他的合约可见
uint256 public favoriteNumber = 5;
string favoriteInText = "Five";
int256 favoriteInt = 5;
address myAddress = 0x25A338f01d0Caf3953C9cBa5779E98B39dCf9630;
bytes32 favoriteBytes = "cat"; // string 可以自动转化为bytes
// 定义一个方法,表示这个方法可以被继承
function store(uint256 _favoritNumber) public virtual {
favoriteNumber = _favoritNumber;
retrieve();
}
// view:意味着只会读取这个合约的状态,不会修改状态
// pure:意味着既不能修改也不能读取合约的状态
function retrieve() public view returns(uint256) {
return favoriteNumber;
}
function add() public pure returns(uint256) {
return (1 + 1);
}
// --------------------------------结构体、数组等--------------------------------
struct People {
uint8 age;
string name;
uint256 favoriteNumber;
}
People public zhangsan = People({age: 25, name: "zhangsan", favoriteNumber: 255});
// 数组,这里我们定义的是一个动态数组,如果[3]表示最多存放3个对象
People[] public personList;
/*
* storage: 用于长期存储合约的状态变量,数据永久保存在区块链上,读取和写入都需要消耗gas。
* memory: 用于函数执行期间的临时数据存储,数据在函数调用结束后被自动清理,使用成本低。
* calldata: 用于传递函数调用参数,数据只读且仅在函数调用期间存在,使用它可以节省gas成本。
*/
// 定义一个函数,向personList中添加元素
function addPerson(uint8 _age, string memory _name, uint256 _favoriteNumber) public {
personList.push(People(_age, _name, _favoriteNumber));
}
// --------------------------------Mappings等--------------------------------
mapping(string => People) public nameToPeopleMap;
// 定义一个方法,向map中存入数据
function addToMap(uint8 _age, string memory _name, uint256 _favoriteNumber) public returns(People memory) {
People memory people = People(_age, _name, _favoriteNumber);
nameToPeopleMap[_name] = people;
return nameToPeopleMap[_name];
}
}
2、StorageFactory|使用合约部署合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
// 引入我们自己写的第一个简单合约
import "./SimpleStorage.sol";
// 定义一个合约生产工厂
contract StorageFactory {
SimpleStorage[] public simpleStorageArray;
function createSimpleStorageContract() public {
// new关键字的作用
// 创建新的智能合约实例
SimpleStorage simpleStorage = new SimpleStorage();
simpleStorageArray.push(simpleStorage);
}
function sfStore(uint256 _simpleStorageIndex, uint256 _simpleStorageNumber) public {
// Address
// ABI
// SimpleStorage(address(simpleStorageArray[_simpleStorageIndex])).store(_simpleStorageNumber);
simpleStorageArray[_simpleStorageIndex].store(_simpleStorageNumber);
}
function sfGet(uint256 _simpleStorageIndex) public view returns (uint256) {
// return SimpleStorage(address(simpleStorageArray[_simpleStorageIndex])).retrieve();
return simpleStorageArray[_simpleStorageIndex].retrieve();
}
}
3、ExtraStorage|合约的继承
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 合约的继承,先导入需要继承的合约
import "./SimpleStorage.sol";
contract ExtraStorage is SimpleStorage {
function store(uint256 _favoriteNumber) public override {
favoriteNumber = _favoriteNumber + 5;
}
}
4、PriceConverter|引入其他库以及自定义库
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
// 通过npm从github中获取喂价服务合约
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
library PriceConverter {
// 该方法获取ETH/USD汇率
function getPrice() public view returns(uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306);
// 对返回的元组进行解构
(
/* uint80 roundID */,
int price,
/*uint startedAt*/,
/*uint timeStamp*/,
/*uint80 answeredInRound*/
) = priceFeed.latestRoundData();
// 1e18是ETH的最小单位,1e17是1e18的1/10
require(price >= 1e17, "price too low!");
return uint256(price * 1e10);
}
// 传入eth数量,获取对应的USDT数量
function getConversionRate(uint256 _ethAmount) internal view returns(uint256) {
// 获取1eth兑换为usdt的价格
uint256 usdtPrice = getPrice();
return (usdtPrice * _ethAmount) / 1e18;
}
}
5、FundMe|给合约转账以及从合约取现的功能
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "./PriceConverter.sol";
// 节约gas的一些小技巧
// 1.尽量使用constant、immutable来修饰常量和不可修改的量,
// 2.定义自定义错误,来代替require,更加节约gas
error NotOwnerError();
error CallFailed();
contract FundMe {
// 这样定义后,PriceConverter库就可以使用uint256来调用了
using PriceConverter for uint256;
uint256 public number;
// 定义一个最小发送的usdt数量,假设是1个
// constant:定义一个常量
uint256 internal constant MINIMUM_USDT = 1 * 1e18;
// 定义一个数组,记录所有发送代币的地址
address[] public funders;
// 定义一个mapping记录每个地址发送的代币数量
mapping(address => uint256) public addressToAmountFouded;
// 定义一个owner,表示合约的拥有者
// 因为这个我们只需要在constructor中设置一次,所以我们可以定义为不可变的
// immutable:只能设置一次
address public immutable i_owner;
// 创建一个constructor,在创建合约的时候自动执行这个方法,并且只能执行一次
constructor() {
// 这里的owner就是合约创建者,也就是msg.sender
i_owner = msg.sender;
}
// 这个方法用来让大家捐款
function fund() public payable {
// 调用这个方法,至少需要向合约地址发送价值50个usdt的eth,否则报错dont send enough
// 什么是revert,如果require这个条件没达到,number=5这个操作也会被撤销
number = 5;
require(
msg.value.getConversionRate() >= MINIMUM_USDT,
"dont send enough!"
);
// 记录发送代币的地址
funders.push(msg.sender);
// 记录发送代币的数量
addressToAmountFouded[msg.sender] += msg.value;
}
// 获取费率
function getEthToUsdtPrice() public view {
PriceConverter.getPrice();
}
// 这个方法用来将合约中的代币转到创建合约的账户,这里用到了修饰器
function withdraw() public onlyOwner {
// 首先使用for循环将addressToAmountFouded中的数据清零
for (uint256 i = 0; i < funders.length; i++) {
address funder = funders[i];
addressToAmountFouded[funder] = 0;
}
// 将数组置为空
funders = new address[](0);
// 这里就是区块链上真实的转账,有以下三种方式
// 1、transfer:只有payable address才能进行转账操作
// payable(msg.sender).transfer(address(this).balance);
// 2、send:与上面的类似
// bool sendSuccess = payable(msg.sender).send(address(this).balance);
// require(sendSuccess, "send error");
// 3、call,call可以调用任何函数,这里我们以转账为例,如果函数有返回值就存储到第二个参数bytes dataReturned中
(bool callSuccess, ) = payable(msg.sender).call{
value: address(this).balance
}("");
// require(callSuccess, "call error");
if (!callSuccess) {
revert CallFailed();
}
}
// 定义修饰器,修饰器在函数执行前调用这里面的函数
modifier onlyOwner() {
// 这个修饰器表示只有合约的拥有者才能调用
// require(i_owner == msg.sender, "you isn't owner");
if (i_owner != msg.sender) {
revert NotOwnerError();
}
// _表示余下的代码,也就是被我们onlyOwner修饰的函数
_;
}
// receive函数是solidity中自带的函数,他的触发条件是msg.data中没有数据,并且定义了receive
// 为了防止有人不调用fund,就向我们的合约中转账,无法记录转账人信息的问题,我们可以用receive记录
receive() external payable {
fund();
}
// fallback函数:msg.data中有数据但是没找到具体的函数时就触发、或者当msg.data没有数据且没有定义receive时触发
fallback() external payable {
fund();
}
}