Last active
July 24, 2022 15:53
-
-
Save loadchange/6e56a4f6fa54d359aa78b15097282064 to your computer and use it in GitHub Desktop.
contracts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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也会退还给销售者 | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 英式拍卖 | |
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 多签钱包 | |
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