mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 15:08:25 +00:00
Merge remote-tracking branch 'upstream/main' into camera_entropy
This commit is contained in:
@@ -56,22 +56,19 @@ https://youtu.be/aIIc2DiZYcI
|
||||
* Only valid input letters appear during seed word entry (time-saver!)
|
||||
|
||||
### Considerations:
|
||||
* Built for compatibility with Specter v1.1.0 and up (support for other wallet coordinators coming)
|
||||
* Built for compatibility with Specter-desktop, Sparrow and BlueWallet Vaults
|
||||
* Current release takes ~50 seconds to boot before menu appears (be patient!)
|
||||
* Use Specter's "other" device type when adding as a new signing device
|
||||
* ALWAYS opt to use animated QR codes in Specter-desktop
|
||||
* Always test your setup before transfering larger amounts of bitcoin (try testnet first!)
|
||||
* Currently ONLY generating Native Segwit Multisig Xpubs
|
||||
* Scanning animated QRs into a PC is tricky, be aware of ambient light, glare and focus
|
||||
* (Holding the screen upside-down or at a slight angle can significantly reduce glare)
|
||||
* Display text is small; comes with 240x240 pixel territory but ever trying to optimize
|
||||
* Check out our "seedsigner" telegram community for community help / feedback: (https://t.me/joinchat/GHNuc_nhNQjLPWsS)
|
||||
* Check out our "seedsigner" telegram group for community help / feedback: (https://t.me/joinchat/GHNuc_nhNQjLPWsS)
|
||||
* If you think SeedSigner adds value to the Bitcoin ecosystem, please help me spread the word! (tweets, pics, videos, etc.)
|
||||
|
||||
### Planned Upcoming Improvements / Functionality:
|
||||
* Support for Sparrow & Bluewallet multisig coordinators!
|
||||
* Support for single-signature XPUB generation / signing
|
||||
* Manually step through QR sequences using button/joystick controls
|
||||
* Support for BIP39 passphrases
|
||||
* User-guided manual QR transcription to quickly input seed phrases
|
||||
* Low/Med/High customizable QR density
|
||||
* Other optimizations based on user feedback!
|
||||
|
||||
---------------
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
embit==0.4.2
|
||||
embit==0.4.3
|
||||
picamera==1.13
|
||||
Pillow==8.2.0
|
||||
pyzbar==0.1.8
|
||||
qrcode==6.1
|
||||
RPi.GPIO==0.7.0
|
||||
six==1.16.0
|
||||
spidev==3.5
|
||||
spidev==3.5
|
||||
|
||||
@@ -554,11 +554,19 @@ class Controller:
|
||||
if raw_pbst == "nodata":
|
||||
return Path.MAIN_MENU
|
||||
if raw_pbst == "invalid":
|
||||
self.menu_view.draw_modal(["QR Format Unexpected", "Check Wallet in Settings"], "", "RIGHT to EXIT")
|
||||
self.menu_view.draw_modal(["QR Format Unexpected", "Check Wallet in Settings"], "", "Right to Exit")
|
||||
input = self.buttons.wait_for([B.KEY_RIGHT])
|
||||
return Path.MAIN_MENU
|
||||
if raw_pbst == "invalidpsbt":
|
||||
self.menu_view.draw_modal(["PSBT UR 2.0 Decoding Error", "try again"], "", "Right to Exit")
|
||||
input = self.buttons.wait_for([B.KEY_RIGHT])
|
||||
return Path.MAIN_MENU
|
||||
self.menu_view.draw_modal(["Parsing PSBT ..."])
|
||||
self.wallet.parse_psbt(raw_pbst)
|
||||
parse_status = self.wallet.parse_psbt(raw_pbst)
|
||||
if parse_status == False:
|
||||
self.menu_view.draw_modal(["PSBT Parsing Failed"], "", "Right to Exit")
|
||||
input = self.buttons.wait_for([B.KEY_RIGHT])
|
||||
return Path.MAIN_MENU
|
||||
|
||||
# show transaction information before sign
|
||||
self.signing_tools_view.display_transaction_information(self.wallet)
|
||||
|
||||
@@ -124,7 +124,7 @@ class BlueWallet(Wallet):
|
||||
def set_qr_density(self, density):
|
||||
self.cur_qr_density = density
|
||||
if density == Wallet.QRLOW:
|
||||
self.qrsize = 70
|
||||
self.qrsize = 20
|
||||
elif density == Wallet.QRMEDIUM:
|
||||
self.qrsize = 100
|
||||
elif density == Wallet.QRHIGH:
|
||||
|
||||
@@ -102,9 +102,9 @@ class GenericUR2Wallet(Wallet):
|
||||
if not self.capture_complete():
|
||||
View.draw.rectangle((0, 0, View.canvas_width, View.canvas_height), outline=0, fill=0)
|
||||
tw, th = View.draw.textsize("Collecting QR Codes:", font=View.IMPACT25)
|
||||
View.draw.text(((240 - tw) / 2, 15), "Collecting QR Codes:", fill="ORANGE", font=View.IMPACT25)
|
||||
View.draw.text(((240 - tw) / 2, 15), "Collecting QR Codes:", fill=View.color, font=View.IMPACT25)
|
||||
tw, th = View.draw.textsize(str(round(self.percentage_complete * 100)) + "% Complete", font=View.IMPACT22)
|
||||
View.draw.text(((240 - tw) / 2, 125), str(round(self.percentage_complete * 100)) + "% Complete", fill="ORANGE", font=View.IMPACT22)
|
||||
View.draw.text(((240 - tw) / 2, 125), str(round(self.percentage_complete * 100)) + "% Complete", fill=View.color, font=View.IMPACT22)
|
||||
View.DispShowImage()
|
||||
|
||||
elif self.scan_started_ind == 0:
|
||||
|
||||
@@ -34,10 +34,12 @@ class SparrowWallet(Wallet):
|
||||
return "Sparrow"
|
||||
|
||||
def parse_psbt(self, raw_psbt) -> bool:
|
||||
base64_psbt = a2b_base64(raw_psbt)
|
||||
self.tx = psbt.PSBT.parse(base64_psbt)
|
||||
|
||||
self._parse_psbt()
|
||||
try:
|
||||
base64_psbt = a2b_base64(raw_psbt)
|
||||
self.tx = psbt.PSBT.parse(base64_psbt)
|
||||
self._parse_psbt()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -103,7 +105,11 @@ class SparrowWallet(Wallet):
|
||||
self.qr_data = ["empty"]
|
||||
|
||||
# get data and percentage
|
||||
self.ur_decoder.receive_part(data[0])
|
||||
decoder_work_check = self.ur_decoder.receive_part(data[0])
|
||||
if decoder_work_check == False:
|
||||
self.qr_data = ["invalidpsbt"]
|
||||
self.buttons.trigger_override() # something went wrong, invalid QR
|
||||
return
|
||||
self.percentage_complete = self.ur_decoder.estimated_percent_complete()
|
||||
|
||||
# checking if all frames has been captured, exit camera processing
|
||||
@@ -127,7 +133,9 @@ class SparrowWallet(Wallet):
|
||||
|
||||
def total_frames_parse(data) -> int:
|
||||
if re.search("^UR\:CRYPTO-PSBT\/(\d+)\-(\d+)\/", data, re.IGNORECASE) != None:
|
||||
return 10 #valid
|
||||
return 10 # valid
|
||||
elif re.search("^UR\:CRYPTO-PSBT", data, re.IGNORECASE) != None:
|
||||
return 1 # valid but only 1 segment
|
||||
else:
|
||||
return -1 #invalid
|
||||
|
||||
|
||||
@@ -21,11 +21,12 @@ class SpecterDesktopWallet(Wallet):
|
||||
return "Specter Desktop"
|
||||
|
||||
def parse_psbt(self, raw_psbt) -> bool:
|
||||
|
||||
base64_psbt = a2b_base64(raw_psbt)
|
||||
self.tx = psbt.PSBT.parse(base64_psbt)
|
||||
|
||||
self._parse_psbt()
|
||||
try:
|
||||
base64_psbt = a2b_base64(raw_psbt)
|
||||
self.tx = psbt.PSBT.parse(base64_psbt)
|
||||
self._parse_psbt()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -54,14 +55,18 @@ class SpecterDesktopWallet(Wallet):
|
||||
def total_frames_parse(data) -> int:
|
||||
if re.search("^p(\d+)of(\d+) ", data, re.IGNORECASE) != None:
|
||||
return int(re.search("^p(\d+)of(\d+) ", data, re.IGNORECASE).group(2))
|
||||
elif re.search("UR\:.*", data, re.IGNORECASE) != None:
|
||||
return -1 # if data has UR:, assume invalid wallet selected
|
||||
else:
|
||||
return -1
|
||||
return 1
|
||||
|
||||
def current_frame_parse(data) -> int:
|
||||
if re.search("^p(\d+)of(\d+) ", data, re.IGNORECASE) != None:
|
||||
return int(re.search("^p(\d+)of(\d+) ", data, re.IGNORECASE).group(1))
|
||||
elif re.search("UR\:.*", data, re.IGNORECASE) != None:
|
||||
return -1 # if data has UR:, assume invalid wallet selected
|
||||
else:
|
||||
return -1
|
||||
return 1 # assume a single frame for specter if no p#of# is found.
|
||||
|
||||
def data_parse(data) -> str:
|
||||
return data.split(" ")[-1].strip()
|
||||
@@ -96,7 +101,12 @@ class SpecterDesktopWallet(Wallet):
|
||||
stop = self.qrsize
|
||||
qr_cnt = ((len(data)-1) // self.qrsize) + 1
|
||||
|
||||
while cnt < qr_cnt:
|
||||
if qr_cnt == 1:
|
||||
part = data[start:stop]
|
||||
images.append(qr.qrimage(part))
|
||||
print(part)
|
||||
|
||||
while cnt < qr_cnt and qr_cnt != 1:
|
||||
part = "p" + str(cnt+1) + "of" + str(qr_cnt) + " " + data[start:stop]
|
||||
images.append(qr.qrimage(part))
|
||||
print(part)
|
||||
|
||||
@@ -805,33 +805,33 @@ class SeedToolsView(View):
|
||||
# Screen Title
|
||||
View.draw.rectangle((0, 0, View.canvas_width, View.canvas_height), outline=0, fill=0)
|
||||
tw, th = View.draw.textsize("Add Passphrase", font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 2), "Add Passphrase", fill="ORANGE", font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 2), "Add Passphrase", fill=View.color, font=View.IMPACT18)
|
||||
|
||||
# Screen Botton
|
||||
tw, th = View.draw.textsize("use joystick to add and", font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 197), "use joystick to add and", fill="ORANGE", font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 197), "use joystick to add and", fill=View.color, font=View.IMPACT18)
|
||||
tw, th = View.draw.textsize("remove passphrase characters", font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 217), "remove passphrase characters", fill="ORANGE", font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 217), "remove passphrase characters", fill=View.color, font=View.IMPACT18)
|
||||
|
||||
# Display passphrase selection in progress
|
||||
View.draw.text((5,33), "Phrase:", fill="ORANGE", font=View.IMPACT20)
|
||||
View.draw.text((72,35), self.passphrase[:14] + "▒", fill="ORANGE", font=View.COURIERNEW20)
|
||||
View.draw.text((5,33), "Phrase:", fill=View.color, font=View.IMPACT20)
|
||||
View.draw.text((72,35), self.passphrase[:14] + "▒", fill=View.color, font=View.COURIERNEW20)
|
||||
|
||||
if len(self.passphrase) >= 14:
|
||||
View.draw.text((72,50), self.passphrase[14:] + "▒", fill="ORANGE", font=View.COURIERNEW20)
|
||||
View.draw.text((72,50), self.passphrase[14:] + "▒", fill=View.color, font=View.COURIERNEW20)
|
||||
|
||||
# Display message when max passphrase length reached
|
||||
if len(self.passphrase) >= 28:
|
||||
View.draw.text((50,70), "Max passphrase of 28", fill="ORANGE", font=View.IMPACT18)
|
||||
View.draw.text((53,90), "characters reached", fill="ORANGE", font=View.IMPACT18)
|
||||
View.draw.text((50,70), "Max passphrase of 28", fill=View.color, font=View.IMPACT18)
|
||||
View.draw.text((53,90), "characters reached", fill=View.color, font=View.IMPACT18)
|
||||
|
||||
# Save Button
|
||||
c_x_offset = 240 - View.IMPACT25.getsize("Exit")[0]
|
||||
View.draw.text((c_x_offset , 172), "Exit", fill="ORANGE", font=View.IMPACT22)
|
||||
View.draw.text((c_x_offset , 172), "Exit", fill=View.color, font=View.IMPACT22)
|
||||
|
||||
# Toglle Button
|
||||
x = 240 - View.IMPACT20.getsize(self.pass_case_toggle)[0]
|
||||
View.draw.text((x, 110), self.pass_case_toggle, fill="ORANGE", font=View.IMPACT20)
|
||||
View.draw.text((x, 110), self.pass_case_toggle, fill=View.color, font=View.IMPACT20)
|
||||
|
||||
# draw letter and arrows
|
||||
if self.pass_case_toggle == "lower":
|
||||
@@ -842,17 +842,15 @@ class SeedToolsView(View):
|
||||
pass_letter_disp = self.pass_letter
|
||||
|
||||
tw, th = View.draw.textsize(pass_letter_disp, font=View.IMPACT35)
|
||||
View.draw.text((((30-tw)/2), 112), pass_letter_disp, fill="ORANGE", font=View.IMPACT35)
|
||||
View.draw.polygon([(8, 105) , (14, 89) , (20, 105 )], outline="ORANGE", fill="BLACK")
|
||||
View.draw.polygon([(8, 168), (14, 184), (20, 168)], outline="ORANGE", fill="BLACK")
|
||||
View.draw.text((((30-tw)/2), 112), pass_letter_disp, fill=View.color, font=View.IMPACT35)
|
||||
View.draw.polygon([(8, 105) , (14, 89) , (20, 105 )], outline=View.color, fill="BLACK")
|
||||
View.draw.polygon([(8, 168), (14, 184), (20, 168)], outline=View.color, fill="BLACK")
|
||||
|
||||
View.DispShowImage()
|
||||
|
||||
return
|
||||
|
||||
def gather_passphrase_up(self):
|
||||
# View.draw.polygon([(8, 105) , (14, 89) , (20, 105 )], outline="ORANGE", fill="ORANGE")
|
||||
# View.DispShowImage()
|
||||
|
||||
pass_choice = self.get_pass_value_options()
|
||||
|
||||
@@ -863,8 +861,6 @@ class SeedToolsView(View):
|
||||
self.pass_letter = pass_choice[idx-1]
|
||||
|
||||
def gather_passphrase_down(self):
|
||||
# View.draw.polygon([(8, 168) , (14, 184) , (20, 168 )], outline="ORANGE", fill="ORANGE")
|
||||
# View.DispShowImage()
|
||||
|
||||
pass_choice = self.get_pass_value_options()
|
||||
|
||||
@@ -925,7 +921,10 @@ class SeedToolsView(View):
|
||||
self.draw.text(((240 - tw) / 2, 60), "The final word is :", fill=View.color, font=View.IMPACT23)
|
||||
tw, th = self.draw.textsize(last_word, font=View.IMPACT50)
|
||||
self.draw.text(((240 - tw) / 2, 90), last_word, fill=View.color, font=View.IMPACT50)
|
||||
self.draw.text((73, 210), "Right to Continue", fill=View.color, font=View.IMPACT18)
|
||||
|
||||
tw, th = View.draw.textsize("Right to Continue", font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 210), "Right to Continue", fill=View.color, font=View.IMPACT18)
|
||||
|
||||
View.DispShowImage()
|
||||
|
||||
input = self.buttons.wait_for([B.KEY_RIGHT])
|
||||
@@ -1307,14 +1306,14 @@ class SeedToolsView(View):
|
||||
draw.rectangle((View.canvas_width - mask_width - 1, mask_height, View.canvas_width, View.canvas_height - mask_height), fill=mask_rgba)
|
||||
|
||||
# Draw a box around the cutout portion of the mask for better visibility
|
||||
draw.line((mask_width, mask_height, mask_width, View.canvas_height - mask_height), fill="ORANGE")
|
||||
draw.line((View.canvas_width - mask_width, mask_height, View.canvas_width - mask_width, View.canvas_height - mask_height), fill="ORANGE")
|
||||
draw.line((mask_width, mask_height, View.canvas_width - mask_width, mask_height), fill="ORANGE")
|
||||
draw.line((mask_width, View.canvas_height - mask_height, View.canvas_width - mask_width, View.canvas_height - mask_height), fill="ORANGE")
|
||||
draw.line((mask_width, mask_height, mask_width, View.canvas_height - mask_height), fill=View.color)
|
||||
draw.line((View.canvas_width - mask_width, mask_height, View.canvas_width - mask_width, View.canvas_height - mask_height), fill=View.color)
|
||||
draw.line((mask_width, mask_height, View.canvas_width - mask_width, mask_height), fill=View.color)
|
||||
draw.line((mask_width, View.canvas_height - mask_height, View.canvas_width - mask_width, View.canvas_height - mask_height), fill=View.color)
|
||||
|
||||
msg = "click to exit"
|
||||
tw, th = draw.textsize(msg, font=View.IMPACT18)
|
||||
draw.text(((View.canvas_width - tw) / 2, View.canvas_height - th - 2), msg, fill="ORANGE", font=View.IMPACT18)
|
||||
draw.text(((View.canvas_width - tw) / 2, View.canvas_height - th - 2), msg, fill=View.color, font=View.IMPACT18)
|
||||
|
||||
def draw_block_labels(cur_block_x, cur_block_y):
|
||||
# Create overlay for block labels (e.g. "D-5")
|
||||
@@ -1323,8 +1322,8 @@ class SeedToolsView(View):
|
||||
|
||||
block_labels = Image.new("RGBA", (View.canvas_width, View.canvas_height), (255,255,255,0))
|
||||
draw = ImageDraw.Draw(block_labels)
|
||||
draw.rectangle((mask_width, 0, View.canvas_width - mask_width, pixels_per_block), fill="ORANGE")
|
||||
draw.rectangle((0, mask_height, pixels_per_block, View.canvas_height - mask_height), fill="ORANGE")
|
||||
draw.rectangle((mask_width, 0, View.canvas_width - mask_width, pixels_per_block), fill=View.color)
|
||||
draw.rectangle((0, mask_height, pixels_per_block, View.canvas_height - mask_height), fill=View.color)
|
||||
|
||||
label_font = View.COURIERNEW24
|
||||
x_label = block_labels_x[cur_block_x]
|
||||
|
||||
@@ -96,22 +96,13 @@ class SettingsToolsView(View):
|
||||
|
||||
line1 = "SeedSigner"
|
||||
line2 = "Version v" + self.controller.VERSION
|
||||
line3 = "built for use with"
|
||||
line4 = "Specter-desktop"
|
||||
line5 = "v1.1.0 or higher"
|
||||
line6 = "(Joystick RIGHT to EXIT)"
|
||||
line3 = "(Joystick RIGHT to EXIT)"
|
||||
|
||||
View.draw.rectangle((0, 0, View.canvas_width, View.canvas_height), outline=0, fill=0)
|
||||
tw, th = View.draw.textsize(line1, font=View.IMPACT22)
|
||||
View.draw.text(((240 - tw) / 2, 20), line1, fill=View.color, font=View.IMPACT22)
|
||||
tw, th = View.draw.textsize(line2, font=View.IMPACT22)
|
||||
View.draw.text(((240 - tw) / 2, 55), line2, fill=View.color, font=View.IMPACT22)
|
||||
tw, th = View.draw.textsize(line3, font=View.IMPACT22)
|
||||
View.draw.text(((240 - tw) / 2, 90), line3, fill=View.color, font=View.IMPACT22)
|
||||
tw, th = View.draw.textsize(line4, font=View.IMPACT22)
|
||||
View.draw.text(((240 - tw) / 2, 125), line4, fill=View.color, font=View.IMPACT22)
|
||||
tw, th = View.draw.textsize(line5, font=View.IMPACT22)
|
||||
View.draw.text(((240 - tw) / 2, 160), line5, fill=View.color, font=View.IMPACT22)
|
||||
tw, th = View.draw.textsize(line6, font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 210), line6, fill=View.color, font=View.IMPACT18)
|
||||
View.DispShowImage()
|
||||
tw, th = View.draw.textsize(line3, font=View.IMPACT18)
|
||||
View.draw.text(((240 - tw) / 2, 210), line3, fill=View.color, font=View.IMPACT18)
|
||||
View.DispShowImage()
|
||||
|
||||
@@ -109,14 +109,14 @@ class SigningToolsView(View):
|
||||
self.draw.rectangle((0, 0, View.canvas_width, View.canvas_height), outline=0, fill=0)
|
||||
|
||||
tw, th = self.draw.textsize("Confirm Tx Details", font=View.IMPACT25)
|
||||
self.draw.text(((240 - tw) / 2, 3), "Confirm Tx Details", fill="ORANGE", font=View.IMPACT25)
|
||||
self.draw.text(((240 - tw) / 2, 3), "Confirm Tx Details", fill=View.color, font=View.IMPACT25)
|
||||
|
||||
in_fee_outs_str = str(wallet.ins)
|
||||
in_fee_outs_str += " inputs - fee = " if wallet.ins > 1 else " input - fee = "
|
||||
in_fee_outs_str += str(wallet.outs)
|
||||
in_fee_outs_str += " outs" if wallet.outs > 1 else " out"
|
||||
tw, th = self.draw.textsize(in_fee_outs_str, font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 40), in_fee_outs_str, fill="ORANGE", font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 40), in_fee_outs_str, fill=View.color, font=View.IMPACT22)
|
||||
|
||||
receiving_addr_str1 = ""
|
||||
receiving_addr_str2 = ""
|
||||
@@ -130,32 +130,32 @@ class SigningToolsView(View):
|
||||
receiving_addr_str1 += "Self-Transfer"
|
||||
|
||||
tw, th = self.draw.textsize(receiving_addr_str1, font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 75), receiving_addr_str1, fill="ORANGE", font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 75), receiving_addr_str1, fill=View.color, font=View.IMPACT22)
|
||||
if len(receiving_addr_str2) > 0:
|
||||
tw, th = self.draw.textsize(receiving_addr_str2, font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 100), receiving_addr_str2, fill="ORANGE", font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 100), receiving_addr_str2, fill=View.color, font=View.IMPACT22)
|
||||
|
||||
|
||||
if wallet.spend > 0:
|
||||
spending_str = "Spend: " + str(wallet.spend) + " sats"
|
||||
tw, th = self.draw.textsize(spending_str, font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 130), spending_str, fill="ORANGE", font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 130), spending_str, fill=View.color, font=View.IMPACT22)
|
||||
|
||||
if wallet.change > 0 and wallet.dest_addr_cnt == 0:
|
||||
change_str = "Amount: " + str(int(wallet.change)) + " sats"
|
||||
tw, th = self.draw.textsize(change_str, font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 155), change_str, fill="ORANGE", font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 155), change_str, fill=View.color, font=View.IMPACT22)
|
||||
elif wallet.change > 0:
|
||||
change_str = "Change: " + str(int(wallet.change)) + " sats"
|
||||
tw, th = self.draw.textsize(change_str, font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 155), change_str, fill="ORANGE", font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 155), change_str, fill=View.color, font=View.IMPACT22)
|
||||
|
||||
fee_str = "Fee: " + str(int(wallet.fee)) + " sats"
|
||||
tw, th = self.draw.textsize(fee_str, font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 180), fee_str, fill="ORANGE", font=View.IMPACT22)
|
||||
self.draw.text(((240 - tw) / 2, 180), fee_str, fill=View.color, font=View.IMPACT22)
|
||||
|
||||
tw, th = self.draw.textsize("Left to Exit, Right to Continue", font=View.IMPACT18)
|
||||
self.draw.text(((240 - tw) / 2, 215), "Left to Exit, Right to Continue", fill="ORANGE", font=View.IMPACT18)
|
||||
self.draw.text(((240 - tw) / 2, 215), "Left to Exit, Right to Continue", fill=View.color, font=View.IMPACT18)
|
||||
|
||||
View.DispShowImage()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user