Skip to content

Instantly share code, notes, and snippets.

@loadchange
Last active July 24, 2022 15:53
Show Gist options
  • Save loadchange/6e56a4f6fa54d359aa78b15097282064 to your computer and use it in GitHub Desktop.
Save loadchange/6e56a4f6fa54d359aa78b15097282064 to your computer and use it in GitHub Desktop.
contracts
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.10;
contract DeployWithCreate2 {
address public owner;
constructor(address _owner) {
owner = _owner;
}
}
contract Create2Factory {
event Deploy(address addr);
// 部署合约
function deploy(uint _salt) external {
DeployWithCreate2 _contract = new DeployWithCreate2{
salt: bytes32(_salt)
}(msg.sender);
emit Deploy(address(_contract));
}
// 根据机器码和可变言提前获取合约地址
function getAddress(bytes memory bytecode, uint _salt) public view returns (address) {
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode)));
return address(uint160(uint(hash)));
}
// 获取被部署合约机器码
function getBytecode(address _owner) public pure returns(bytes memory){
bytes memory bytecode = type(DeployWithCreate2).creationCode;
return abi.encodePacked(bytecode, abi.encode(_owner));
}
}
// -----------------------------------------------------------------------------------------------------
// 多重调用
contract TestMultiCall {
function func1() external view returns(uint, uint) {
return (1, block.timestamp);
}
function func2() external view returns(uint, uint) {
return (2, block.timestamp);
}
function getData1() external pure returns(bytes memory) {
// abi.encodeWithSignature("func1()");
return abi.encodeWithSelector(this.func1.selector);
}
function getData2() external pure returns(bytes memory) {
// abi.encodeWithSignature("func1()");
return abi.encodeWithSelector(this.func2.selector);
}
}
contract MultiCall {
function multiCall(address[] calldata targets, bytes[] calldata data) external view returns(bytes[] memory){
require(targets.length == data.length,"target length !== data length");
bytes[] memory results = new bytes[](data.length);
for( uint i; i < targets.length; i++) {
(bool success, bytes memory result) = targets[i].staticcall(data[i]);
require(success, "call failed");
results[i] = result;
}
return results;
}
}
// 多重委托调用
contract MultiDelegateCall {
error DelegateCallFailed();
function multiDelegatecall(bytes[] calldata data) external payable returns(bytes[] memory results) {
results = new bytes[](data.length);
for(uint i; i < data.length; i++) {
(bool ok, bytes memory res) = address(this).delegatecall(data[i]);
if (!ok) {
revert DelegateCallFailed();
}
results[i] = res;
}
}
}
contract TestMultiDelegateCall is MultiDelegateCall {
event Log(address caller, string func, uint i);
function func1(uint x, uint y) external {
emit Log(msg.sender, "func1", x + y);
}
function func2() external returns(uint) {
emit Log(msg.sender, "func2", 2);
return 111;
}
mapping(address => uint) public balanceOf;
// 多重委托调用有可能会产生漏洞
function mint() external payable {
balanceOf[msg.sender] += msg.value; // 使用多重委托调用的时候不要重复 使用主币数量,或者让多重委托调用的函数不能接收主币
}
}
contract Helper {
function getFunc1Data(uint x,uint y) external pure returns(bytes memory) {
return abi.encodeWithSelector(TestMultiDelegateCall.func1.selector, x, y);
}
function getFunc2Data() external pure returns(bytes memory) {
return abi.encodeWithSelector(TestMultiDelegateCall.func2.selector);
}
function getMintData() external pure returns(bytes memory) {
return abi.encodeWithSelector(TestMultiDelegateCall.mint.selector);
}
}
// -----------------------------------------------------------
// abi解码
contract AbiDecode {
struct MyStruct {
string name;
uint[2] nums;
}
function encode(uint x, address addr, uint[] calldata arr, MyStruct calldata myStruct) external pure returns(bytes memory) {
return abi.encode(x, addr, arr, myStruct);
}
function decode(bytes calldata data ) external pure returns(uint x, address addr, uint[] memory arr, MyStruct memory myStruct) { // 返回值中有数组或者结构体 需要声明存储位置
( x, addr, arr, myStruct) = abi.decode(data,(uint, address, uint[],MyStruct ));
}
}
// 节约gas
// 50908
// 1,优先使用calldata 49163
// 2,加载状态变量到内存变量 48952
// 3,多个条件判断使用短路方式 48634
// 4,在循环中使用++1,而不是i+=1,i++ 48226
// 5,数组长度缓存到内存 48191
// 6,缓存多次使用的数组元素到内存 48029
contract GasGolf {
uint public total;
function sumIfEvenAndLessThan99(uint[] calldata nums) external {
uint _total = total;
uint len = nums.length;
for (uint i = 0; i < len; ++i) {
uint num = nums[i];
if (num % 2 == 0 && num < 99) {
_total += num;
}
}
total = _total;
}
}
// 时间锁合约
// 常应用在DApp Defi应用上,保护管理员权限
contract TimeLock {
address public owner;
mapping(bytes32 => bool) public queued;
error NotOwnerError();
error AlreadyQueuedError(bytes32 exId);
error NotQueuedError(bytes32 exId);
error TimestampNotInRangeError(uint blockTimestamp, uint timestamp);
error TimestampNotPassedError(uint blockTimestamp, uint timestamp);
error TimestampExpiredError(uint blockTimestamp, uint timestamp);
error TxFailedError();
uint public constant MIN_DELAY = 10;
uint public constant MAX_DELAY = 1000;
uint public constant GRACE_PERIOD = 1000;
event Queue(bytes32 indexed exId, address indexed _target,uint _value, string _func, bytes _data,uint _timestamp);
event Execute(bytes32 indexed exId, address indexed _target,uint _value, string _func, bytes _data,uint _timestamp);
event Cancel(bytes32 indexed exId);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
if(msg.sender != owner) {
revert NotOwnerError();
}
_;
}
function getTxId(address _target,uint _value, string calldata _func, bytes calldata _data,uint _timestamp) public pure returns(bytes32 exId) {
return keccak256(
abi.encode(_target, _value, _func, _data, _timestamp)
);
}
function queue(address _target,uint _value, string calldata _func, bytes calldata _data,uint _timestamp) external onlyOwner {
bytes32 txId = getTxId(_target, _value, _func, _data, _timestamp);
if(queued[txId]) {
revert AlreadyQueuedError(txId);
}
if (
_timestamp < block.timestamp + MIN_DELAY ||
_timestamp > block.timestamp + MAX_DELAY
) {
revert TimestampNotInRangeError(block.timestamp, _timestamp);
}
queued[txId] = true;
emit Queue(txId, _target, _value, _func, _data, _timestamp);
}
receive() external payable {}
function execute(address _target,uint _value, string calldata _func, bytes calldata _data,uint _timestamp) external payable onlyOwner returns(bytes memory) {
bytes32 txId = getTxId(_target, _value, _func, _data, _timestamp);
if (!queued[txId]) {
revert NotQueuedError(txId);
}
if (block.timestamp < _timestamp) {
revert TimestampNotPassedError(block.timestamp, _timestamp);
}
if (block.timestamp > _timestamp + GRACE_PERIOD) {
revert TimestampExpiredError(block.timestamp, _timestamp + GRACE_PERIOD);
}
queued[txId] = false;
bytes memory data;
if (bytes(_func).length > 0) {
data = abi.encodePacked(bytes4(keccak256(bytes(_func))), _data);
} else {
data = _data;
}
(bool ok, bytes memory res) = _target.call{value: _value}(data);
if(!ok) {
revert TxFailedError();
}
emit Execute(txId, _target, _value, _func, _data, _timestamp);
return res;
}
function cancel(bytes32 _txId) external onlyOwner {
if(!queued[_txId]) {
revert NotQueuedError(_txId);
}
queued[_txId] = false;
emit Cancel(_txId);
}
}
contract TestTimeLock {
address public timeLock;
constructor(address _timeLock) {
timeLock = _timeLock;
}
function test() external {
require(msg.sender == timeLock);
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.10;
import "./ERC20.sol";
// 众筹合约
contract CrowdFund {
event Launch(uint id, address indexed creator, uint goal, uint32 startAt, uint32 endAt);
event Cancel(uint id);
event Pledge(uint indexed id, address indexed caller, uint amount);
event Unpledge(uint indexed id, address indexed caller, uint amount);
event Claim(uint id);
event Refund(uint indexed id,address indexed caller, uint amount);
struct Campaign {
address creator;
uint goal;
uint pledged; // 已经参与的token数量
uint32 startAt;
uint32 endAt;
bool claimed; // 筹款金额是否被取走
}
IERC20 public immutable token;
uint public count; // 筹款活动计数器(筹款活动ID)
mapping(uint => Campaign) public campaigns; // 筹款活动ID 映射
mapping(uint => mapping(address => uint)) public pledgedAmount; // 参与筹款活动映射; 活动ID => (用户地址 => 投资金额)
constructor(address _token) {
token = IERC20(_token);
}
// 创建众筹 众筹目标 开始时间 、结束时间 目标
function launch(uint _goal, uint32 _startAt, uint32 _endAt) external {
require(_startAt >= block.timestamp, "start at < now");
require(_endAt >= _startAt,"end at < start at");
require(_endAt <= block.timestamp + 90 days, "end at > max duration");
count += 1;
campaigns[count] = Campaign({
creator: msg.sender,
goal: _goal,
pledged: 0,
startAt: _startAt,
endAt: _endAt,
claimed: false
});
emit Launch(count, msg.sender, _goal, _startAt, _endAt);
}
// 取消众筹
function cancel(uint _id) external {
Campaign memory campaign = campaigns[_id];
require(msg.sender == campaign.creator, "not 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,"ended");
campaign.pledged += _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,"ended");
campaign.pledged -= _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 creator");
require(block.timestamp > campaign.endAt,"not ended");
require(campaign.pledged >= campaign.goal,"pledgged < goal");
require(!campaign.claimed,"claimed");
campaign.claimed = true;
token.transfer(msg.sender, campaign.pledged);
emit Claim(_id);
}
// 失败后退回
function refund(uint _id) external {
Campaign storage campaign = campaigns[_id];
require(block.timestamp > campaign.endAt,"not ended");
require(campaign.pledged < campaign.goal,"pledgged < goal");
uint bal = pledgedAmount[_id][msg.sender];
pledgedAmount[_id][msg.sender] = 0;
token.transfer(msg.sender, bal);
emit Refund(_id, msg.sender, bal);
}
}
interface IERC721 {
function transferFrom(
address _form,
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 staringPrice; // 起拍价
uint public immutable startAt; // 开始事件
uint public immutable expiresAt;
uint public immutable discountRate; // 没秒折扣率
constructor(uint _staringPrice, uint _discountRate, address _nft, uint _nftId) {
seller = payable(msg.sender);
staringPrice = _staringPrice;
discountRate = _discountRate;
startAt = block.timestamp;
expiresAt = startAt + DURATION;
require(_staringPrice >= _discountRate * DURATION, "starting price < discount");
nft = IERC721(_nft);
nftId = _nftId;
}
function getPrice() public view returns(uint) {
uint timeElapsed = block.timestamp - startAt;
uint discount = discountRate * timeElapsed;
return staringPrice - discount;
}
function buy() external payable {
require(block.timestamp < expiresAt, "auction expired"); // 拍卖没结束
uint price= getPrice();
require(msg.value >= price, "ETH < price");
nft.transferFrom(seller, msg.sender, nftId);
uint refund = msg.value - price; // 退款金额
if(refund >0){
payable(msg.sender).transfer(refund);
}
selfdestruct(seller); // 合约自毁,销毁后 合约内剩余的主币会退还给销售者,合约部署时占用的空间gas也会退还给销售者
}
}
// 英式拍卖
contract EnglishAuction {
event Start();
event Big(address indexed sender, uint amount);
event Withdraw(address indexed _add, uint amount);
event End(address _add, uint amount);
IERC721 public immutable nft;
uint public immutable nftId;
address payable public immutable seller;
uint32 public endAt;
bool public started;
bool public ended;
address public highesBidder;
uint public highestBig;
mapping(address => uint) public bids;
constructor(address _nft,uint _nftId, uint _startingBid) {
nft = IERC721(_nft);
nftId = _nftId;
seller = payable(msg.sender); // 拍卖者 就是铸造合约的人
highestBig = _startingBid;
}
function start() external {
require(msg.sender == seller, "not seller"); // 只有拍卖人能开始拍卖
require(!started, "started");
started = true;
endAt = uint32(block.timestamp + 600); // 结束时间是当前时间+600秒
nft.transferFrom(seller, address(this), nftId); // nft 转移到当前拍卖合约
emit Start();
}
function bid() external payable {
require(started, "not started");
require(block.timestamp < endAt, "ended");
require(msg.value > highestBig, "value <highestBig");
if (highesBidder != address(0)) {
bids[highesBidder] += highestBig;
}
highestBig = msg.value;
highesBidder = msg.sender;
emit Big(msg.sender, msg.value);
}
function withdraw() external {
uint bal = bids[msg.sender];
bids[msg.sender] = 0;
payable(msg.sender).transfer(bal);
emit Withdraw(msg.sender, bal);
}
function end() external {
require(started, "not started");
require(!ended, "ended");
require(block.timestamp >= endAt, "not ended");
ended = true;
if (highesBidder != address(0)) {
nft.transferFrom(address(this),highesBidder, nftId);
seller.transfer(highestBig);
} else {
nft.transferFrom(address(this), seller, nftId);
}
emit End(highesBidder, highestBig);
}
function getBalance() external returns(uint) {
return address(this).balance;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.10;
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
contract ERC20 is IERC20 {
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
string public name = "Test";
string public symbol = "TEST";
uint8 public decimals = 18;
// 铸币
function mint(uint amount) external {
balanceOf[msg.sender] += amount;
totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}
// 销毁
function burn(uint amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer( msg.sender, address(0), amount);
}
// 转账
function transfer(address _to, uint256 amount) external returns (bool success){
balanceOf[msg.sender] -= amount;
balanceOf[_to] += amount;
emit Transfer(msg.sender, _to, amount);
return true;
}
// 批准某账户可以赚我的钱
function approve(address _spender, uint256 _value) external returns (bool success) {
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success){
allowance[_from][msg.sender] -= _value;
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
return true;
}
}
// 多签钱包
contract MultiSigWallet {
// 存款事件
event Deposit(address indexed sender, uint amount);
// 提交交易申请事件
event Submit(uint indexed exId);
// 合约签名人批准事件
event Approve(address indexed owner, uint txId);
// 合约签名人撤销批准事件 交易没被执行之前都可以撤销
event Revoke(address indexed owner, uint txId);
// 执行事件
event Execute(uint indexed txId);
// 合约所有签名人
address[] public owners;
mapping(address => bool) public isOwner;
// 确认数 至少有多少签名人同意才可以
uint public required;
// 交易结构
struct Transaction {
address to;
uint value;
bytes data; // 调用合约使用
bool executed; // 交易是否执行
}
// 合约中全部交易
Transaction[] public transactions;
// 交易的id号 => (对应签名人的地址 => 是否批准)
mapping(uint => mapping(address => bool)) public approved;
constructor(address[] memory _owners, uint _required) {
require(_owners.length > 0, "owners required"); // 检查签名人列表
require(_required > 0 && _required <= _owners.length, "owners required"); // 确认数需大于 0 小于等于签名人数
for (uint i; i < _owners.length; i++) {
address owner = _owners[i];
require(owner != address(0), "invalid owner");
require(!isOwner[owner], "owner is not unique");
isOwner[owner] = true;
owners.push(owner);
}
}
modifier onlyOwner() {
require(isOwner[msg.sender], "not owner");
_;
}
// 交易是否存在
modifier txExists(uint _txId) {
// 交易ID 是根据数组索引产生的,如果校验的校验ID大于等于数组长度都是非法
require(_txId < transactions.length, "tx does not exist");
_;
}
// 当前签名人没批准过此交易
modifier notApproved(uint _txId) {
require(!approved[_txId][msg.sender], "tx already approved");
_;
}
// 检查当前交易还没有被执行过
modifier notExecuted(uint _txId) {
require(!transactions[_txId].executed, "tx already executed");
_;
}
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
function submit(address _to, uint _value, bytes calldata _data) external onlyOwner {
transactions.push(Transaction({
to: _to,
value: _value,
data: _data,
executed: false
}));
emit Submit(transactions.length-1);
}
function approve(uint _txId) external onlyOwner txExists(_txId) notApproved(_txId) notExecuted(_txId) {
approved[_txId][msg.sender] = true;
emit Approve(msg.sender, _txId);
}
function revoke(uint _txId) external onlyOwner txExists(_txId) notExecuted(_txId) {
require(approved[_txId][msg.sender], "tx not apprived");
approved[_txId][msg.sender] = false;
emit Revoke(msg.sender, _txId);
}
// 查询交易有多少签名人批准了
function _getApprovalCount(uint _txId) private view returns (uint count) {
for (uint i; i < owners.length; i++) {
if (approved[_txId][owners[i]]) {
count += 1;
}
}
}
function execute(uint _txId) external onlyOwner txExists(_txId) notExecuted(_txId) {
require(_getApprovalCount(_txId) >= required, "approvals < required");
// 注意需要 storage 修饰,因为要修改交易内的值
Transaction storage transaction = transactions[_txId];
transaction.executed = true;
// 开始低级调用
(bool success,) = transaction.to.call{value: transaction.value}(transaction.data);
require(success, "tx failed");
emit Execute(_txId);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment