【Rust】getblockのCLIコマンド機能を作る

CLIでgetblockと引数にblock heightのi32を受け取って、ブロック情報を表示します。

#[derive(Subcommand)]
enum Command {
  Getnewaddress,
  Getinfo,
  Getbalance(BalanceArgs),
  Getaccount,
  Getblock(BlockArgs),
}

fn main() {
   let args = App::parse();
   match args.command {
      Command::Getnewaddress => getnewaddress(),
      Command::Getinfo => getinfo(),
      Command::Getbalance(args) => args.run(),
      Command::Getaccount => getaccount(),
      Command::Getblock(args) => args.run(),
   }
}

#[derive(Args)]
struct BlockArgs {
  height: i32,
}

impl BlockArgs {
  fn run(&self) {
    let _ = cli::get_block(self.height);
  }
}

$ ./target/debug/ruscal getblock 8
Block { height: 8, version: “0.1.0”, time: 2025-02-25T06:48:54.561868021Z, transactions: [SignedTransaction { version: “0.1.0”, time: 2025-02-25T06:47:49.809920514Z, sender: “0410f87429e89498e928d00b6a6186fdc9ccbde2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0”, receiver: “1FsHydJvr3nKj3KcwFhSozBf2WBKMf9jeo”, amount: 50, nft_data: “”, nft_origin: “”, op_code: “”, signature: “8A5216CD62C309A4923E9088CA9284AFC7C0261D89754FCE84735EC34A193A089B7A4C62FC32F91AE0B83549D575CE3D5EBAC1C4B9A61F0EF67D1B851F3F3E53” }, SignedTransaction { version: “0.1.0”, time: 2025-02-25T06:48:45.817409745Z, sender: “0410f87429e89498e928d00b6a6186fdc9ccbde2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0”, receiver: “1FsHydJvr3nKj3KcwFhSozBf2WBKMf9jeo”, amount: 60, nft_data: “”, nft_origin: “”, op_code: “”, signature: “6E73E636DDA5A5F360280442BEA308CD3C38C7A4C769BC8D4965B5106F9F6647D15B0382DE90ECFB84AE462BB81F26099DB1F8A9F0276541C44C60376FF31D16” }], hash: “0000ddb477024b0fb97c02e34bfad38eaee9891524759ba8801e06283b12cb9c”, nonce: “270778”, merkle: “713f5eb6109b03eb22cf8236171040f6bbf6171fbb2513b782b8b9fa9f41807f”, miner: “MTYwLjE2LjExNy44MA==”, validator: “MTYwLjE2LjExNy44MA==”, op: “” }

同じような容量で、gettransactionも同様に作ります。
$ ./target/debug/ruscal gettransaction 8A5216CD62C309A4923E9088CA9284AFC7C0261D89754FCE84735EC34A193A089B7A4C62FC32F91AE0B83549D575CE3D5EBAC1C4B9A61F0EF67D1B851F3F3E53

transactionの送信の場合

#[derive(Args)]
struct MoveArgs {
  pub_key: String,
  address: String,
  amount: i32
}

impl MoveArgs {
  fn run(&self) {
    // transaction送信関数を呼び出す
  }
}

$ ./target/debug/ruscal move de2ca55d5746f0e1bf8f1a1fbdb7634de5c1d5b4e49dc29bb78613fefb199d93eb8eb73545791a245c1f1ca6d5ce0 12sSVCmfi7kgsdG4ZmaargPppDRvkE5zvD 1000

なるほど、大分わかってきました。

【Ethereum】erc20とethをswap

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));
    }
}

【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");
    }
}

【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;

ちょっと記事が古いので期待通りに動かないが、

【Solana】Proof of HistoryをPythonで書く

前のブロックのiter,hashoutをベースに iter%(2**delay) == 0 になるまで、ブロックごとに262144回sha256を計算して、hash値を求める。hashoutが決まっているので、先回りしてブロックを作れそうだが、SolanaはPoSのため、不正がバレるとstakingが没収されてしまう。なるほどねー

import datetime
import hashlib
import time
import os
import pathlib
import random
import string
from random import randint

iter=1
hashin = ''.join(random.choices(string.ascii_uppercase + string.digits, k = 10))
hashout = hashlib.sha256(hashin.encode()).hexdigest()

print(hashin)
print(hashout)

def datastream():
    v1 = int(randint(1200, 1500))
    v2 = int(randint(1300, 1700))
    v3 = int(randint(1100, 1500))
    v4 = int(randint(4000, 5600))
    v5 = int(randint(4000, 5600))
    v6 = int(randint(1900, 2400))
    v7 = int(randint(1920, 2300))
    v8 = int(randint(1850, 2200))
    v9 = int(randint(1900, 2300))
    v10 = int(randint(1800, 2200))
    return [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10];

