-
Notifications
You must be signed in to change notification settings - Fork 6
/
multipleESDTSender.py
156 lines (127 loc) · 6.12 KB
/
multipleESDTSender.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from multiversx_sdk_wallet import UserSigner, UserPEM
from multiversx_sdk_core import Address, TokenPayment
from multiversx_sdk_core.transaction_builders import DefaultTransactionBuildersConfiguration, \
MultiESDTNFTTransferBuilder
from multiversx_sdk_network_providers import ProxyNetworkProvider
from alive_progress import alive_bar
from pathlib import Path
import json
import argparse
import requests
import sys
import pandas as pd
pd.options.mode.chained_assignment = None # default='warn'
# ---------------------------------------------------------------- #
# INPUTS
# ---------------------------------------------------------------- #
parser = argparse.ArgumentParser()
parser.add_argument("--filename", help="CSV file with two cols : Address and Count", required=True)
parser.add_argument("--ids", help="List of token id of the ESDTs", nargs='+', required=True)
parser.add_argument('--amounts_airdrop', help="List of respective amount to send", nargs='+', type=int, required=True)
parser.add_argument('--decimals', help="List of respective decimals (default 18)", nargs='+', type=int, default=[])
parser.add_argument("--pem", help="The wallet that sends txs (needs to hold the ESDT)", required=True)
parser.add_argument("--weighted",
help="Flag (true for an airdrop weighted by the quantity of NFTs hold for each address.)",
required=False, default=False)
args = parser.parse_args()
# Read the input file
data_df = pd.read_csv(args.filename)
# If not done already, remove SC addresses
eligible_holders = data_df[data_df.Address.apply(lambda x: "qqqqqq" not in x)]
# Compute the total of token per address (not taking into account the number of NFT hold)
# TMP FIX : Remove 0.0001 token per holder to avoid "insufficient funds" (due to Python's loss of precision)
airdrops_per_holder = []
for amount_airdrop in args.amounts_airdrop:
airdrop_per_holder = float(amount_airdrop) / (eligible_holders.shape[0]) - 0.0001
airdrops_per_holder.append(airdrop_per_holder)
# Compute the weighted airdrop if set to true as an argument
if args.weighted:
for index, amount_airdrop in enumerate(args.amounts_airdrop):
airdrop_per_NFT = float(amount_airdrop) / (eligible_holders.Count.sum()) - 0.0001
eligible_holders["Airdrop_" + str(index)] = airdrop_per_NFT * data_df.Count
# ---------------------------------------------------------------- #
# CONSTANTS
# ---------------------------------------------------------------- #
config_network = {
"mainnet": {"chainID": "1", "proxy": ""},
"devnet": {"chainID": "D", "proxy": "devnet-"},
"testnet": {"chainID": "T", "proxy": "testnet-"}
}
CHAIN = "mainnet"
CHAIN_ID = config_network[CHAIN]["chainID"]
PROXY = config_network[CHAIN]["proxy"]
TOKEN_DECIMALS = []
TOKEN_IDs = []
# ---------------------------------------------------------------- #
# MAIN Multiple ESDT FUNCTION
# ---------------------------------------------------------------- #
def sendMultipleESDT(owner, owner_on_network, receiver, amounts, signer):
payments = []
for index, amount_to_send in enumerate(amounts):
payment = TokenPayment.fungible_from_amount(TOKEN_IDs[index], amount_to_send, TOKEN_DECIMALS[index])
payments.append(payment)
config = DefaultTransactionBuildersConfiguration(chain_id=CHAIN_ID)
builder = MultiESDTNFTTransferBuilder(
config=config,
sender=owner,
destination=receiver,
payments=payments,
nonce=owner_on_network.nonce
)
tx = builder.build()
tx.signature = signer.sign(tx)
hashes = provider.send_transaction(tx)
owner_on_network.nonce += 1
return hashes
# ---------------------------------------------------------------- #
# SETTING MAINNET PARAMS
# ---------------------------------------------------------------- #
# The signer of the tokens, that will send them
signer = UserSigner.from_pem_file(Path(f"./{args.pem}"))
pem = UserPEM.from_file(Path(f"./{args.pem}"))
pubkey = bytes.fromhex(pem.public_key.hex())
owner = Address(pubkey, "erd")
provider = ProxyNetworkProvider(f"https://{PROXY}gateway.multiversx.com")
owner_on_network = provider.get_account(owner)
# ---------------------------------------------------------------- #
# CHECK ESDT BALANCES
# ---------------------------------------------------------------- #
for index, id in enumerate(args.ids):
try:
response = requests.get(f'https://{PROXY}api.multiversx.com/tokens/{id}')
response.raise_for_status()
except requests.exceptions.HTTPError as e:
print(f"ERROR: Token {id} does not exist")
sys.exit()
try:
response = requests.get(f'https://{PROXY}api.multiversx.com/accounts/{owner}/tokens/{id}')
response.raise_for_status()
if index < len(args.decimals):
TOKEN_DECIMALS.append(args.decimals[index])
else:
TOKEN_DECIMALS.append(18) # default : 18
balance = response.json()['balance']
amount_to_drop = float(args.amounts_airdrop[index]) * pow(10, int(TOKEN_DECIMALS[index]))
if float(balance) < amount_to_drop:
print(f"ERROR: You don't have enough {id}")
sys.exit()
except requests.exceptions.HTTPError as e:
print(f"ERROR: You don't own any {id}")
sys.exit()
TOKEN_IDs.append(id)
# ---------------------------------------------------------------- #
# AIRDROP LOOP
# ---------------------------------------------------------------- #
with alive_bar(len(eligible_holders), title=f'Sending ESDTs') as bar:
for _, row in eligible_holders.iterrows():
address = Address.from_bech32(row["Address"])
quantities = []
for index, amount in enumerate(args.amounts_airdrop):
quantity = row["Airdrop_" + str(index)] if args.weighted else airdrops_per_holder[index]
quantities.append(quantity)
try:
sendMultipleESDT(owner, owner_on_network, address, quantities, signer)
except:
# Keep those addresses aside for debugging, and re-sending after
print(address)
bar()