[Python] requirements.txt

pip freeze > requirements.txt

다음과 같은 형식으로 나타날 경우는 pip list --format=freeze > requirements.txt

pytz @ file:///private/var/folders/sy/f16zz6x50xz3113nwtb9bvq00000gp/T/abs_e0y2h1cdsw/croot/pytz_1695131602326/work

이를 이용해 패키지를 설치할 때는 다음과 같이 한다.

pip install --no-cache-dir -r requirements.txt

도커 이미지 빌드 시 사용은 https://hub.docker.com/_/python 를 참고.

README 템플릿

  • 코드가 상위 수준에서 하는 일
  • 설치 방법
  • 문제 해결 단계
  • 코드 유지 관리자
  • 라이선스 정보
  • 변경 로그changelog
  • 기본 사용 예시
  • 심화 자료와 문서 링크
## 설치
 1.
 2.
 3.
 4.
 5.

## 사용 예시

## 문제 해결

## 변경 로그

## 추가 자료

## 라이선스 정보

README 체크리스트

[Ethereum] 트랜잭션 해시로부터 Public key 및 주소 얻기

Ethereum의 경우 버전에 따라 트랜잭션에 gasPrice를 쓸 지, maxFeePerGas/maxPriorityFeePerGas 를 쓸 지가 정해진다. https://ethereum.stackexchange.com/questions/147692/how-to-get-public-key-by-the-transaction-hash 를 참고.

주소를 얻을 때, Public key에서 앞의 한 바이트(0x04)를 빼고, keccak256을 돌리는 부분에 유의하자.

const { ethers } = require("hardhat");

// 트랜잭션 서명 값
// const r = "0xe680637b83a1dd102364503bd77979b87c92ba651132a4df8e839af69c20af95";
// const s = "0x65becfa32384747adb05f543397baaf23d1c55fa28d0c9a38924912dae6df28f";
// const v = 12266684226873;
// const chainId = 6133342113419;

// 트랜잭션 해시
const txHash = "0xc7159866fb5b94d291e8624bdc731b7a6d46533b398065de7aa7dd1af7b6aa36";
const ethermainHash = "0xef120deb6ff2e516ad44724d220c0cd73166d169a2a0b0f66dfb5613d2d6169c"
const sepoliaHash = "0xfe4ddad4cae9ea353fc91441ee5db6c70bd5468673d907926be92dd4b8a63f63"

// 실제 트랜잭션에 사용된 private key. 아무거나 넣자.
const privateKey = "0x0000000000000000000000000000000000000000000000000000000000000001";

// Web3 프로바이더 설정 (Hardhat 로컬 노드 사용 예시)
const provider = ethers.provider;

// 서명된 트랜잭션 정보 가져오기
async function getPublicKeyFromTransactionHash(provider, txHash) {
  // Fetch the transaction using the transaction hash and provier
  const tx = await provider.getTransaction(txHash);

  console.log(tx);

  // Extract the all the relevant fields from the transaction (We need all of them)
  const unsignedTx = {
    gasPrice: tx.gasPrice,
    gasLimit: tx.gasLimit,
    value: tx.value,
    nonce: tx.nonce,
    data: tx.data,
    chainId: tx.chainId,
    to: tx.to,
    type: tx.type,
    // maxFeePerGas: tx.maxFeePerGas,
    // maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
  };

  // Serializing tx without the signature
  const serializedTx = ethers.utils.serializeTransaction(unsignedTx);

  // Extract the signature (v, r, s) from the transaction
  const { v, r, s } = tx;

  // Join splitted signature
  const signature = ethers.utils.joinSignature({ v, r, s });

  const recoveredPublicKey = ethers.utils.recoverPublicKey(
    ethers.utils.keccak256(serializedTx),
    signature,
  );
  console.log("recoveredPublicKey:", recoveredPublicKey);
  const keccak = ethers.utils.keccak256("0x" + recoveredPublicKey.slice(4))
  console.log("keccak256 of recoveredPublicKey:", keccak, ethers.utils.keccak256("0x" + recoveredPublicKey.slice(4)));

  // Recover the address or public key with (replace recoverAddress by recoverPublicKey) associated with the transaction
  return ethers.utils.recoverAddress(
    ethers.utils.keccak256(serializedTx),
    signature
  );
}

async function main() {
  const address = await getPublicKeyFromTransactionHash(provider, txHash);

  console.log("Address:", address);
}

main()
.then(() => process.exit(0))
.catch((error) => {
    console.error(error);
    process.exit(1);
});

[matplotlib] 한글 폰트 설정