class Block:
    blockNo = 0
    count = iter
    data = None
    next = None
    hash = "None"
    previous_hash = "None"
    timestamp = datetime.datetime.now()

    def __init__(self, data):
        self.data = datastream()

    def hash(self):
        file = pathlib.Path("TToken")
        if file.exists():
            tier=open("TToken").readline().rstrip()
        else:
            with open("benchmark.py") as infile:
                exec(infile.read())
            tier=open("TToken").readline().rstrip()

        if tier=="T1":
            h = hashlib.md5()
        elif tier=="T2":
            h = hashlib.sha1()
        elif tier=="T3":
            h = hashlib.blake2s()
        elif tier=="T4":
            h = hashlib.sha3_256()

        h.update(
		str(self.nonce).encode('utf-8') +
		str(self.data).encode('utf-8') +
		str(self.previous_hash).encode('utf-8') +
		str(self.timestamp).encode('utf-8') +
		str(self.blockNo).encode('utf-8')
		)
        return h.hexdigest()
    
        block.blockNo = self.block.blockNo + 1

    def __str__(self):
        return "Block Number: " + str(self.blockNo) + "\nHistory Count: " + str(self.count) + "\nBlock Data: " + str(self.data) + "\nBlock Hash: " + str(self.hash) + "\nPrevious Hash: " + str(self.previous_hash) + "\n--------------"

class Blockchain:
	block = Block("Genesis")
	dummy = head = block

	def add(self, block):
		if (self.block.blockNo ==0):
			block.previous_hash =  "Origin"
		else:
			block.previous_hash = self.block.hash
		block.blockNo = self.block.blockNo + 1

		self.block.next = block
		self.block = self.block.next

	def mine(self, block):
		global iter
		global hashin
		global hashout
		delay = 18
		cont = 1
		while(cont == 1):
			if int(iter%(2**delay) == 0):
				block.count = iter
				block.hash = hashout
				self.add(block)
				print(block)
				iter += 1
				hashin=hashout
				hashout = hashlib.sha256(hashin.encode()).hexdigest()
				cont = 0
				break
			else:
				iter += 1
				hashin=hashout
				hashout = hashlib.sha256(hashin.encode()).hexdigest()

t_initial = time.perf_counter()
blockchain = Blockchain()
b=int(input('Enter the number of Blocks for this simulation:'))

for n in range(b):
    blockchain.mine(Block(n+1))
t_final = time.perf_counter()
delta_t = t_final - t_initial
delta_unit = delta_t*1000/b
print("comutation Time per Block(ms):"+str(delta_unit))
input('Press ENTER to exit')

Enter the number of Blocks for this simulation:3
Block Number: 1
History Count: 262144
Block Data: [1297, 1642, 1372, 5138, 5301, 2188, 1998, 2150, 1914, 1862]
Block Hash: a53e39cef8be27ba38e73fe216fbfc2efc63bca056fa2a6a18380e9d93c98ea3
Previous Hash: Origin
————–
Block Number: 2
History Count: 524288
Block Data: [1211, 1633, 1307, 4757, 5133, 2206, 2032, 1891, 2257, 2139]
Block Hash: 79561ebd2627b432d1e619dee9db7ac85593a4357925827754b1faefd42c1b72
Previous Hash: a53e39cef8be27ba38e73fe216fbfc2efc63bca056fa2a6a18380e9d93c98ea3
————–
Block Number: 3
History Count: 786432
Block Data: [1459, 1682, 1131, 5339, 4983, 2057, 1948, 2192, 2017, 2076]
Block Hash: d33e10fa10273b5d64ccdad34ffcbaae7673cb785807c49f199b204a148e6cd9
Previous Hash: 79561ebd2627b432d1e619dee9db7ac85593a4357925827754b1faefd42c1b72
————–
comutation Time per Block(ms):795.6611973543962
Press ENTER to exit

【BNB】BEP-20 Token on BSCを作る

1. bitbankなどでbnbを取得して、metamaskに送信します。
※bitbankの表記では、bnbはビルドアンドビルドとなっており、binance cointと異なるかと思いってしまいますが、ビルドアンドビルドでOKです。
metamaskでbscネットワークに接続し、bnbが受け取れたことを確認してアドレスを取得

2. remixを表示し、bep-20.solファイルを作成する
https://remix.ethereum.org/

pragma solidity >=0.6.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";

contract HpsToken is ERC20 {
    constructor(uint256 initialSupply) public ERC20("HpscriptToken", "HPS") {
        _mint(msg.sender, initialSupply);
    }
}

3. コンパイル

4.DEPLOY & RUN TRANSACTIONS
– Environmentをmetamaskにする
– ACCOUNTがMetamaskのアドレスになっていることを確認
– CONTRACTが先ほどコンパイルしたTokenになっている
– トークン供給量は100,000,000に設定する
-> Deploy

5. txハッシュからトランザクション確認
https://bscscan.com/

6. トークンの受け取り
コントラクトのアドレスを入力

【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

スマートコントラクトで作るのか… なるほどね〜

【Blockchain】トレーサビリティの仕組みを考える

