# Solidity 開發實作講義 ### 多簽錢包 程式碼 [gist](https://gist.github.com/madeinfree/d345abdc9ee50308d1ff5c57b71dca0b) ### Rug Pull Token ```solidity! // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract RugPullToken is ERC20 { constructor(uint256 initialSupply) ERC20("Rug Pull", "RP") {} function mint(uint256 _amount) public { _mint(msg.sender, _amount); } } ``` ### ERC721 KryptoToy NFT ```solidity! // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract KryptoToy is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; constructor() ERC721("KryptoToy", "KT") {} function mint(address to) public { uint256 tokenId = _tokenIds.current(); _mint(to, tokenId); _tokenIds.increment(); } } ``` ### 擴充小抄 ```solidity! address private _owner; _owner = msg.sender; modifier onlyOwner { require(msg.sender == _owner, "not owner"); _; } function ownerMint() public onlyOwner { uint256 tokenId = _tokenIds.current(); _mint(msg.sender, tokenId); _tokenIds.increment(); } function burn(uint256 _tokenId) public { _burn(_tokenId); } // -----------------------------------// uint256 mintPrice = 0.03 ether; ``` ### 荷蘭拍 [時間轉換器](https://www.epochconverter.com/) ```solidity! struct Auction { uint256 startTime; uint256 timeStep; uint256 startPrice; uint256 endPrice; uint256 priceStep; uint256 stepNumber; } Auction public auction; // "50000000000000000" -> 0.05 ether function getAuctionPrice() public view returns (uint256) { Auction memory currentAuction = auction; if (block.timestamp < currentAuction.startTime) { return currentAuction.startPrice; } uint256 step = (block.timestamp - currentAuction.startTime) / currentAuction.timeStep; if (step > currentAuction.stepNumber) { step = currentAuction.stepNumber; } return currentAuction.startPrice > step * currentAuction.priceStep ? currentAuction.startPrice - step * currentAuction.priceStep : currentAuction.endPrice; } function setAuction( uint256 _startTime, uint256 _timeStep, uint256 _startPrice, uint256 _endPrice, uint256 _priceStep, uint256 _stepNumber ) public onlyOwner { auction.startTime = _startTime; // 開始時間 auction.timeStep = _timeStep; // 5 多久扣一次 auction.startPrice = _startPrice; // 50000000000000000 起始金額 auction.endPrice = _endPrice; // 10000000000000000 最後金額 auction.priceStep = _priceStep; // 10000000000000000 每次扣除多少金額 auction.stepNumber = _stepNumber; // 5 幾個階段 } function auctionMint() external payable { require(msg.value >= getAuctionPrice(), "not enough value"); uint256 tokenId = _tokenIds.current(); _mint(msg.sender, tokenId); _tokenIds.increment(); } ``` ### 白名單機制 ```solidity! address[] public whitelistArray; mapping(address => bool) public whitelistMapping; /** ["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db"] */ for (uint256 i; i < whitelist.length; i++) { whitelistArray.push(whitelist[i]); whitelistMapping[whitelist[i]] = true; } function inWhitelistArray() private view returns (bool) { for (uint256 i; i < whitelistArray.length; i++) { if (whitelistArray[i] == msg.sender) { return true; } } return false; } function whitelistMintFromArray() external { require(inWhitelistArray(), "not in whitelist"); uint256 tokenId = _tokenIds.current(); _mint(msg.sender, tokenId); _tokenIds.increment(); } function whitelistMintFromMapping() external { require(whitelistMapping[msg.sender], "not in whitelist"); uint256 tokenId = _tokenIds.current(); _mint(msg.sender, tokenId); _tokenIds.increment(); } ``` ### Merkle Tree ![](https://i.imgur.com/kmqbVkk.png) ```bash! npm install merkletreejs ethers ``` ```javascript! const { MerkleTree } = require("merkletreejs"); const { ethers } = require("ethers"); const leaves = [ { address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" }, { address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" }, { address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" }, ].map((x) => ethers.utils.keccak256(ethers.utils.solidityPack(["address"], [x.address])) ); const tree = new MerkleTree(leaves, ethers.utils.keccak256, { sortPairs: true, }); const root = tree.getHexRoot(); const leaf = ethers.utils.keccak256( ethers.utils.solidityPack( ["address"], ["0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"] ) ); console.log(tree.getHexProof(leaf)); console.log(tree.verify(tree.getHexProof(leaf), leaf, root)); ``` ### Merkle Proof 合約小抄 ```solidity! import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; bytes32 merkleRoot; constructor(address[] memory whitelist, bytes32 _root) merkleRoot = _root; function whitelistMintFromMerkleProof( bytes32[] memory _proof ) external { require(MerkleProof.verify(_proof, merkleRoot, keccak256(abi.encodePacked(msg.sender))), "not in merkle proof whitelist"); uint256 tokenId = _tokenIds.current(); _mint(msg.sender, tokenId); _tokenIds.increment(); } // root 0x78d3daf2836a57661b587d5f2723b86785c8c3c007b63606ed332cb4a69da702 // proof ["0xe9707d0e6171f728f7473c24cc0432a9b07eaaf1efed6a137a4a8c12c79552d9","0x343750465941b29921f50a28e0e43050e5e1c2611a3ea8d7fe1001090d5e1436"] ``` ### signature 簽章機制 ```javascript! const { keccak256, toBuffer, ecsign, bufferToHex } = require('ethereumjs-util') const { ethers } = require('ethers') const createVoucher = function (hash, signerPvtKey) { const privateKey = new Buffer.from(signerPvtKey, 'hex') return ecsign(hash, privateKey) } const generateHashBuffer = function (typesArray, valueArray) { return keccak256( toBuffer(ethers.utils.defaultAbiCoder.encode(typesArray, valueArray)) ) } const serializeVoucher = function (voucher) { return { r: bufferToHex(voucher.r), s: bufferToHex(voucher.s), v: voucher.v, } } const serializeVoucherForInput = function (voucher) { return [bufferToHex(voucher.r), bufferToHex(voucher.s), voucher.v] } const hash = generateHashBuffer( ['address', 'uint256'], ['0x5B38Da6a701c568545dCfcB03FcB875f56beddC4', 0] ) voucher = createVoucher( hash, Buffer.from( '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'.substring( 2, 66 ), 'hex' ) ) console.log(serializeVoucherForInput(voucher)) ``` ```solidity! struct Voucher { bytes32 r; bytes32 s; uint8 v; } address _signer = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; function signatureMint( uint256 tokenId, Voucher memory voucher ) public { bytes32 digest = keccak256(abi.encode(msg.sender, tokenId)); require(_isVerifiedVoucher(digest, voucher), "signature failed.."); _mint(msg.sender, tokenId); } function _isVerifiedVoucher(bytes32 digest, Voucher memory voucher) internal view returns (bool) { address signer = ecrecover(digest, voucher.v, voucher.r, voucher.s); require(signer != address(0), "ECDSA: invalid signature"); return signer == _signer; } ``` ### 鏈上亂數 [Chainlink Goerli](https://vrf.chain.link/goerli) `區塊資料 block.timestamp` ```solidity! // SPDX-License-Identifier: MIT pragma solidity 0.8.16; contract Random { function generateRandom() private view returns (uint256) { return uint256( keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)) ); } function getAnswer() public view returns (uint256) { return generateRandom() % 1000; } } ``` `Chainlink VRF` ```solidity= // SPDX-License-Identifier: MIT pragma solidity 0.8.16; import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; contract Random is VRFConsumerBaseV2 { VRFCoordinatorV2Interface COORDINATOR; uint256 public random; uint64 s_subscriptionId; address vrfCoordinator = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D; bytes32 s_keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15; uint16 requestConfirmations = 3; uint32 callbackGasLimit = 250000; uint32 numWords = 1; uint256[] public requestIds; constructor(uint64 subscriptionId) VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); s_subscriptionId = subscriptionId; } function generateRandom() private view returns (uint256) { return uint256( keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)) ); } function generateRandomFromChainlink() public returns (uint256 requestId) { requestId = COORDINATOR.requestRandomWords( s_keyHash, s_subscriptionId, requestConfirmations, callbackGasLimit, numWords ); } function getAnswer() public view returns (uint256) { require(random != 0, "random not initialize."); return random % 1000; } function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { requestIds.push(requestId); random = randomWords[0]; } } ```
{"title":"Solidity 101 開發實作課程講義","description":"程式碼 gist","contributors":"[{\"id\":\"9ef7cf5b-bb4a-4e8b-a4ec-3decd4deef26\",\"add\":9558,\"del\":0}]"}
Expand menu