์ด ์ ์ฅ์๋ ์ด๋๋ฆฌ์ ๋ค์ดํฐ๋ธ ํ ํฐ(ETH)๊ณผ ERC-20 ํ ํฐ์ ์ฌ๋ฌ ๊ณ์ ์ ํจ์จ์ ์ผ๋ก ์ ์กํ๊ณ , ๋ค์ ํ ๊ณ์ ์ผ๋ก ์์งํ๊ธฐ ์ํ ์ค๋งํธ ์ปจํธ๋ํธ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ผ๊ด ์ ์ก: ํ ๋ฒ์ ํธ๋์ญ์ ์ผ๋ก ์ฌ๋ฌ ์ฃผ์์ ์ด๋๋ ํ ํฐ์ ์ ์ก
- ํ ํฐ ์น์ธ: ์ฌ๋ฌ ๊ณ์ ์์ ํน์ ์ฃผ์์ ERC-20 ํ ํฐ ์ฌ์ฉ ๊ถํ ๋ถ์ฌ
- ํ ํฐ ์์ง: ์ฌ๋ฌ ๊ณ์ ์์ ํ๋์ ์ฃผ์๋ก ํ ํฐ ๋ชจ์ผ๊ธฐ
- ๋ค์ดํฐ๋ธ ํ ํฐ(์: ETH) ์์ง: ์ฌ๋ฌ ๊ณ์ ์์ ํ๋์ ์ฃผ์๋ก ๋ค์ดํฐ๋ธ ํ ํฐ ๋ชจ์ผ๊ธฐ
- ๋๊ธฐ์/๋น๋๊ธฐ์ ์ฒ๋ฆฌ: ์ผ๋ฐ ์ฒ๋ฆฌ ๋๋ ๋๊ท๋ชจ ๊ณ์ ์ ์ํ ๊ณ ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ
eth-bulk-operations/
โโโ contracts/ # ์ค๋งํธ ์ปจํธ๋ํธ ํ์ผ
โ โโโ BatchTransfer.sol
โโโ script/ # Foundry ๋ฐฐํฌ ์คํฌ๋ฆฝํธ
โ โโโ DeployBatchTransfer.s.sol
โโโ scripts/ # ํ์ด์ฌ ์คํฌ๋ฆฝํธ
โ โโโ token/ # ERC-20 ๊ด๋ จ ์คํฌ๋ฆฝํธ
โ โ โโโ approve_sync.py
โ โ โโโ approve_async.py
โ โ โโโ gather_sync.py
โ โ โโโ gather_async.py
โ โโโ native/ # ๋ค์ดํฐ๋ธ ํ ํฐ ๊ด๋ จ ์คํฌ๋ฆฝํธ
โ โ โโโ batch_send.py
โ โโโ utils/ # ๊ณตํต ์ ํธ๋ฆฌํฐ
โ โโโ common.py
โโโ examples/ # ์์ ํ์ผ
โโโ accounts_example.json
โโโ .env.example
- Python 3.7+
- Foundry (์ปจํธ๋ํธ ๋ฐฐํฌ ์ ํ์)
curl -L https://foundry.paradigm.xyz | bash์ค์น ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ํ, ๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ Foundry๋ฅผ ์์คํ ์ ๋ก๋ํฉ๋๋ค:
source ~/.bashrc # bash ์ฌ์ฉ์
# ๋๋
source ~/.zshrc # zsh ์ฌ์ฉ์๊ทธ๋ฐ ๋ค์ foundryup ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ Foundry ๋๊ตฌ๋ฅผ ์ค์นํฉ๋๋ค:
foundryupcurl -L https://foundry.paradigm.xyz | bash์ค์น ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ ํ, ๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ Foundry๋ฅผ ์์คํ ์ ๋ก๋ํฉ๋๋ค:
source ~/.bashrc๊ทธ๋ฐ ๋ค์ foundryup ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ Foundry ๋๊ตฌ๋ฅผ ์ค์นํฉ๋๋ค:
foundryupWindows์์๋ ๊ณต์ ์ค์น ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํ์ญ์์ค.
๊ฐ๋จํ ์์ฝํ๋ฉด:
curl -L https://foundry.paradigm.xyz | bash- ๊ทธ๋ฐ ๋ค์ foundryup ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ Foundry ๋๊ตฌ๋ฅผ ์ค์นํฉ๋๋ค:
foundryup์ค์น๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋์๋์ง ํ์ธํ๋ ค๋ฉด ๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํ์ธ์:
forge --version
cast --version
foundryup --help-
์ ์ฅ์ ๋ณต์ :
git clone https://github.com/yourusername/eth-bulk-operations.git cd eth-bulk-operations -
ํ์ด์ฌ ํจํค์ง ์ค์น:
pip install web3 python-dotenv
-
ํ๊ฒฝ ์ค์ :
cp examples/.env.example .env # .env ํ์ผ์ ํธ์งํ์ฌ ํ์ํ ์ ๋ณด๋ฅผ ์ ๋ ฅํ์ธ์
์ด ์ปจํธ๋ํธ๋ ํ ๋ฒ์ ํธ๋์ญ์ ์ผ๋ก ์ฌ๋ฌ ์ฃผ์์ ETH๋ ERC-20 ํ ํฐ์ ์ ์กํ ์ ์๊ฒ ํด์ค๋๋ค.
batchTransfer: ์ฌ๋ฌ ์ฃผ์๋ก ETH ์ผ๊ด ์ ์กbatchTransferERC20: ์ฌ๋ฌ ์ฃผ์๋ก ERC-20 ํ ํฐ ์ผ๊ด ์ ์กdepositERC20: ERC-20 ํ ํฐ์ ์ปจํธ๋ํธ์ ์ ๊ธwithdrawERC20: ERC-20 ํ ํฐ์ ์ปจํธ๋ํธ์์ ์ธ์ถwithdraw: ์ปจํธ๋ํธ์์ ETH ์ธ์ถ
-
Foundry ์ด๊ธฐํ(์ฒ์ ์ฌ์ฉ์):
forge init --no-commit
-
์์กด์ฑ ์ค์น:
forge install OpenZeppelin/openzeppelin-contracts
-
์ปจํธ๋ํธ ๋ฐฐํฌ:
# ๋ก์ปฌ ํ ์คํธ๋ท forge script script/DeployBatchTransfer.s.sol --rpc-url http://localhost:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast # ์ค์ ๋คํธ์ํฌ forge script script/DeployBatchTransfer.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY --broadcast --verify
# scripts/native ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd scripts/native
# batch_send.py ์คํ
python batch_send.py# scripts/token ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd scripts/token
# ๋๊ธฐ์ ์ฒ๋ฆฌ (์๋์ ๊ณ์ ์ธ ๊ฒฝ์ฐ)
python approve_sync.py
# ๋น๋๊ธฐ์ ์ฒ๋ฆฌ (๋๋์ ๊ณ์ ์ธ ๊ฒฝ์ฐ)
python approve_async.pyBatchTransfer ์ปจํธ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ์ฃผ์์ ERC-20 ํ ํฐ์ ํ ๋ฒ์ ์ ์กํ ์ ์์ต๋๋ค.
# scripts/native ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd scripts/native
# ์ปจํธ๋ํธ์ batchTransferERC20 ํจ์๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด batch_send.py ์คํ
# ์ด ๋ ์คํ ์ ์คํฌ๋ฆฝํธ ๋ด๋ถ์ USE_ERC20 ๋ณ์๋ฅผ True๋ก ์ค์ ํ๊ณ
# TOKEN_CONTRACT_ADDRESS ๋ณ์๋ฅผ ์ฌ์ฉํ ํ ํฐ์ ์ฃผ์๋ก ์ค์ ํด์ผ ํฉ๋๋ค
python batch_send.py์ฐธ๊ณ : ERC-20 ํ ํฐ์ ์ฌ์ฉํ๊ธฐ ์ ์, ํ ํฐ ์ปจํธ๋ํธ์์ BatchTransfer ์ปจํธ๋ํธ์ ๋ํด approve๋ฅผ ๋จผ์ ์คํํด์ผ ํฉ๋๋ค.
# scripts/native ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd scripts/native
# ๋๊ธฐ์ ์ฒ๋ฆฌ (์๋์ ๊ณ์ ์ธ ๊ฒฝ์ฐ)
python gather_native_sync.py
# ๋น๋๊ธฐ์ ์ฒ๋ฆฌ (๋๋์ ๊ณ์ ์ธ ๊ฒฝ์ฐ)
python gather_native_async.py# scripts/token ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd scripts/token
# ๋๊ธฐ์ ์ฒ๋ฆฌ (์๋์ ๊ณ์ ์ธ ๊ฒฝ์ฐ)
python gather_sync.py
# ๋น๋๊ธฐ์ ์ฒ๋ฆฌ (๋๋์ ๊ณ์ ์ธ ๊ฒฝ์ฐ)
python gather_async.py๊ฐ ์คํฌ๋ฆฝํธ๋ ์คํํ๊ธฐ ์ ์ ๋ค์๊ณผ ๊ฐ์ ์ค์ ์ ํ์ธํ๊ณ ํ์์ ๋ง๊ฒ ์์ ํด์ผ ํฉ๋๋ค:
.envํ์ผ์ RPC_URL ๋ฐ CHAIN_ID ์ค์ - ERC-20 ํ ํฐ์ ์ปจํธ๋ํธ ์ฃผ์ ์์
- ๊ณ์ ์ ๋ณด๊ฐ ๋ด๊ธด JSON ํ์ผ ๊ฒฝ๋ก ์ง์
# ๋ค์ดํฐ๋ธ ํ ํฐ(ETH) ์ ์ก ์ค์
BATCH_SIZE = 100 # ํ ๋ฒ์ ์ฒ๋ฆฌํ ์ฃผ์ ๊ฐ์
AMOUNT_PER_ADDRESS = 0.001 # ๊ฐ ์ฃผ์์ ๋ณด๋ผ ETH ์
contract_address = '0x...' # ๋ฐฐํฌํ BatchTransfer ์ปจํธ๋ํธ ์ฃผ์
# ERC-20 ํ ํฐ ์ ์ก ์ค์
USE_ERC20 = False # ERC-20 ํ ํฐ ์ ์ก ์ True๋ก ์ค์
TOKEN_CONTRACT_ADDRESS = '0x...' # ์ ์กํ ERC-20 ํ ํฐ ์ปจํธ๋ํธ ์ฃผ์
TOKEN_AMOUNT_PER_ADDRESS = 10 # ๊ฐ ์ฃผ์์ ๋ณด๋ผ ํ ํฐ ์ (ํ ํฐ ๋จ์)TOKEN_CONTRACT_ADDRESS = '0x...' # ERC-20 ํ ํฐ ์ปจํธ๋ํธ ์ฃผ์
SPENDER_ADDRESS = '0x...' # approve ๋์ ์ฃผ์ (์ผ๋ฐ์ ์ผ๋ก BatchTransfer ์ฃผ์)
APPROVE_AMOUNT = 150 # approveํ ํ ํฐ ์ (ETH ๋จ์)RECIPIENT_ADDRESS = '0x...' # ETH๋ฅผ ์์งํ ๋์ ์ฃผ์
MIN_ETH_BALANCE = 0.001 # ์ ์ก์ ์ํ ์ต์ ETH ์์ก
RESERVE_GAS_ETH = 0.0005 # ๊ฐ์ค๋น๋ฅผ ์ํด ๋จ๊ฒจ๋ ETH ๊ธ์ก
CONCURRENT_TASKS = 500 # ๋์์ ์ฒ๋ฆฌํ ํ์คํฌ ์ (๋น๋๊ธฐ์ ๋ฒ์ ๋ง)TOKEN_CONTRACT_ADDRESS = '0x...' # ERC-20 ํ ํฐ ์ปจํธ๋ํธ ์ฃผ์
RECIPIENT_ADDRESS = '0x...' # ํ ํฐ์ ๋ชจ์ ๋์ ์ฃผ์
MIN_TOKEN_BALANCE = 0.001 # ์ ์ก ์ต์ ํ ํฐ ์์ก (ETH ๋จ์)๊ณ์ ์ ๋ณด๋ JSON ํ์ผ๋ก ๊ด๋ฆฌ๋ฉ๋๋ค. ๋ค์๊ณผ ๊ฐ์ ํ์์ผ๋ก ์์ฑํด์ผ ํฉ๋๋ค:
[
{
"address": "0x1234...",
"private_key": "0xabcd..."
},
...
]์ฃผ์: ๊ฐ์ธํค๋ ๋งค์ฐ ๋ฏผ๊ฐํ ์ ๋ณด์ด๋ฏ๋ก ๋ณด์์ ์ฃผ์ํ์ธ์. ํ ์คํธ์ฉ ๊ณ์ ๋ง ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.