1、函数签名
作用:了解虚拟机是如何找到一个函数的.
呼叫一个函数的数据由两部分组成,第一部分:函数签名;第二部分:参数。
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
contract functionSelector{
function getSelector(string calldata _func) external pure returns (bytes4){
return bytes4(keccak256(bytes(_func)));
}
}
contract Receiver{
event Log(bytes data);
function transfer(address _to, uint _amount) external{
//获取调用时的发送的数据
emit Log(msg.data);
}
//0xa9059cbb 函数名称
//00000000000000000000000078731d3ca6b7e34ac0f824c42a7cc18a495cabab 地址
//0000000000000000000000000000000000000000000000000000000000000001 参数
}
(1)调用transfer函数后,返回数据,包括函数名称、地址和参数,这里参数使用的1.
(2)函数名称是如何产生的?
0xa9059cbb产生方式:return bytes4(keccak256(bytes(_func)))
这里_func为"transfer(address,uint256)"
2、荷兰式拍卖
有一个倒计时,随着时间流逝,拍卖品价格会逐渐降低。直到有人举牌。
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.7;
interface IERC721{
function transferFrom(
address _from,
address _to,
uint _nftID
)external;
}
contract DutchAuction {
uint private constant DURATION = 7 days;
IERC721 public immutable nft;
uint public immutable nftID;
address payable public immutable seller; //销售者
uint public immutable startingPrice; //起拍价
uint public immutable startAt;//起拍时间
uint public immutable expiresAt;//流拍时间
uint public immutable discountRate; //折扣率,每秒都在起拍价格上减去一定数量
constructor(
uint _startingPrice,
uint _discountRate,
address _nft,
uint _nftID
){
seller = payable(msg.sender); //销售成功时将主币发送给出售者
startingPrice = _startingPrice;
discountRate = _discountRate;
startAt = block.timestamp;
expiresAt = block.timestamp + DURATION;
//要求拍卖结束后价格不为负,折扣率*拍卖时间>起拍价格
require(
_startingPrice >= _discountRate * DURATION,"discountRate*time should bigger then startingPrice"
);
nft = IERC721(_nft);
nftID = _nftID;
}
//获取拍卖价格
function getPrice() public view returns (uint){
uint timeElapsed = block.timestamp - startAt;
uint discount = discountRate * timeElapsed;
return startingPrice - discount;
}
//购买,通过传入主币进行购买
function buy() external payable{
//购买时间应当在拍卖期内
require(block.timestamp < expiresAt,"auction expired");
uint price = getPrice();
//要求支付的主币数量大于等于拍卖价格
require(msg.value >= price,"ETH < price");
//将NFT从拍卖者账户发送到购买者的账户中
nft.transferFrom(seller, msg.sender, nftID);
//为购买者找零
uint refund = msg.value - price;
if(refund > 0){
payable(msg.sender).transfer(refund);
}
//将主币发送到出售者账户中、销毁拍卖合约
selfdestruct(seller);
}
}
(1)部署ERC721
(2)铸造NFT
(3)部署拍卖合约
(4)批准NFT交易
(5)购买NFT
(6)检查NFT所有者
3、众筹
包括:设定众筹目标时间、目标金额。投资者可资助众筹项目,投资者可撤回代币。众筹项目创建者能够获取众筹代币
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
import "./ERC20.sol";
contract CrowdFund{
struct Campaign{
address creator;
uint goal;
uint pledge;
//时间戳采用uint32格式
uint32 startAt;
uint32 endAt;
//筹款是否被创建者领取过
bool claimed;
}
IERC20 public immutable token;
uint public count;
mapping(uint => Campaign) public campaigns;
mapping(uint => mapping(address => uint)) public pledgedAmount;
constructor(address _token){
token = IERC20(_token);
}
event launch(uint count,address indexed addr,uint goal,uint32 startAt,uint32 endAt);
event Cancel(uint id);
event Pledge(uint id,address indexed addr,uint amount);
event unPledge(uint id,address indexed addr,uint amount);
event Claimed(uint id);
event reFund(uint id,address indexed addr);
function Launch(
uint _goal,
uint32 _startAt,
uint32 _endAt
)external{
require(_startAt >= block.timestamp,"start at < now");
require(_endAt >= _startAt,"end at<start at");
//方法:days能够转换为秒数,block.timestamp是指合约建立时的时间戳
require(_endAt<=block.timestamp + 90 days,"end at>max duration");
count += 1;
//构建一个众筹结构体,并添加至所有结构体中
campaigns[count] = Campaign({
creator:msg.sender,
goal: _goal,
pledge:0,
startAt: _startAt,
endAt: _endAt,
claimed:false
});
emit launch(count,msg.sender,_goal,_startAt,_endAt);
}
//取消众筹, 如果众筹已经开始,那么该众筹无法取消
function cancel(uint _id) external {
Campaign storage campaign = campaigns[_id];
require(msg.sender == campaign.creator);
require(block.timestamp < campaign.startAt);
delete campaigns[_id];
emit Cancel(_id);
}
//筹集资金
function pledge(uint _id,uint _amount) external {
Campaign storage campaign = campaigns[_id];
require(block.timestamp >= campaign.startAt,"not started");
require(block.timestamp <= campaign.endAt,"has ended");
campaign.pledge += _amount;
pledgedAmount[_id][msg.sender] += _amount;
token.transferFrom(msg.sender,address(this), _amount);
emit Pledge(_id, msg.sender, _amount);
}
//撤回资金:众筹结束之前可以反悔
function unpledge(uint _id,uint _amount) external{
Campaign storage campaign = campaigns[_id];
require(block.timestamp <= campaign.endAt,"has ended");
require(_amount<=pledgedAmount[_id][msg.sender]);
campaign.pledge -= _amount;
pledgedAmount[_id][msg.sender] -= _amount;
token.transfer(msg.sender, _amount);
emit unPledge(_id,msg.sender,_amount);
}
//达到目标领取资金
function claim(uint _id) external{
Campaign storage campaign = campaigns[_id];
require(msg.sender == campaign.creator,"not creater");
require(block.timestamp > campaign.endAt,"not ended");
require(campaign.pledge >= campaign.goal,"not arrive goal");
require(!campaign.claimed,"claimed");
campaign.claimed = true;
token.transfer(msg.sender,campaign.pledge);
emit Claimed(_id);
}
//没达到目标,付款用户撤回资金
function refund(uint _id) external{
Campaign storage campaign = campaigns[_id];
require(block.timestamp > campaign.endAt,"not ended");
require(campaign.pledge < campaign.goal,"pledged < goal");
uint bal = pledgedAmount[_id][msg.sender];
pledgedAmount[_id][msg.sender] = 0;
token.transfer(msg.sender,bal);
emit reFund(_id,msg.sender);
}
}
4、create2方法:用于预测新合约的地址
用于生产新合约,并预测新合约的地址,可用于验证合约。
用工厂合约的地址+salt计算新合约的地址,因此新合约的地址能够预测出来。
new contract后可加{salt},salt用于为合约部署地址确定变量。
新生成合约的地址依靠四部分生成:
固定值:0xff
父合约地址:address(this)
参数:salt
合约机器码:code
在父合约地址、参数、合约机器码不变的情况下,所生成的子合约地址不变。
合约机器码code的生成方式为
使用 type(DeployWithCreate2).creationCode 生成合约代码的机器码。
使用abi.encode(_owner)对某地址打包。
使用abi.encodePacked()对合约机器码和打包的地址进行打包,生成code。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
contract DeployWithCreate2{
address public owner;
constructor(address _owner){
owner = _owner;
}
}
contract Create2Factory{
event Deploy(address addr);
//使用DeployWithCreate2构建合约
function deploy(uint _salt) external {
//new contract后可加{salt},salt用于为合约部署地址确定变量
DeployWithCreate2 _contract = new DeployWithCreate2{
salt: bytes32(_salt)
}(msg.sender);
emit Deploy(address(_contract));
}
function getAddress(bytes memory bytecode,uint _salt)
public
view
returns(address)
{
//keccak256用于将打包数据生成为哈希值
bytes32 hash = keccak256(
//abi.encodePacked用于将一定数据打包
abi.encodePacked(
//固定字符串、本合约的地址、salt、要合约的机器码打包后进行哈希值运算得到的结果就是部署合约的地址
bytes1(0xff),address(this),_salt,keccak256(bytecode)
)
);
return address(uint160(uint(hash)));
}
function getBytecode(address _owner) public pure returns (bytes memory){
//type(Contract).creationCode用于生成合约代码的机器码
bytes memory bytecode = type(DeployWithCreate2).creationCode;
//合约代码的机器码和合约的发布者地址打包后返回的机器码为用于生成合约地址的机器码
return abi.encodePacked(bytecode,abi.encode(_owner));
}
}