### フロントエンド
プロセスの選択肢があり、写真を撮って、Postする。緯度、経度情報が必要な場合は付与する。

<head>
    <meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
    <style>body {margin: 50px;}</style>
</head>
<body>
<div id="app">
    <form action="/complete" method="post" enctype="multipart/form-data">
    <h2 class="subtitle is-5">1.製品番号</h2>
    <span>1111001</span>
    <input type="hidden" name="serial_num" value="1111001"/>
    <br><br>
    <h2 class="subtitle is-5">2.製造工程を選択してください<h2>
    <select name="process" id="pet-select" class="select">
        <option value="">--Please choose an option--</option>
        <option value="1">企画デザイン</option>
        <option value="2">皮の検品</option>
        <option value="3">型抜き</option>
        <option value="4">パーツ作り</option>
        <option value="5">糊付け</option>
        <option value="6">表断面加工</option>
        <option value="7">縫製</option>
        <option value="8">仕上げ</option>
    </select>
    <br><br><br>
    <h2 class="subtitle is-5">3.背面カメラ</h2>
    <input type="file" name="file" onchange="previewFile(this);" capture="environment" accept="image/*"></label><br><br>
    <img id="preview"><br><br>
    <h2 class="subtitle is-5">4.現在地</h2>
    <span v-if="err">
        <!-- 緯度経度の情報を取得できませんでした。 -->
    </span>
    <lat></lat><br>
    <long></long><br>
    <input type="hidden" name="lat" v-model="lat"/>
    <input type="hidden" name="long" v-model="long"/>
    <br>
    <button class="button" type="submit">登録する</button>
    </form>
</div>

<script>
let lat = "35.6895014";
let long = "139.6917337";
function success(pos) {
  const crd = pos.coords;

  lat = crd.latitude;
  long = crd.longitude;
 
  let url = "/index.html?lat=" +lat + "&long=" + long;
  var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        err: null
    }
  })
}

function error(err) {
    Vue.component('lat',{
        template : '<span>緯度:' + lat + '</span>'
    })
    Vue.component('long',{
        template : '<span>経度:' + long + '</span>'
    })
    console.warn(`ERROR(${err.code}): ${err.message}`);
    let url = "/index.html?lat=" +lat + "&long=" + long;
    var app = new Vue({
    el: '#app',
    data: {
        lat: lat,
        long: long,
        err: err
    }
  })
  
}
navigator.geolocation.getCurrentPosition(success, error);
</script>
<script>
function previewFile(file) {
    if (file.files[0].size < 3000000) {
    var fileData = new FileReader();
    fileData.onload = (function() {
        document.getElementById('preview').setAttribute("style","width:150px;height:150px");
        document.getElementById('preview').src = fileData.result;
        
    });
    fileData.readAsDataURL(file.files[0]);
    }
}
</script>
</body>

### サーバサイド
追跡情報をチェーン上に入れるは、トランザクションとして署名して、walletから誰かのwallet宛に送らなければならない。
=> transaction detaの中に、商品識別子(serial number等)は必ず含める必要がある。
=> 商品識別子、製造プロセス、緯度、経度などの情報を全てトランザクションデータの中に入れる場合は、データ容量がある程度大きくなくてはいけないが、 商品識別子だけ入れて、後の項目はアプリケーション側(smart contract)のDBに保存すれば、データ容量は少なくて済む。その場合、アプリケーション側の開発が必要。
=> トレーサビリティに特化したブロックチェーンを作ることもできなくはないが、開発効率を考えるとナンセンス。
=> transaction dataにmessageの項目を作るのが一番良さそうには見える。

DEXとは何か? Uniswap/Raydiumを触ってみる

DEXはDecentralized Exchangesの略称で、仲介者となる企業が存在しない、ユーザ同士が直接仮想通貨を取引できるブロックチェーン上に構築される取引所

### 代表的なDEX
– Uniswap (ECR-20規格であれば全て取引できる)
– PancakeSwap
– dYdX (仮想通貨の先物取引)

ECR20は、イーサリアムのブロックチェーン上で動作するトークンの統一ルール、規格の一つ

### 日本の仮想通貨法
仮想通貨法には第一号仮想通貨、第二号仮想通貨の分類があり、前者は物品の購入などに際して不特定の者に使用できかつ不特定の者を相手として交換でき、後者は不特定の者を相手として第一号仮想通貨と交換できる
ECR20トークンはイーサリアムと交換可能なことから、第二号仮想通貨に該当する

### uniswap
metamaskなどのwalletがあれば簡単にswapができる
Uniswapでswapを実行すると、metamask上のwalletでも、swapした通貨を保有していることが確認できる。
https://app.uniswap.org/

イーサリアム: UniSwap
Binance Smart Chain: PancakeSwap
Solana : Raydium, Jupiter

Raydimでswapする場合は、PhantomなどSolのwalletを作成して、あとは接続するだけです。

RaydimもUIはUniSwapとかなり似ています。

なるほど、DEXはかなり凄いですね…