pragma solidity ^0.8.0; interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); function transferFrom(address from, address to, uint256 amount) external returns (bool); } contract SimpleSwap { address public owner; IERC20 public token; uint256 public rate; constructor(address _tokenAddress, uint256 _rate) { owner = msg.sender; token = IERC20(_tokenAddress); rate = _rate; } receive() external payable { uint256 tokenAmount = msg.value * rate; require(token.transfer(msg.sender, tokenAmount), "Token transfer failed"); } function swapTokenForETH(uint256 tokenAmount) external { uint256 ethAmount = tokenAmount / rate; require(address(this).balance >= ethAmount, "Not enough ETH in contract"); require(token.transferFrom(msg.sender, address(this), tokenAmount), "Token transfer failed"); (bool success, ) = msg.sender.call{value: ethAmount}(""); require(success, "ETH transfer failed"); } function withdrawETH() external { require(msg.sender == owner, "Not owner"); payable(owner).transfer(address(this).balance); } function withdrawTokens() external { require(msg.sender == owner, "Not owner"); uint256 balance = tokenBalance(); require(token.transfer(owner, balance), "Token withdrawal failed"); } function tokenBalance() public view returns (uint256) { return token.balanceOf(address(this)); } }
Category: Ethereum
【Ethereum】送信者が特定の条件の元、ethを送金
sendではなく、recipient.callで送信してますね。
pragma solidity ^0.8.0; constract ConditionTransfer { address public owner; address payable public recipient; uint256 public unlockTime; constructor(address payable _recipient, uint256 _unlockTime) { owner = msg.sender; recipient = _recipient; unlockTime = _unlockTime; } receive() external payable {} function release() external { require(msg.sender == owner, "Only owner can release funds"); require(block.timestamp >= unlockTime, "Funds are locked"); uint256 amount = address(this).balance; require(amount > 0, "No funds to send"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Transfer failed"); } }
【Python】kecchak256
kecchak256は、文字列をランダムな256ビットの16進数にマッピングする機能
ethereumで使用されている
$ pip3 install pysha3
import sha3 data = b'Hello, keccak256!' keccak = sha3.keccak_256() keccak.update(data) hash_hex = keccak.hexdigest() print("keccak256 hash:", hash_hex)
$ python3 keccak256.py
keccak256 hash: 0fa5c2f72a50589fff02f2f5105ba40d97c01e114df6fd00cc4b1fc0afbec43a
【Ethereum】ERC721
ERC721は非代替トークンのNFT規格の一つ
pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyToken is ERC721, Ownable { constructor(address initialOwner) ERC721("HFToken", "HogeFuga") // MyToken..トークン名 MTK..トークン名の略称 Ownable(initialOwner) {} function safeMint(address to, uint256 tokenId) public onlyOwner { _safeMint(to, tokenId); } }
【Ethereum】Solidityの基礎2
# Solidityのオブジェクト
## msg オブジェクト
– msg.sender: address
– msg.value: eth value
– msg.gas
– msg.data
## txオブジェクト
– tx.gasprice
– tx.origin: transaction address
## blockオブジェクト
– block.coinbase
– block.difficulty
– block.gaslimit
– block.number
– block.timestamp
## addressオブジェクト
– address.balance
– address.transfer
– address.send
## 関数
addmod, mulmod, keccak256, sha3, sha256, erecover, selfdestruct, this
## その他
– external: コントラクトの外部
– internal: コントラクトの内部
– view: データの閲覧のみ
– pure: 読み取りも行わない
– event: ブロックチェーン上で何かが生じた時、web上やアプリに伝えることができる仕組み
## ECR20
pragma solidity ^0.8.17; interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes calldata _extraData) external; } contract TokenERC20 { string public name; string public symbol; uint8 public decimals = 18; uint256 public totalSupply; mapping (address => uint256) public balanceOf; mapping (address => mapping (address => uint256)) public allowance; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed _owner, address indexed _sender, uint256 _value); event Burn(address indexed from, uint256 value); constructor( uint256 initialSupply, string memory tokenName, string memory tokenSymbol ) public { totalSupply = initialSupply * 10 ** uint256(decimals); balanceOf[msg.sender] = totalSupply; name = tokenName; symbol = tokenSymbol; } function _transfer(address _from, address _to, uint _value) internal { require(_to != address(0)); require(balanceOf[_from] = _value); require(balanceOf[_to] + _value = balanceOf[_to]); uint previousBalances = balanceOf[_from] + balanceOf[_to]; balanceOf[_from] -= _value; balanceOf[_to] += _value; emit Transfer(_from, _to, _value); assert(balanceOf[_from] + balanceOf[_to] == previousBalances); } function transfer(address _to, uint256 _value) public returns (bool success) { _transfer(msg.sender, _to, _value); return true; } function approve(address _spender, uint256 _value) public returns (bool success) { allowance[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } function approveAndCall(address _spender, uint256 _value, bytes memory _extraData) public returns (bool success) { tokenRecipient spender = tokenRecipient(_spender); if (approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, address(this), _extraData); return true; } } function burn(uint256 _value) public returns (bool success) { require(balanceOf[msg.sender] = _value); balanceOf[msg.sender] -= _value; totalSupply -= _value; emit Burn(msg.sender, _value); return true; } function burnFrom(address _from, uint256 _value) public returns (bool success) { require(balanceOf[_from] = _value); require(_value = allowance[_from][msg.sender]); balanceOf[_from] -= _value; allowance[_from][msg.sender] -= _value; totalSupply -= _value; emit Burn(_from, _value); return true; } }
from solidity:
TypeError: No matching declaration found after argument-dependent lookup.
–> ecr20.sol:31:8:
|
ん? 何故だ…
【Ethereum】トレーサビリティのsmart contractを考える
単純にアドレスに保存するだけであれば、mapping(address => Data) accounts;とすれば良さそう。
ただし、この場合は、商品ごとにアドレスを持っていないといけない。
緯度経度やstatusの管理は、フロントエンド側(React)で実装か…
memoryとstorageの仕様詳細が欲しいところ。。。
pragma solidity ^0.8.17; contract Traceability { struct Data { uint256 id; string name; string latitude; string longitude; string status; } address[] public users; mapping(address => Data) accounts; function registerAccount(uint256 _id, string memory _name, string memory latitude, string memory longitude, string memory status) public returns (bool) { accounts[msg.sender].id = _id; accounts[msg.sender].name = _name; accounts[msg.sender].latitude = latitude; accounts[msg.sender].longitude = longitude; accounts[msg.sender].status = status; users.push(msg.sender); return true; } function ViewAccount(address user) public view returns (uint256, string memory, string memory, string memory, string memory) { uint256 _id = accounts[user].id; string memory _name = accounts[user].name; string memory _latitude = accounts[user].latitude; string memory _longitude = accounts[user].longitude; string memory _status = accounts[user].status; return (_id, _name, _latitude, _longitude, _status); } }
ethの受け取り
pragma solidity ^0.8.17; contract RecvEther { address public sender; uint public recvEther; function () payable { sender = msg.sender; recvEther += msg.value; } }
【Ethereum】solidity 基礎
memoryは処理中のみ保存し、storageは処理後、ブロックチェーンに保存される。
pragma solidity ^0.8.17; contract Register { struct Data { string name; uint256 age; string hobby; } // 全ユーザのアドレスを保存 address[] public users; mapping(address => Data) accounts; function registerAccount(string memory _name, uint256 _age, string memory _hobby) public returns (bool) { if (!isUserExist(msg.sender)) { users.push(msg.sender); } // msg.sender はトランザクション送信者 accounts[msg.sender].name = _name; accounts[msg.sender].age = _age; accounts[msg.sender].hobby = _hobby; return true; } function isUserExist(address user) public view returns (bool) { for (uint256 i = 0; i < users.length; i++) { if (users[i] == user) { return true; } } return false; } function ViewAccount(address user) public view returns (string memory, uint256, string memory) { string memory _name = accounts[user].name; uint256 _age = accounts[user].age; string memory _hobby = accounts[user].hobby; return (_name, _age, _hobby); } }
remixでデプロイのテストができる
metamaskアカウントでsepoliaにdeployする
https://sepolia.etherscan.io/
フロントエンド
import React from "react"; import Resister from "./Register.json"; import getWeb3 from "./getWeb3"; // import "./App.css"; class App extends React.Component { constructor(props) { super(props); this.state = { web3: null, accounts: null, contract: null, name: null, age: null, hobby: null, address: "", outputName: null, outputAge: null, outputHobby: null, }; } componentDidMount = async () => { try { const web3 = await getWeb3(); const accounts = await web3.eth.getAccounts(); const networkId = await web3.eth.net.getId(); const deployedNetwork = Resister.networks[networkId]; const instance = new web3.eth.Contract( Resister.abi, deployedNetwork && deployedNetwork.address ); this.setState({ web3, accounts, contract: instance }); } catch (error) { alert( `Failed to load web3, accounts, or contract. Check console for details.` ); console.error(error); } const { accounts } = this.state; console.log(accounts); }; // アカウント情報の登録 writeRecord = async () => { const { accounts, contract, name, age, hobby } = this.state; const result = await contract.methods.registerAccount(name, age, hobby).send({ from: accounts[0], }); console.log(result); if (result.status === true) { alert('会員登録が完了しました。'); } }; // アカウント情報の読み込み viewRecord = async () => { const { contract, accounts } = this.state; console.log(contract); const result = await contract.methods.viewAccount(accounts[0]).call(); console.log(result); const outputName = result[0]; const outputAge = result[1]; const outputHobby = result[2]; this.setState({ outputName, outputAge, outputHobby }); }; handleChange = (name) => (event) => { this.setState({ [name]: event.target.value }); }; render() { return ( <div className="App"> <br /> <form> <div> <label>氏名:</label> <input onChange={this.handleChange("name")} /> </div> <div> <label>年齢:</label> <input onChange={this.handleChange("age")} /> </div> <div> <label>趣味:</label> <input onChange={this.handleChange("hobby")} /> </div> <button type='button' onClick={this.writeRecord}> 会員登録 </button> </form> <br /> <br /> <form> <label>検索したいアドレスを入力してください。</label> <input onChange={this.handleChange("address")} /> <button type='button' onClick={this.viewRecord}> 検索 </button> </form> <br /> <br /> {this.state.outputName ? <p>氏名: {this.state.outputName}</p> : <p></p>} {this.state.outputAge ? <p>年齢: {this.state.outputAge}</p> : <p></p>} {this.state.outputHobby ? <p>趣味: {this.state.outputHobby}</p> : <p></p>} </div> ); } } export default App;
ちょっと記事が古いので期待通りに動かないが、
【Ethereum】ECR20を発行しよう
$ pip3 install eth-brownie
$ npm install -g ganache-cli
$ brownie init
$ tree
.
├── build
│ ├── contracts
│ ├── deployments
│ └── interfaces
├── contracts
├── interfaces
├── reports
├── scripts
└── tests
contract/TokenERC20.sol
https://github.com/PatrickAlphaC/erc20-brownie/tree/main/contracts
pragma solidity ^0.6.0; interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes calldata _extraData) external; } contract TokenERC20 { // Public variables of the token string public name; string public symbol; uint8 public decimals = 18; // 18 decimals is the strongly suggested default, avoid changing it uint256 public totalSupply; // This creates an array with all balances mapping (address => uint256) public balanceOf; mapping (address => mapping (address => uint256)) public allowance; // This generates a public event on the blockchain that will notify clients event Transfer(address indexed from, address indexed to, uint256 value); // This generates a public event on the blockchain that will notify clients event Approval(address indexed _owner, address indexed _spender, uint256 _value); // This notifies clients about the amount burnt event Burn(address indexed from, uint256 value); /** * Constructor function * * Initializes contract with initial supply tokens to the creator of the contract */ constructor( uint256 initialSupply, string memory tokenName, string memory tokenSymbol ) public { totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens name = tokenName; // Set the name for display purposes symbol = tokenSymbol; // Set the symbol for display purposes emit Transfer(address(0), msg.sender, totalSupply); } /** * Internal transfer, only can be called by this contract */ function _transfer(address _from, address _to, uint _value) internal { // Prevent transfer to 0x0 address. Use burn() instead require(_to != address(0x0)); // Check if the sender has enough require(balanceOf[_from] >= _value); // Check for overflows require(balanceOf[_to] + _value >= balanceOf[_to]); // Save this for an assertion in the future uint previousBalances = balanceOf[_from] + balanceOf[_to]; // Subtract from the sender balanceOf[_from] -= _value; // Add the same to the recipient balanceOf[_to] += _value; emit Transfer(_from, _to, _value); // Asserts are used to use static analysis to find bugs in your code. They should never fail assert(balanceOf[_from] + balanceOf[_to] == previousBalances); } /** * Transfer tokens * * Send `_value` tokens to `_to` from your account * * @param _to The address of the recipient * @param _value the amount to send */ function transfer(address _to, uint256 _value) public returns (bool success) { _transfer(msg.sender, _to, _value); return true; } /** * Transfer tokens from other address * * Send `_value` tokens to `_to` on behalf of `_from` * * @param _from The address of the sender * @param _to The address of the recipient * @param _value the amount to send */ function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { require(_value <= allowance[_from][msg.sender]); // Check allowance allowance[_from][msg.sender] -= _value; _transfer(_from, _to, _value); return true; } /** * Set allowance for other address * * Allows `_spender` to spend no more than `_value` tokens on your behalf * * @param _spender The address authorized to spend * @param _value the max amount they can spend */ function approve(address _spender, uint256 _value) public returns (bool success) { allowance[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } /** * Set allowance for other address and notify * * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it * * @param _spender The address authorized to spend * @param _value the max amount they can spend * @param _extraData some extra information to send to the approved contract */ function approveAndCall(address _spender, uint256 _value, bytes memory _extraData) public returns (bool success) { tokenRecipient spender = tokenRecipient(_spender); if (approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, address(this), _extraData); return true; } } /** * Destroy tokens * * Remove `_value` tokens from the system irreversibly * * @param _value the amount of money to burn */ function burn(uint256 _value) public returns (bool success) { require(balanceOf[msg.sender] >= _value); // Check if the sender has enough balanceOf[msg.sender] -= _value; // Subtract from the sender totalSupply -= _value; // Updates totalSupply emit Burn(msg.sender, _value); return true; } /** * Destroy tokens from other account * * Remove `_value` tokens from the system irreversibly on behalf of `_from`. * * @param _from the address of the sender * @param _value the amount of money to burn */ function burnFrom(address _from, uint256 _value) public returns (bool success) { require(balanceOf[_from] >= _value); // Check if the targeted balance is enough require(_value <= allowance[_from][msg.sender]); // Check allowance balanceOf[_from] -= _value; // Subtract from the targeted balance allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance totalSupply -= _value; // Update totalSupply emit Burn(_from, _value); return true; } }
$ brownie compile
$ ganache-cli
Ganache CLI v6.12.2 (ganache-core: 2.13.2)
(node:179457) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node –trace-deprecation …` to show where the warning was created)
Available Accounts
==================
(0) 0xe866BE96aA793C69dc4CB69D717D12e7b0d5267d (100 ETH)
(1) 0x0696f9fB2fFe037F6E13Deba40912562f97faDC2 (100 ETH)
(2) 0x3785c546Fe6Fc72a7468059d4f7eE6f3d4727F2C (100 ETH)
(3) 0x11165cCc75b3E917C9918C529778c9cE4E58d513 (100 ETH)
(4) 0x3B26Bfe04381231728b9ca3bbC42435aa6D2155e (100 ETH)
(5) 0x9aA0b1bd92d42Da1cA1e842A05Ec5c69F3Dcd123 (100 ETH)
(6) 0x0aC021eCA14d61880693fd6Cd0FAAD598f208778 (100 ETH)
(7) 0xbd07f6ad435698D878f94D9A29337E09c51fd900 (100 ETH)
(8) 0xb8d7f305778207E48A1b70D1D54226ABd76156De (100 ETH)
(9) 0x7e41f77B97d567178A1443E2fe44d3168111BaF6 (100 ETH)
Private Keys
==================
// 省略
HD Wallet
==================
Mnemonic: course acid cereal genuine old blind someone ticket thrive palace napkin time
Base HD Path: m/44’/60’/0’/0/{account_index}
Gas Price
==================
20000000000
Gas Limit
==================
6721975
Call Gas Limit
==================
9007199254740991
Listening on 127.0.0.1:8545
script/deploy_token.py
from brownie import accounts, config, TokenERC20 initial_supply = 1000000000000000000000 # 1000 token_name = "Hpscript" token_symbol = "HPS" def main(): account = accounts.add(config["wallets"]["from_key"]) erc20 = TokenERC20.deploy( initial_supply, token_name, token_symbol, {"from": account} , publish_source=True )
brownie-config.yaml
dependencies: - OpenZeppelin/openzeppelin-contracts@3.4.0 compiler: solc: remappings: - '@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.4.0' dotenv: .env wallets: from_key: ${PRIVATE_KEY}
.env
export WEB3_INFURA_PROJECT_ID='' export PRIVATE_KEY='' export ETHERSCAN_TOKEN=''
$ source .env
$ brownie run scripts/deploy_token.py –network sepolia
スマートコントラクトで作るのか… なるほどね〜
[Ethereum] SolidityのContractをweb3.jsで呼び出したい
<body> <script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js/dist/web3.min.js"></script> <script> const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io/v3/*")); const address = "*"; const abi = [{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"clients","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"gender","type":"string"},{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"uint256","name":"height","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_gender","type":"string"},{"internalType":"uint256","name":"_weight","type":"uint256"},{"internalType":"uint256","name":"_height","type":"uint256"}],"name":"register","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"excesise","outputs":[{"internalType":"uint256","name":"newWeight","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] const contract = new web3.eth.Contract(abi, address); const handleCall = async () => { await contract.methods.register("tanaka", "aaa", 65, 175); const result = await contract.methods.excesise.call(); console.log(result); }; handleCall(); </script> </body> </html>
{arguments: Array(0), call: ƒ, send: ƒ, encodeABI: ƒ, estimateGas: ƒ, …}
何故だろう… 期待通りにいかん…
[ethereum] solidityをRopstenにデプロイしたい
$ npm install truffle-hdwallet-provider
$ truffle compile
truffleconfig.js
const HDWalletProvider = require("truffle-hdwallet-provider"); const mnemonic = ""; ropsten: { provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/${project_id}`), network_id: 3, // Ropsten's id gas: 5500000, // Ropsten has a lower block limit than mainnet confirmations: 2, // # of confs to wait between deployments. (default: 0) timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) },
$ truffle migrate –network ropsten
なるほど、これは凄い