Hello world! So, the cryptoworld is basically on fire right now (and in fact this has been the case for quite a few very lonnnnnnnggg months already since we entered bear market territory…) but anyway, I still have some funds placed on DeFi protocols and generating a few bucks now and then, and the idea in this dev session is to automate the retrieval process for those rewards, converting them in stable coins in the process. This should not be too hard, right ? So I have hope to be able to finish this new article just tonight! [But I might be dreaming a little lol We'll see…]
withdraw(uint256 _pid, uint256 _wantAmt)
belt_addr = "0xE0e514c71282b6f4e823703a39374Cf58dc3eA4f" dex.get_quote(1.0,belt_addr, "BUSD")
"""BelfFinance protocol.""" import logging import time from nvp.nvp_component import NVPComponent from nvp.nvp_context import NVPContext from nvh.crypto.blockchain.evm_blockchain import EVMBlockchain logger = logging.getLogger(__name__) class BelfFinance(NVPComponent): """BelfFinance component class""" def __init__(self, ctx, chain=None): """Component constructor""" NVPComponent.__init__(self, ctx) if chain is None: chain = self.ctx.get_component("bsc_chain") self.chain: EVMBlockchain = chain self.config = self.ctx.get_config()["belt_finance"] logger.debug("Loading Belt finance chef contract...") self.master_sc = self.chain.get_contract( self.config["bsc_master_address"], abi_file="ABI/bsc_belt_finance.json" ) self.belt_token = self.chain.get_token(self.config["belt_token_address"]) self.busd_token = self.chain.get_token("BUSD") def get_pending_rewards(self, as_value=False): """report the pending rewards""" # We call the function "pendingBELT" to get the pending belt reward addr = self.chain.get_account_address() # The only pool we are interested in for the moment is the beltETH pool with pid==8 pid = 8 pending = self.master_sc.call_function("pendingBELT", pid, addr) if as_value: return self.belt_token.to_value(pending) return pending def get_belt_price(self): """Get the price of BELT in USD""" dex = self.chain.get_default_exchange() return dex.get_quote(1.0, self.belt_token.address(), "BUSD") def collect_pending_rewards(self, min_usd_value=5.0, to_stablecoin=False): """Collect the pending rewards""" amount = self.get_pending_rewards(as_value=False) val = self.belt_token.to_value(amount) price = self.get_belt_price() usd_val = val * price if val < min_usd_value: logger.info("Not claiming %f BELT: value too low: %.4f BUSD", val, usd_val) return # Otherwise we can perform the claim: pid = 8 logger.info("Claiming BELT rewards on pool %d...", pid) opr = self.master_sc.build_function_call("withdraw", pid, 0) b0 = self.belt_token.get_balance(as_value=False) if self.chain.perform_operation(opr, self.config["harvest_max_gas"]) is not None: b1 = self.belt_token.get_balance(as_value=False) while b1 == b0: logger.info("Waiting for BELT balance update...") time.sleep(1.0) b1 = self.belt_token.get_balance(as_value=False) got = b1 - b0 val = self.belt_token.to_value(got) logger.info("=> Claimed %f BELT from belt finance for pool %d", val, pid) if not to_stablecoin: # Send a collect message right now: rchat = self.get_component("rchat") msg = ":white_check_mark: 💰 *[Belt.finance]*: " msg += f"Claimed {val} BELT from belt finance from pool {pid}" rchat.send_message(msg) b1 = self.belt_token.get_balance(as_value=False) if b1 == b0: logger.info("Did not collect any BELT token: not swapping for BUSD.") return # got = b1 - b0 got = b1 # We swapp **ALL** the available BELTs below not just what we received val = self.belt_token.to_value(got) logger.info("Swapping %f BELT for BUSD...", val) usd_b0 = self.busd_token.get_balance(as_value=False) dex = self.chain.get_default_exchange() addr0 = self.belt_token.address() addr1 = self.busd_token.address() dex.swap_tokens(got, [addr0, addr1]) usd_b1 = self.busd_token.get_balance(as_value=False) usd_val = self.busd_token.to_value(usd_b1 - usd_b0) logger.info("=> Got %f BUSD from %f BELT", usd_val, val) rchat = self.get_component("rchat") msg = ":white_check_mark: 💰 *[Belt.finance]*: " msg += f"Swapped {val:.6g} BELT to {usd_val:.6g} BUSD" rchat.send_message(msg) def process_cmd_path(self, cmd): """Check if this component can process the given command""" if cmd == "price": price = self.get_belt_price() logger.info("Current BELT price: %.6g BUSD", price) return True if cmd == "pending": val = self.get_pending_rewards(as_value=True) price = self.get_belt_price() logger.info("Pending rewards: %.6f BELT (value: %.6f BUSD)", val, val * price) return True if cmd == "collect": min_val = self.get_param("min_value") to_stable = self.get_param("to_stable") self.collect_pending_rewards(min_val, to_stable) return True return False if __name__ == "__main__": # Create the context: context = NVPContext() # Add our component: comp = context.register_component("belt_finance", BelfFinance(context)) context.define_subparsers("main", ["price", "pending"]) psr = context.build_parser("collect") psr.add_float("--min", dest="min_value", default=5.0)("Minimal claim value in BUSD") psr.add_flag("-s", "--stable", dest="to_stable")("Swap BELT for stablecoin") comp.run()
$ nvp beltfinance collect --min 0.1 -s
File "/mnt/data1/dev/projects/NervProj/nvp/nvp_component.py", line 93, in run res = self.process_command(cmd) File "/mnt/data1/dev/projects/NervProj/nvp/nvp_component.py", line 83, in process_command return self.process_cmd_path(self.ctx.get_command_path()) File "/mnt/data1/dev/projects/NervHome/nvh/crypto/blockchain/blockchain_manager.py", line 55, in process_cmd_path chain.handle("collect_evm_blocks") File "/mnt/data1/dev/projects/NervProj/nvp/nvp_component.py", line 105, in handle return self.call_handler(f"{self.handlers_path}.{hname}", self, *args, **kwargs) File "/mnt/data1/dev/projects/NervProj/nvp/nvp_component.py", line 100, in call_handler return self.ctx.call_handler(hname, *args, **kwargs) File "/mnt/data1/dev/projects/NervProj/nvp/nvp_context.py", line 676, in call_handler return handler(*args, **kwargs) File "/mnt/data1/dev/projects/NervHome/nvh/crypto/blockchain/handlers/collect_evm_blocks.py", line 36, in handle process_block(cdb, tdb, bck, f"{idx*100.0/count:.2f}%: ") File "/mnt/data1/dev/projects/NervHome/nvh/crypto/blockchain/handlers/collect_evm_blocks.py", line 67, in process_block process_transactions(cdb, tdb, txs) File "/mnt/data1/dev/projects/NervHome/nvh/crypto/blockchain/handlers/collect_evm_blocks.py", line 126, in process_transactions tdb.insert_swap_tokens_ops(parsed["swap_tokens"]) File "/mnt/data1/dev/projects/NervHome/nvh/crypto/blockchain/transactions_db.py", line 247, in insert_swap_tokens_ops self.execute(self.insert_swap_tokens_sql, rows, many=True, commit=True) File "/mnt/data1/dev/projects/NervHome/nvh/crypto/blockchain/transactions_db.py", line 113, in execute return self.sql_db.execute(*args, **kaargs) File "/mnt/data1/dev/projects/NervHome/nvh/core/postgresql_db.py", line 60, in execute c.executemany(code, data) psycopg2.errors.NumericValueOutOfRange: "-11579208923731620000000000000000000000000" is out of range for type real
if am_in < 0.0 or am_out < 0.0: logger.warning("Invalid amounts in tx (hash=%s)", txh) utl.send_rocketchat_message(f"Invalid amounts in tx {txh}") return None
if am_in < 0.0 or am_out < 0.0 or am_in > 1e74 or am_out > 1e74: logger.warning("Invalid amounts %f or %f in tx (hash=%s)", am_in, am_out, txh) utl.send_rocketchat_message(f"Invalid amounts in tx {txh}") return None
"""Retrieve a Transaction hash given a transaction Id""" import logging from nvh.crypto.blockchain.evm_blockchain import EVMBlockchain logger = logging.getLogger("get_tx_hash") def handle(chain: EVMBlockchain, tid): """Handler function entry point""" tdb = chain.get_tx_db() tname = tdb.get_txdata_table() sql = f"SELECT block_number,tx_index from {tname} WHERE id={tid};" cur = tdb.execute(sql) row = cur.fetchone() if row is None: logger.warning("No transaction found with id=%d", tid) return None bnum, tx_index = row[0], row[1] # Get the corresponding block: bck = chain.get_block(bnum) txh = bck["transactions"][tx_index].hex() return txh
"""Find the most significant signature in the latest transactions""" import logging import nvp.core.utils as utl from nvh.crypto.blockchain.evm_blockchain import EVMBlockchain logger = logging.getLogger("find_relevant_signatures") def handle(chain: EVMBlockchain, tx_window_size=2000000, sig_count=1, tx_count=5): """Handler function entry point""" tdb = chain.get_tx_db() tname = tdb.get_txdata_table() sql = f"SELECT id,sig,registers FROM {tname} WHERE registers is not null LIMIT {tx_window_size};" rows = tdb.execute(sql).fetchall() nrows = len(rows) logger.info("Got %d rows", nrows) bcount = 0 size_stats = {} sig_tx_ids = {} for row in rows: sig = row[1] if sig is None: sig = 0 if sig not in size_stats: size_stats[sig] = { "sig": hex(utl.to_uint32(sig)), "sig_num": sig, "min_size": 100000, "max_size": 0, "mean_size": 0, "count": 0, } if sig not in sig_tx_ids: sig_tx_ids[sig] = [] if len(sig_tx_ids[sig]) < tx_count: sig_tx_ids[sig].append(row[0]) desc = size_stats[sig] size = 0 if row[2] is None else len(row[2]) desc["min_size"] = min(desc["min_size"], size) desc["max_size"] = max(desc["max_size"], size) desc["mean_size"] += size desc["count"] += 1 bcount += size stats = list(size_stats.values()) for desc in stats: desc["share"] = desc["mean_size"] desc["mean_size"] /= desc["count"] stats.sort(key=lambda item: item["share"], reverse=True) logger.info("Total number of bytes: %d", bcount) logger.info("Most significant sigs: %s", stats[:sig_count]) for i in range(sig_count): tids = sig_tx_ids[stats[i]["sig_num"]] tids = [chain.handle("get_tx_hash", tid) for tid in tids] logger.info("Sig %d ref transactions: %s", i, tids)
chain.handle("find_relevant_signatures")
2022/07/03 10:04:51 [find_relevant_signatures] INFO: Got 2000000 rows 2022/07/03 10:04:54 [find_relevant_signatures] INFO: Total number of bytes: 478017569 2022/07/03 10:04:54 [find_relevant_signatures] INFO: Most significant sigs: [{'sig': '0xc9807539', 'sig_num': -914328263, 'min_size': 896, 'max_size': 1408, 'mean_size': 1235.1013127734946, 'count': 23995, 'share': 29636256}] 2022/07/03 10:04:55 [find_relevant_signatures] INFO: Sig 0 ref transactions: ['0x9c45b5dcc9041e921d65f6d07b585c2f15965694341576556be5d4d654ef0a67', '0x92706123bef1c4f7c4728c3390e1962e281044d5b80ba81957bccf98b05a5c96', '0xbe2a21f152210d1613f1ca652aa9314a4ca329a9c70da75856f3ec3580cf9f0a', '0x61cdb2d318f8a2da9265870f493a1a6f74c1134d4c5bd1b44bef4bdf1ce7cb25', '0xd43e26dff6428f5f9b2197c760aa24e4ff7a8b168cd27a3e1dbefc8231e5773d']
transmit(bytes _report, bytes32[] _rs, bytes32[] _ss, bytes32 _rawVs)
and it seems to always come from a AccessControlledOffchainAggregator contract: this seems to be related to oracle stuff, so I would not know what to do with that data and I can probably just discard it for now.if cmd == "find-relevant-sig": chain_name = self.get_param("chain") chain: EVMBlockchain = self.get_component(f"{chain_name}_chain") sigs = chain.handle("find_relevant_signatures", verbose=False) sig = sigs[0]["sig"] txh = sigs[0]["tx_ids"][0] utl.send_rocketchat_message(f"{chain_name.upper()}: current most significant sig: {sig}, example tx: {txh}") return True
lfile="${log_dir}/relevant_sigs.log" nvp bchain find-relevant-sig -c bsc 2>&1 | tee -a $lfile nvp bchain find-relevant-sig -c eth 2>&1 | tee -a $lfile nvp bchain find-relevant-sig -c avax 2>&1 | tee -a $lfile nvp bchain find-relevant-sig -c celo 2>&1 | tee -a $lfile
function swap( IAggregationExecutor caller, SwapDescription calldata desc, bytes calldata data )
safeTransfer(address _token, address _to, uint256 _value)
.[ { "inputs": [ { "internalType": "address", "name": "_token", "type": "address" }, { "internalType": "address", "name": "_to", "type": "address" }, { "internalType": "uint256", "name": "_value", "type": "uint256" } ], "name": "safeTransfer", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ]
class ABIBuilder(NVPObject): """Class holding a Contract ABI construction""" def __init__(self, fnames): """Constructor""" NVPObject.__init__(self) self.abi = [] self.add_functions(fnames) def get_abi(self): """Retrieve the abi data""" return self.abi def add_function(self, fname): """Add a function to the abi""" # fname will be a full function declaration, so we parse it as such: idx = fname.find("(") self.check(idx > 0, "Invalid function name: %s", fname) args = fname[idx + 1 : -1] fname = fname[:idx] logger.info("Adding function '%s' with args '%s'", fname, args) inputs = [] args = [] if args == "" else args.split(",") for idx, aname in enumerate(args): inputs.append({"internalType": aname, "name": f"arg{idx}", "type": aname}) desc = {"inputs": inputs, "name": fname, "outputs": [], "stateMutability": "nonpayable", "type": "function"} self.abi.append(desc) def add_functions(self, fnames): """Add a list of functions""" for fname in fnames: self.add_function(fname)
abi = chain.build_abi(["safeTransfer(address,address,uint256)", "swap(uint256,uint256,address,address)"]) print("Generated ABI:", abi) sc = chain.get_contract(addr, abi=abi)
sc = chain.get_contract(addr, funcs=["safeTransfer(address,address,uint256)", "swap(uint256,uint256,address,address)"])
RewardMarketState storage supplyState = rewardSupplyState[rewardType][ qiToken ]; Double memory supplyIndex = Double({mantissa: supplyState.index}); Double memory supplierIndex = Double({ mantissa: rewardSupplierIndex[rewardType][qiToken][supplier] }); rewardSupplierIndex[rewardType][qiToken][supplier] = supplyIndex .mantissa; if (supplierIndex.mantissa == 0 && supplyIndex.mantissa > 0) { supplierIndex.mantissa = initialIndexConstant; } Double memory deltaIndex = sub_(supplyIndex, supplierIndex); uint256 supplierTokens = QiToken(qiToken).balanceOf(supplier); uint256 supplierDelta = mul_(supplierTokens, deltaIndex); uint256 supplierAccrued = add_( rewardAccrued[rewardType][supplier], supplierDelta ); rewardAccrued[rewardType][supplier] = supplierAccrued;
def get_pending_rewards(self, as_value=False): """report the pending rewards""" # We call the function "pendingBELT" to get the pending belt reward addr = self.chain.get_account_address() # Need to manually compute the pending rewards here: qi_btc_addr = self.config["qi_btc_address"] qitoken = self.chain.get_token(qi_btc_addr) supplier_tokens = qitoken.get_balance(as_value=False) res = self.ctrl_sc.call_function("rewardSupplyState", 0, qi_btc_addr) supply_index = res[0] # supply_ts = res[1] # logger.info("supply state: %s", res) supplier_index = self.ctrl_sc.call_function("rewardSupplierIndex", 0, qi_btc_addr, addr) # logger.info("supplier index: %d", supplier_index) delta_index = supply_index - supplier_index # logger.info("qi_btc balance: %d", supplier_tokens) pending_qi = (supplier_tokens * delta_index) // 1e36 # Compute AVAX reward: res = self.ctrl_sc.call_function("rewardSupplyState", 1, qi_btc_addr) supply_index = res[0] supplier_index = self.ctrl_sc.call_function("rewardSupplierIndex", 1, qi_btc_addr, addr) delta_index = supply_index - supplier_index pending_avax = (supplier_tokens * delta_index) // 1e36 if as_value: pending_qi = self.qi_token.to_value(pending_qi) pending_avax = self.avax_token.to_value(pending_avax) return pending_qi, pending_avax
"""BenqiFinance protocol.""" import logging import time from nvp.nvp_component import NVPComponent from nvp.nvp_context import NVPContext from nvh.crypto.blockchain.evm_blockchain import EVMBlockchain logger = logging.getLogger(__name__) class BenqiFinance(NVPComponent): """BenqiFinance component class""" def __init__(self, ctx, chain=None): """Component constructor""" NVPComponent.__init__(self, ctx) if chain is None: chain = self.ctx.get_component("avax_chain") self.chain: EVMBlockchain = chain self.config = self.ctx.get_config()["benqi_finance"] logger.debug("Loading Benqi finance controller contract...") self.ctrl_sc = self.chain.get_contract( self.config["controller_address"], abi_file="ABI/avax_benqi_controller.json" ) self.qi_token = self.chain.get_token(self.config["qi_token_address"]) self.avax_token = self.chain.get_wrapped_native_token() self.usdc_token = self.chain.get_token("USDC") def get_pending_rewards(self, as_value=False): """report the pending rewards""" # We call the function "pendingBELT" to get the pending belt reward addr = self.chain.get_account_address() # Need to manually compute the pending rewards here: qi_btc_addr = self.config["qi_btc_address"] qitoken = self.chain.get_token(qi_btc_addr) supplier_tokens = qitoken.get_balance(as_value=False) res = self.ctrl_sc.call_function("rewardSupplyState", 0, qi_btc_addr) supply_index = res[0] # supply_ts = res[1] # logger.info("supply state: %s", res) supplier_index = self.ctrl_sc.call_function("rewardSupplierIndex", 0, qi_btc_addr, addr) # logger.info("supplier index: %d", supplier_index) delta_index = supply_index - supplier_index # logger.info("qi_btc balance: %d", supplier_tokens) pending_qi = (supplier_tokens * delta_index) // 1e36 # Compute AVAX reward: res = self.ctrl_sc.call_function("rewardSupplyState", 1, qi_btc_addr) supply_index = res[0] supplier_index = self.ctrl_sc.call_function("rewardSupplierIndex", 1, qi_btc_addr, addr) delta_index = supply_index - supplier_index pending_avax = (supplier_tokens * delta_index) // 1e36 if as_value: pending_qi = self.qi_token.to_value(pending_qi) pending_avax = self.avax_token.to_value(pending_avax) return pending_qi, pending_avax def get_qi_price(self): """Get the price of QI in USD""" dex = self.chain.get_default_exchange() return dex.get_quote(1.0, self.qi_token.address(), "USDC") def get_avax_price(self): """Get the price of AVAX in USD""" dex = self.chain.get_default_exchange() return dex.get_quote(1.0, "AVAX", "USDC") def collect_reward_type(self, rtype, token, amount, price, qi_tokens, min_usd_value, to_stablecoin): """Collect reward type""" val = token.to_value(amount) usd_val = val * price sym = token.symbol() # Will receive native tokens directly here: is_native = sym == "WAVAX" if is_native: sym = "AVAX" if usd_val < min_usd_value: logger.info("Not claiming %f %s: value too low: %.4f USD", val, sym, usd_val) return # Otherwise we can perform the claim: logger.info("Claiming %s rewards...", sym) addr = self.chain.get_account_address() opr = self.ctrl_sc.build_function_call("claimReward", rtype, addr, qi_tokens) b0 = self.chain.get_balance(sym, as_value=False) if self.chain.perform_operation(opr, self.config["harvest_max_gas"]) is not None: b1 = self.chain.get_balance(sym, as_value=False) while b1 == b0: logger.info("Waiting for %s balance update...", sym) time.sleep(1.0) b1 = self.chain.get_balance(sym, as_value=False) got = b1 - b0 val = token.to_value(got) logger.info("=> Claimed %f %s from benqi finance", val, sym) if not to_stablecoin: # Send a collect message right now: rchat = self.get_component("rchat") msg = ":white_check_mark: 💰 *[Benqi.finance]*: " msg += f"Claimed {val} {sym} from benqi finance." rchat.send_message(msg) b1 = self.chain.get_balance(sym, as_value=False) if b1 == b0: logger.info("Did not collect any %s token: not swapping for USDC.", sym) return got = b1 - b0 val = token.to_value(got) logger.info("Swapping %f %s for USDC...", val, sym) usd_b0 = self.usdc_token.get_balance(as_value=False) dex = self.chain.get_default_exchange() addr1 = self.usdc_token.address() dex.swap_tokens(got, [sym, addr1]) usd_b1 = self.usdc_token.get_balance(as_value=False) usd_val = self.usdc_token.to_value(usd_b1 - usd_b0) logger.info("=> Got %f BUSD from %f %s", usd_val, val, sym) rchat = self.get_component("rchat") msg = ":white_check_mark: 💰 *[Benqi.finance]*: " msg += f"Swapped {val:.6g} {sym} to {usd_val:.6g} USDC" rchat.send_message(msg) def collect_pending_rewards(self, min_usd_value=5.0, to_stablecoin=False): """Collect the pending rewards""" amount_qi, amount_avax = self.get_pending_rewards(as_value=False) price_qi = self.get_qi_price() price_avax = self.get_avax_price() qi_tokens = [self.config["qi_btc_address"]] self.collect_reward_type(0, self.qi_token, amount_qi, price_qi, qi_tokens, min_usd_value, to_stablecoin) self.collect_reward_type(1, self.avax_token, amount_avax, price_avax, qi_tokens, min_usd_value, False) def process_cmd_path(self, cmd): """Check if this component can process the given command""" if cmd == "price": p1 = self.get_qi_price() p2 = self.get_avax_price() logger.info("Current QI price: %.6g USDC", p1) logger.info("Current AVAX price: %.6g USDC", p2) return True if cmd == "pending": r1, r2 = self.get_pending_rewards(as_value=True) p1 = self.get_qi_price() p2 = self.get_avax_price() logger.info( "Pending rewards: %.6f QI (value: %.6f USDC), %.6f AVAX (value: %.6f USDC)", r1, r1 * p1, r2, r2 * p2 ) return True if cmd == "collect": min_val = self.get_param("min_value") to_stable = self.get_param("to_stable") self.collect_pending_rewards(min_val, to_stable) return True return False if __name__ == "__main__": # Create the context: context = NVPContext() # Add our component: comp = context.register_component("belt_finance", BenqiFinance(context)) context.define_subparsers("main", ["price", "pending"]) psr = context.build_parser("collect") psr.add_float("--min", dest="min_value", default=5.0)("Minimal claim value in USDC") psr.add_flag("-s", "--stable", dest="to_stable")("Swap rewards for stablecoin") comp.run()
"""PancakeSwap2 protocol.""" import logging import time from nvp.nvp_component import NVPComponent from nvp.nvp_context import NVPContext from nvh.crypto.blockchain.evm_blockchain import EVMBlockchain logger = logging.getLogger(__name__) class PancakeSwap2(NVPComponent): """PancakeSwap2 component class""" def __init__(self, ctx, chain=None): """Component constructor""" NVPComponent.__init__(self, ctx) if chain is None: chain = self.ctx.get_component("bsc_chain") self.chain: EVMBlockchain = chain self.config = self.ctx.get_config()["pancakeswap2"] logger.debug("Loading PancakeSwap2 chef contract...") self.chef_sc = self.chain.get_contract(self.config["chef_address"], abi_file="ABI/bsc_pancakeswap2.json") addr = self.chef_sc.call_function("rewardToken") self.reward_token = self.chain.get_token(addr) self.symbol = self.reward_token.symbol() logger.info("Reward token is: %s", self.symbol) self.busd_token = self.chain.get_token("BUSD") def get_pending_rewards(self, as_value=False): """report the pending rewards""" # We call the function "pendingReward" to get the pending rewards addr = self.chain.get_account_address() # The only pool we are interested in for the moment is the beltETH pool with pid==8 pending = self.chef_sc.call_function("pendingReward", addr) if as_value: return self.reward_token.to_value(pending) return pending def get_reward_token_price(self): """Get the price of reward token in USD""" dex = self.chain.get_default_exchange() return dex.get_quote(1.0, self.reward_token.address(), "BUSD") def collect_pending_rewards(self, min_usd_value=5.0, to_stablecoin=False): """Collect the pending rewards""" amount = self.get_pending_rewards(as_value=False) val = self.reward_token.to_value(amount) price = self.get_reward_token_price() sym = self.symbol usd_val = val * price if usd_val < min_usd_value: logger.info("Not claiming %f %s: value too low: %.4f BUSD", val, sym, usd_val) return # Otherwise we can perform the claim: logger.info("Claiming %s rewards...", sym) opr = self.chef_sc.build_function_call("deposit", 0) b0 = self.reward_token.get_balance(as_value=False) if self.chain.perform_operation(opr, self.config["harvest_max_gas"]) is not None: b1 = self.reward_token.get_balance(as_value=False) while b1 == b0: logger.info("Waiting for %s balance update...", sym) time.sleep(1.0) b1 = self.reward_token.get_balance(as_value=False) got = b1 - b0 val = self.reward_token.to_value(got) logger.info("=> Claimed %f %s from pancakeswap2", val, sym) if not to_stablecoin: # Send a collect message right now: rchat = self.get_component("rchat") msg = ":white_check_mark: 💰 *[PancakeSwap2]*: " msg += f"Claimed {val} {sym} from pancakeswap2 pool" rchat.send_message(msg) b1 = self.reward_token.get_balance(as_value=False) if b1 == b0: logger.info("Did not collect any %s token: not swapping for BUSD.", sym) return # got = b1 - b0 got = b1 # We swapp **ALL** the available BELTs below not just what we received val = self.reward_token.to_value(got) logger.info("Swapping %f %s for BUSD...", val, sym) usd_b0 = self.busd_token.get_balance(as_value=False) dex = self.chain.get_default_exchange() addr0 = self.reward_token.address() addr1 = self.busd_token.address() dex.swap_tokens(got, [addr0, addr1]) usd_b1 = self.busd_token.get_balance(as_value=False) usd_val = self.busd_token.to_value(usd_b1 - usd_b0) logger.info("=> Got %f BUSD from %f %s", usd_val, val, sym) rchat = self.get_component("rchat") msg = ":white_check_mark: 💰 *[PancakeSwap2]*: " msg += f"Swapped {val:.6g} {sym} to {usd_val:.6g} BUSD" rchat.send_message(msg) def process_cmd_path(self, cmd): """Check if this component can process the given command""" if cmd == "price": price = self.get_reward_token_price() logger.info("Current %s price: %.6g BUSD", self.symbol, price) return True if cmd == "pending": val = self.get_pending_rewards(as_value=True) price = self.get_reward_token_price() logger.info("Pending rewards: %.6f %s (value: %.6f BUSD)", val, self.symbol, val * price) return True if cmd == "collect": min_val = self.get_param("min_value") to_stable = self.get_param("to_stable") self.collect_pending_rewards(min_val, to_stable) return True return False if __name__ == "__main__": # Create the context: context = NVPContext() # Add our component: comp = context.register_component("pancakeswap2_pools", PancakeSwap2(context)) context.define_subparsers("main", ["price", "pending"]) psr = context.build_parser("collect") psr.add_float("--min", dest="min_value", default=5.0)("Minimal claim value in BUSD") psr.add_flag("-s", "--stable", dest="to_stable")("Swap BELT for stablecoin") comp.run()