Splinterlands offers plenty of opportunity to make money. Aside from playing the game, there's passive investing and active trading.
But there's one more option. Renting. Let's say you have a card collection, but no time to play or are speculating on higher prices.
In that case you can rent your collection out.
Once you looked into it, renting can be quite the time consuming task. Making sure that all cards are rented out, adjusting prices and canceling unprofitable rentals.
Wouldn't it be nice if you could automate all of that? Well, fret not it's possible. And not as hard as it looks.
Requirements
Dependency | Version | Source |
---|---|---|
python | 3.x | https://www.python.org/downloads/ |
Beem | 0.24.26(or latest) | https://pypi.org/project/beem/ |
1. Making Inventory
First we need to know which cards we want to rent out. Usually it's not the whole collection but a subset. To uniquely identify a card we need its unique identifier uid for short. The uid can be found next to your card when viewing your card collection. Check the column "Card ID"
For this tutorial I'll be using two cards. The giant roc from above and a untamed card. You'll see later why I'm using two cards from different generations.
- C1-2-M57QKW5M68 (lvl 10 giant roc beta card)
- C4-209-F0UTJ8IC5S (lvl 4 chain golem untamed card)
1.1 Loading your collection
But now we need a couple informations about those cards. So lets write our first function to get those. First lets get your card collection:
import requests
API2 = "https://api2.splinterlands.com"
def get_card_collection(player):
url: str = API2 + "/cards/collection/" + player
return requests.get(url).json()["cards"]
As you can see, the function is rather simple, it just takes the player name and calls the endpoint.
The endpoint returns a list objects with lots of details for each of your cards. Below you can see an example object of that list.
{
"player":"aicu",
"uid":"C1-2-M57QKW5M68",
"card_detail_id":2,
"xp":7560,
"gold":false,
"edition":1,
"market_id":"9c0434883e015f2d452aebf943dd8224ac22bc7d-1",
"buy_price":"29.850",
"market_listing_type":"RENT",
"market_listing_status":0,
"last_used_block":43230660,
"last_used_player":"aicu",
"last_used_date":"2020-05-09T17:22:03.000Z",
"last_transferred_block":"None",
"last_transferred_date":"None",
"alpha_xp":0,
"delegated_to":"None",
"delegation_tx":"None",
"skin":"None",
"delegated_to_display_name":"None",
"display_name":"None",
"lock_days":"None",
"unlock_date":"None",
"level":10
}
1.2 Getting more details
Looks like we have all the information we need. But just in case lets implement another endpoint which gets even more card details:
def get_card_details():
url: str = API2 + "/cards/get_details"
return requests.get(url).json()
So we called just another endpoint, the endpoint returned another list of json objects, below an abbreviated version, but it already shows all the information we need.
{
"id":1,
"name":"Goblin Shaman",
"color":"Red",
"type":"Monster",
"sub_type":null,
"rarity":1,
"drop_rate":80,
.
.
.
}
Before we move on lets do some preprocessing of the data.
collection: Dict[str, dict] = {}
card_details: Dict[int, dict] = {}
collection_resp = get_card_collection("player_name")
details_resp = get_card_details()
for card in collection_resp:
collection[card["uid"]] = card
for detail in details_resp:
card_details[detail["id"]] = detail
We did that to make it easier and faster to look up data
later on. In programming speed is often in tradeoff with storage. In this case we increased the storage size by storing the data in a dictionary but also made the look up of the data faster compared to searching through a list (O(1) vs O(n)).
Last but not least lets declare our list of cards which we want to rent out:
cards_for_rental = {'C1-2-M57QKW5M68', 'C4-209-F0UTJ8IC5S'}
2. Getting Market Data
Now we have a lot of information about our card collection but still no market prices. Let's fix that:
def get_rental_listings(card_id: int, edition: int, gold: bool = False):
url: str = API2 + "/market/for_rent_by_card"
request: dict = {"card_detail_id": card_id,
"gold": str(gold).lower(),
"edition": edition}
return requests.get(url, params=request).json()
This function calls the api and returns a list of all available rental offers for this card. Now let's connect our uid with this endpoint:
def get_rentals_by_uid(uid: str):
uid_card = collection.get(uid)
return get_rental_listings(uid_card["card_detail_id"],
uid_card["edition"],
uid_card["gold"])
We now have a function which takes our uid and returns all the rental prices currently on the market for that type of card. You might have noticed that our collection dictionary comes in handy now.
The endpoint returns a list of the following objects. We can see all details about the market listing, like type, seller and cost of the rental.
{
"fee_percent":500,
"uid":"C1-2-S91HZ9Q7KW",
"seller":"doloknight",
"card_detail_id":2,
"xp":0,
"gold":false,
"edition":1,
"buy_price":"0.195",
"currency":"DEC",
"desc":"None",
"type":"RENT",
"market_id":"b5bd9f79f39a289ed6d02c27286623176089c479-36",
"last_transferred_block":"None",
"last_transferred_date":"None",
"last_used_block":58726972,
"last_used_date":"2021-10-30T03:25:17.361Z",
"last_used_player":"abdabiiz"
}
With that list we can figure out what the current market rate is for each card. But how do we do that ? Just use the lowest price in the list ? Lets do just that:
rental_list = get_rentals_by_uid(cards_for_rental[0])
rental_list.sort(key=lambda x: float(x["buy_price"]), reverse=False)
print(rental_list[0])
{
"fee_percent":500,
"uid":"C1-2-AW0GE0V0WW",
"seller":"louis88",
"card_detail_id":2,
"xp":0,
"gold":false,
"edition":1,
"buy_price":"0.100",
"currency":"DEC",
"desc":"None",
"type":"RENT",
"market_id":"dda6a28ad29965d7408af5a2b676d3a8ccfeea91-56",
"last_transferred_block":"None",
"last_transferred_date":"None",
"last_used_block":60404653,
"last_used_date":"2021-12-27T13:32:24.397Z",
"last_used_player":"sp3ktraline"
}
That is the cheapest card on the market. If we offer our card at that price or below we're golden. Probably not. The thing is, this card is a level 1 card without any other cards merged into it (xp = 0). But the card I'm using is a level 10 card. We might get lucky and find a couple lvl 10 cards in there, but there's a better way:
Calculating the price per BCS which means calculating the price per single card. If we look at our two cards that would be:
11 XP for the lvl 4 chain golem and 7560 XP for the giant roc. What happened here ? Why does the legendary chain golem only have 11 XP and the giant roc over 7000 ?
That's because from untamed onwards XP is exactly the number of cards which were merged into it. Before that each merged card counted a different amount of XP. Which is also different for alpha, gold, and beta cards. And there's also the edge case of merging alpha cards into beta cards. Then the cards has some alpha xp as well. All in all it's a nightmare to compute it for all.
But I'm going to show you a way to compute BCX for untamed, alpha, beta and gold cards. But I'm going to omit the edge case with alpha xp, keeps the tutorial cleaner. It's not hard to do, just a bit messy.
3. Calculating BCX
First we need the XP tables for alpha, beta and gold cards, splinterlands offers those in the settings endpoint:
def get_settings():
url: str = API2 + "/settings"
return requests.get(url).json()
settings = get_settings()
Now let's calculate the bcx. For that I need a couple helper functions:
def determine_base_xp(rarity: int, xp_table: List[int], alpha: bool, beta: bool,
gold: bool):
return xp_table[rarity - 1] if alpha or beta or gold else 1
def determine_xp_table(rarity: int, alpha: bool, beta: bool, gold: bool):
xp_table: List[int] = []
if alpha:
xp_table = settings["beta_gold_xp"] if gold else settings["alpha_xp"]
elif beta:
xp_table = settings["beta_gold_xp"] if gold else settings["beta_xp"]
else:
xp_table = settings["gold_xp"] if gold else settings["xp_levels"][rarity - 1]
return xp_table
def is_beta(card_detail_id: int, edition: int):
return edition in [1, 2] or (edition == 3 and card_detail_id <= 223)
def is_alpha(edition: int):
return edition == 0
Thats a lot to take in. The two helper fuctions is_beta and is_alpha are pretty much self explanatory. They determine whether a card is an alpha or beta card and therefor uses the legacy xp system. You might have noticed that we include edition 3 cards (reward cards) with a card_detail_id less than 223. Those cards also use the legacy xp system.
determine_xp_table returns the correct xp table and determine_base_xp returns the value we need to compute the actual amount of cards.
One note here: Technically we don't need the modern xp table (xp_levels) because for modern cards xp = BCX therefor we just return 1 for modern cards because for those xp is the same as the amount of cards as the base rate. You'll see why in a moment.
Computing BCX for an actual card:
def calc_bcx(uid: str):
card_item = collection[uid]
detail_id = card_item["card_detail_id"]
edition = card_item["edition"]
gold = card_item["gold"]
xp = card_item["xp"]
rarity: int = card_details[detail_id]["rarity"]
beta: bool = is_beta(detail_id, edition)
alpha: bool = is_alpha(edition)
xp_table = determine_xp_table(rarity, alpha, beta, gold)
xp_base = determine_base_xp(rarity, xp_table, alpha, beta, gold)
add_inital_card: int = 0 if xp_base == 1 or gold else 1
return (xp / xp_base) + add_inital_card
Lets go over the function. First we load all the information we need from our collection by uid. That would be the card_detail_id, card edition, whether its a gold card and the xp. Then we load the rarity of the card from the card_details.
Afterwards we determine whether its an alpha or beta card. Then we determine the correct xp table and the xp base rate.
Then we divide the xp by the "base rate" we determined. For alpha and beta cards we need to add one because the initial card is not included in the xp value. Gold cards seems to have it included.
Any other card editions just get divided by one which doesn't change the value.
Lets see if the function works, we're expecting 505 bcx for the first and 11 for the second:
print(calc_bcx(cards_for_rental[0])
=> 505.0
print(calc_bcx(cards_for_rental[1]))
=> 11.0
Looks good, now we're almost at the interesting part.
4. Computing price per bcx for all rental positions
We can now compute the BCX for our cards. Now we just need to figure out the price per BCX. If you remember the response from earlier we have a uid and a seller name. So we could in theory get the sellers collection and compute it that way. But if you look closely we essentially have all the information we need.
So we can save us this call, let's rewrite the calc_bcx bcs function a bit:
def calc_bcx_uid(uid: str):
card_item = collection[uid]
detail_id = card_item["card_detail_id"]
edition = card_item["edition"]
gold = card_item["gold"]
xp = card_item["xp"]
return calc_bcx(detail_id, edition, gold, xp)
def calc_bcx(detail_id: int, edition: int, gold: bool, xp):
rarity: int = card_details[detail_id]["rarity"]
beta: bool = is_beta(detail_id, edition)
alpha: bool = is_alpha(edition)
xp_table = determine_xp_table(rarity, alpha, beta, gold)
xp_base = determine_base_xp(rarity, xp_table, alpha, beta, gold)
add_card: int = 0 if xp_base == 1 or gold else 1
return (xp / xp_base) + add_card
Now we have a function that can compute the bcx from minimal input and we reused it in the initial function. That saved us redundant code and a couple of api calls.
Now, lets compute the price per bcx for each market listing, choose the cheapest and compute our cards value:
def calc_price_per_bcx(uid: str):
result = get_rentals_by_uid(uid)
price_per_bcx = []
for entry in result:
per_bcx = float(entry["buy_price"]) / calc_bcx(entry["card_detail_id"],
entry["edition"],
entry["gold"],
entry["xp"])
price_per_bcx.append(per_bcx)
price_per_bcx.sort(key=lambda x: x, reverse=False)
return price_per_bcx
Now we computed the price per bcx for each position. Just by dividing the buying price by the bcx of the card.
print(calc_price_per_bcx(cards_for_rental[0])[0])
Since the list is already sorted in ascending order we can just take the first value:
0.05584158415841584
Lets compute the price of our card:
print(lowest_ppbcx * calc_bcx_uid(cards_for_rental[0]))
=> 28.2 # 0.05584158415841584 * 505
Which tells us that our card is worth 28.2 DEC per day. Lets see how close that is to the cheapest card on the market:
Looks like we're spot on. The cheapest card by DEC/BCX value is 28.2 DEC Per Day. Now we can either pick that price or go a bit lower. That's up to you.
5. Posting, updating and deleting rental listings
We have our price and we have our cards. Now we just have to tell the website or rather the blockchain:
from beem import Hive
hive = Hive(keys=["ACTIVE_KEY","POSTING_KEY"])
Depending on the type of transaction we make we need different blockchain authorities. For deleting and creating rental listings we just need to posting key. For updating the active key is required.
Let's take a look at the transactions needed for creating, updating and deleting:
# Create market listing:
[ [ "custom_json", { "id": "sm_market_list", "json": "{\"cards\":[[\"G1-58-MB64QJHNF4\",149.9],
[\"G1-69-EQGGYGWAAO\",172.45],
[\"G3-89-5I1ZMYRS1C\",148.9],
[\"G4-209-2J82ZH9YXC\",224.65],
[\"G3-213-HN9UCR2M5S\",174.9]],
\"type\":\"rent\",\"fee\":500}", "required_auths": [],
"required_posting_auths": [ "aicu" ] } ] ]
# update price
[ "custom_json", { "id": "sm_update_price", "json": "{\"ids\":[\"b532ae648a69b8fcbbf792848b0dde16af97c729-4\"],
\"new_price\":\"149.244\"}", "required_auths": [ "aicu" ],
"required_posting_auths": [] } ]
#cancel rental
{ "id": "sm_market_cancel_rental",
"json": "{\"items\":[\"0ce7b01f1c95ba5229a420c8e719bfb7ff1b2370-23\"],
As you can see the structure for update and rental are pretty straight forward. But creating a market listing is a bit ugly. We can either stitch the needed json together or we use the inherent dictionary of python classes which are very similar to json.
Sounds complicated but its rather easy once you wrap your head around it.
I've contemplated using this technique in this tutorial but I think it makes things a lot easier to use and at the same time teaches a interesting technique.
To keep things orderly I recommend that you create a new python file. And place the following classes inside:
from typing import List, Tuple, Dict
class MarketListing:
cards: List[List[any]]
type: str
fee: int
"""
@param order_type: type of the order rent, sell etc
@params order_fee: fee of the order taken by the marketplace, integer e.g. 500 = 5% .
@param orders: List of Tuples where the first argument is the uid and the second the price in DEC
"""
def __init__(self, order_type: str, order_fee: int, orders: List[Tuple[str, float]]):
if orders:
self.cards = []
for order in orders:
self.cards.append([order[0], order[1]])
self.type = order_type
self.fee = order_fee
class MarketUpdatePrice:
class UpdatePriceItem:
ids: List[str]
new_price: float
def __init__(self, price: float, market_id: str = None):
self.ids = []
if market_id:
self.ids.append(market_id)
self.new_price = price
def append_id(self, market_id: str):
if market_id:
self.ids.append(market_id)
orders: Dict[float, UpdatePriceItem] = {}
"""
@param orders: List of Tuples where the first argument is the market id and the second the updated sprice in DEC
"""
def __init__(self, orders: List[Tuple[str, float]]):
if orders:
for order in orders:
order_entry = self.orders.get(order[1], self.UpdatePriceItem(order[1]))
order_entry.append_id(order[0])
self.orders[order[1]] = order_entry
class CancelRental:
items: List[str]
def __init__(self, items: List[str]):
self.items = items
We now have three classes representing the three market operations.
CancelRental is pretty self explanatory. Its just a list of strings. Market ids in this case.
MarketUpdatePrice has some more logic build in. From the looks of it you can update the price for multiple market ids at once. So what this class does is take a list of tuples of market id and price and groups them in an internal object. Those objects mirror the json structure for a update price operation. I'll show you how to use it shortly after.
MarketListing mirrors exactly the structure of the sm_market_list operation. Turning a list of tuples, order type and fee into the required json structure.
Lets see how we use the new classes in our hive operation calls:
def create_listing(order_type: str, order_fee: int, orders: List[Tuple[str, float]]):
listing: MarketListing = MarketListing(order_type, order_fee, orders)
data = listing.__dict__
hive.custom_json("sm_market_list", json_data=data,
required_posting_auths=["your_user"])
def update_prices(orders: List[Tuple[str, float]]):
update_price: MarketUpdatePrice = MarketUpdatePrice(orders)
for order in update_price.orders.values():
data = order.__dict__
hive.custom_json("sm_update_price", json_data=data,
required_auths=["your_user"])
def cancel_rental(rentals: List[str]):
rentals = CancelRental(rentals)
data = rentals.__dict__
hive.custom_json("sm_market_cancel_rental", json_data=data,
required_posting_auths=["your_user"])
As you can see a "simple" call of "__dict__" on the class turns it into a dictionary which is for our use case good enough.
NOTE: Be careful if you plan on using this technique with booleans. They are serialized in python style meaning as True and False. Javascript and the splinterlands backend expects booleans in lowercase.
Now we have all the parts we need. Lets determine new prices for the flying roc and chain golem and submit it:
prices_for_update = []
new_listings = []
for uid in cards_for_rental:
card = collection[uid]
lowest_ppbcx = calc_price_per_bcx(uid)[0]
new_price = lowest_ppbcx * calc_bcx_uid(uid)
market_id = card["market_id"]
if market_id and not card["delegated_to"]:
prices_for_update.append((market_id, max(0.1, new_price)))
print("adding updated price for ", uid, market_id, str(new_price))
if not market_id:
new_listings.append((uid, max(0.1, new_price)))
print("creating new listing for ", uid, str(new_price))
adding updated price for C4-209-F0UTJ8IC5S 5f2f2049bb2d5955fc32983c79039e3f77f144ec-24 72.85
[('5f2f2049bb2d5955fc32983c79039e3f77f144ec-24', 72.85)]
[]
In this loop we check all our cards and add all that are either listed and not rented or not listed at all to separate lists.
And looks like while I was typing this tutorial someone rented my giant roc. So no need to update to a lower price.
But the chain golem is still not delegated. Let's update the price from the old price of 73.300 to 72.85.
if prices_for_update:
update_prices(prices_for_update)
if new_listings:
create_listing('rent', 500, new_listings)
WARNING: don't use a upper case rent here. Splinterlands will interpret that as a normal sell operation.
NOTE the second parameter of 500 is the fee in percent. 500 equals a fee of 5%. If you want your rental to show up on the official splinterlands you need to use 5%. You can check the current "official" fee in the settings endpoint which we loaded earlier. You can look it up with the key "market_fee".
And voila, it worked. The corresponding hive transaction can be found here:
https://www.hiveblockexplorer.com/tx/83ac734f8fb04973215aa0c5bcd492a84bd61992
Now you have all the tools to load your card informations, get current rental prices, compute the price per bcx and update the price on splinterlands. If you're interested how to do scheduling in python and make it check every couple hours take a look at the library APScheduler.
Bonus - Posting only authority solution
And as a bonus:
prices_for_update = []
orders_for_deletion = []
new_listings = []
for uid in cards_for_rental:
card = collection[uid]
lowest_ppbcx = calc_price_per_bcx(uid)[0]
new_price = lowest_ppbcx * calc_bcx_uid(uid)
print(card)
market_id = card["market_id"]
if market_id and not card["delegated_to"]:
orders_for_deletion.append(market_id)
prices_for_update.append((uid, max(0.1, new_price)))
print("creating new listing for ", uid, market_id, str(new_price))
if not market_id:
new_listings.append((uid, max(0.1, new_price)))
print("creating new listing for ", uid, str(new_price))
print(prices_for_update)
print(new_listings)
print(orders_for_deletion)
if prices_for_update:
cancel_rental(orders_for_deletion)
time.sleep(5)
create_listing('rent', 500, prices_for_update)
if new_listings:
create_listing('rent', 500, new_listings)
This version doesn't require a active key.
For everyone who just wants the whole solution:
File: MarketHiveTransactions.py
from typing import List, Tuple, Dict
class MarketListing:
cards: List[List[any]]
type: str
fee: int
"""
@param order_type: type of the order rent, sell etc
@params order_fee: fee of the order taken by the marketplace, integer e.g. 500 = 5% .
@param orders: List of Tuples where the first argument is the uid and the second the price in DEC
"""
def __init__(self, order_type: str, order_fee: int, orders: List[Tuple[str, float]]):
if orders:
self.cards = []
for order in orders:
self.cards.append([order[0], order[1]])
self.type = order_type
self.fee = order_fee
class MarketUpdatePrice:
class UpdatePriceItem:
ids: List[str]
new_price: float
def __init__(self, price: float, market_id: str = None):
self.ids = []
if market_id:
self.ids.append(market_id)
self.new_price = price
def append_id(self, market_id: str):
if market_id:
self.ids.append(market_id)
orders: Dict[float, UpdatePriceItem] = {}
"""
@param orders: List of Tuples where the first argument is the market id and the second the updated sprice in DEC
"""
def __init__(self, orders: List[Tuple[str, float]]):
if orders:
for order in orders:
order_entry = self.orders.get(order[1], self.UpdatePriceItem(order[1]))
order_entry.append_id(order[0])
self.orders[order[1]] = order_entry
class CancelRental:
items: List[str]
def __init__(self, items: List[str]):
self.items = items
File: main.py
import time
from typing import Dict, List, Tuple
import requests
from beem import Hive
from MarketHiveTransactions import MarketUpdatePrice, MarketListing, CancelRental
API2 = "https://api2.splinterlands.com"
def get_card_collection(player):
url: str = API2 + "/cards/collection/" + player
return requests.get(url).json()["cards"]
def get_card_details():
url: str = API2 + "/cards/get_details"
return requests.get(url).json()
collection: Dict[str, dict] = {}
card_details: Dict[int, dict] = {}
collection_resp = get_card_collection("aicu")
details_resp = get_card_details()
for card in collection_resp:
collection[card["uid"]] = card
for detail in details_resp:
card_details[detail["id"]] = detail
def get_rental_listings(card_id: int, edition: int, gold: bool = False):
url: str = API2 + "/market/for_rent_by_card"
request: dict = {"card_detail_id": card_id,
"gold": str(gold).lower(),
"edition": edition}
return requests.get(url, params=request).json()
def get_rentals_by_uid(uid: str):
uid_card = collection.get(uid)
return get_rental_listings(uid_card["card_detail_id"],
uid_card["edition"],
uid_card["gold"])
def get_settings():
url: str = API2 + "/settings"
return requests.get(url).json()
settings = get_settings()
def determine_base_xp(rarity: int, xp_table: List[int], alpha: bool, beta: bool,
gold: bool):
return xp_table[rarity - 1] if alpha or beta or gold else 1
def determine_xp_table(rarity: int, alpha: bool, beta: bool, gold: bool):
xp_table: List[int] = []
if alpha:
xp_table = settings["beta_gold_xp"] if gold else settings["alpha_xp"]
elif beta:
xp_table = settings["beta_gold_xp"] if gold else settings["beta_xp"]
else:
xp_table = settings["gold_xp"] if gold else settings["xp_levels"][rarity - 1]
return xp_table
def is_beta(card_detail_id: int, edition: int):
return edition in [1, 2] or (edition == 3 and card_detail_id <= 223)
def is_alpha(edition: int):
return edition == 0
def calc_bcx_uid(uid: str):
card_item = collection[uid]
detail_id = card_item["card_detail_id"]
edition = card_item["edition"]
gold = card_item["gold"]
xp = card_item["xp"]
return calc_bcx(detail_id, edition, gold, xp)
def calc_bcx(detail_id: int, edition: int, gold: bool, xp):
rarity: int = card_details[detail_id]["rarity"]
beta: bool = is_beta(detail_id, edition)
alpha: bool = is_alpha(edition)
xp_table = determine_xp_table(rarity, alpha, beta, gold)
xp_base = determine_base_xp(rarity, xp_table, alpha, beta, gold)
add_card: int = 0 if xp_base == 1 or gold else 1
return (xp / xp_base) + add_card # +1 because the initial card doesn't get counted in xp
def calc_price_per_bcx(uid: str):
result = get_rentals_by_uid(uid)
price_per_bcx = []
for entry in result:
per_bcx = float(entry["buy_price"]) / calc_bcx(entry["card_detail_id"],
entry["edition"],
entry["gold"],
entry["xp"])
price_per_bcx.append(per_bcx)
price_per_bcx.sort(key=lambda x: x, reverse=False)
return price_per_bcx
hive = Hive(keys=["PRIVATE_POSTING", "PRIVAT_ACTIVE"])
def create_listing(order_type: str, order_fee: int, orders: List[Tuple[str, float]]):
listing: MarketListing = MarketListing(order_type, order_fee, orders)
data = listing.__dict__
hive.custom_json("sm_market_list", json_data=data,
required_posting_auths=["your_username"])
def update_prices(orders: List[Tuple[str, float]]):
update_price: MarketUpdatePrice = MarketUpdatePrice(orders)
for order in update_price.orders.values():
data = order.__dict__
hive.custom_json("sm_update_price", json_data=data,
required_auths=["your_username"])
def cancel_rental(rentals: List[str]):
rentals = CancelRental(rentals)
data = rentals.__dict__
hive.custom_json("sm_market_cancel_rental", json_data=data,
required_posting_auths=["your_username"])
prices_for_update = []
new_listings = []
cards_for_rental = ['C1-2-M57QKW5M68', 'C4-209-F0UTJ8IC5S']
for uid in cards_for_rental:
card = collection[uid]
lowest_ppbcx = calc_price_per_bcx(uid)[0]
new_price = lowest_ppbcx * calc_bcx_uid(uid)
print(card)
market_id = card["market_id"]
if market_id and not card["delegated_to"]:
prices_for_update.append((market_id, max(0.1, new_price)))
print("adding updated price for ", uid, market_id, str(new_price))
if not market_id:
new_listings.append((uid, max(0.1,new_price)))
print("creating new listing for ", uid, str(new_price))
print(prices_for_update)
print(new_listings)
if prices_for_update:
update_prices(prices_for_update)
if new_listings:
create_listing('rent', 500, new_listings)
That's it. Thanks for reading all the way until here. The tutorial is quite the wall of text. And thank you to @epicraptor (SplinterForge.io) and @furryboffin from the discord ( I'm not sure if i got those users right).
And like always a small giveaway. Since we've talked so much about chain golems I'm going to giveaway two chain golem lvl 1. Just comment your splinterlands account below. Winners will be drawn at random after 7 days:
Chain golem lvl 1 C4-209-60U68UYTTC
Chain golem lvl 1 C4-209-SGNHZJWN00