apt-get install -y fonts-nanum
fc-cache -fv
rm ~/.cache/matplotlib -rf

혹은 Windows에서는

from matplotlib import font_manager, rc
font_path = 'C:/Windows/Fonts/NGULIM.TTF'
font = font_manager.FontProperties(fname=font_path).get_name()
rc('font', family=font)

[hardhat/ethers.js] getLedgerSigner

import { subtask } from "hardhat/config";
import { LedgerSigner } from "@ethers-ext/signer-ledger";
import HIDTransport from "@ledgerhq/hw-transport-node-hid";

const USE_LEDGER = {
  "61331819613419": true,
  "2022": true
};

subtask("getLedgerSigner", "Get LedgerSigner")
  .addOptionalPositionalParam("accountNumber", "Account number of the Ledger", "0")
  .setAction(async (taskArgs) => {
    const accountNumber = taskArgs.accountNumber;
    const chainId = (await ethers.provider.getNetwork()).chainId;
    const ledgerNeeded = USE_LEDGER[chainId] ?? false;
    console.log("ChainID:", chainId, "LedgerNeeded:", ledgerNeeded);
    if (ledgerNeeded) {
      const _master = new LedgerSigner(HIDTransport, ethers.provider, `m/44'/60'/${accountNumber}'/0/0`);
      _master.getFeeData = async () => {
        return {
            gasPrice: ethers.BigNumber.from(0),
            lastBaseFeePerGas: ethers.BigNumber.from(0),
            maxFeePerGas: ethers.utils.parseUnits("800", "gwei"),
            maxPriorityFeePerGas: ethers.utils.parseUnits("800", "gwei"),
        };
      };
      console.log(`LedgerSinger: m/44'/60'/${accountNumber}'/0/0:`, await _master.getAddress());
      _master.getBalance = async function() {
        return await this.provider.getBalance(this.getAddress());
      };

      return _master;
    }

    const accounts = await ethers.getSigners();
    return accounts[accountNumber];
  });

사용할 때는 hre를 이용해서 다음과 같이 하면 된다.

import { task } from "hardhat/config";
import './subtasks/getLedgerSigner';

task("ledgerBalance", "Prints an account's balance")
  .addOptionalPositionalParam("accountNumber", "The account number of the ledger", "0")
  .setAction(async (taskArgs) => {
    const signer = await hre.run("getLedgerSigner", { accountNumber: taskArgs.accountNumber });
    console.log("Signer Address:", await signer.getAddress());
    const balance = await signer.getBalance();

    console.log(ethers.utils.formatEther(balance), "ETH");
  });

[vue.js v3] Invalid Host header 에러

vue.js로 작성한 코드를 yarn serve로 dev server를 돌릴 때 호스트 네임을 주는 경우 “Invalid Host header”를 낸다.

다음을 vue.config.jsdevServerallowedHosts: "all"을 추가하면 된다.

  devServer: {
    port: 8888, // 변경하려는 포트 번호로 대체
    allowedHosts: "all",
  }

[ethers.js] Contract를 특정 블록 넘버 기준으로 호출

import { ethers } from 'ethers';
import contract_ABI from './abi/contract_ABI.json';
const provider = new ethers.JsonRpcProvider("http://mainnet.dasomoli.org:8545");
const contract_address = "0x1234567890123456789012345678901234567890";
const contract = new ethers.Contract(contract_address, contract_ABI, provider);

const balanceOf = contract.balanceOf(account_address, { blockTag: 12345678 });

위처럼 호출 시 blockTag: 와 함께 blockNumber를 주면 된다.

[ethers.js] 컨트랙트 이벤트 로그 및 데이터 보기

ethers.js v5 기준

const contract = await ethers.getContractAt("DasomOLIContract", contract_address);
// DasomEvent(type arg1, ...)
const filter = contract.filters.DasomEvent();
const currentBlock = await ethers.provider.getBlockNumber();
const fromBlock = currentBlock - 86400;
const events = await ethers.provider.getLogs({
        ...filter,
        fromBlock: fromBlock,
        toBlock: currentBlock,
      });
if (events.length > 0) {
  events.forEach((event: Log) => {
      console.log(`Dasom event found in block ${event.blockNumber}:`);
      console.log(`Transaction Hash: ${event.transactionHash}`);
      console.log(`Transaction data: ${event.data}`);
      const parsedLog = contract.interface.parseLog(event);
      const args = parsedLog.args;
      const arg1 = args.arg1;
      console.log(`Log Data:`, parsedLog.args);
      console.log('--------------------------');
  });
}