Skip to content

Commit e94ed9d

Browse files
authored
Merge pull request #16 from CodaProtocol/feature/currency-support
Add currency support to library
2 parents 8eb4e9e + 8fd1b19 commit e94ed9d

3 files changed

Lines changed: 129 additions & 5 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*egg-info/
22
build/*
3-
dist/*
3+
dist/*
4+
__pycache__

CodaClient.py

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,89 @@
66
import asyncio
77
import websockets
88
import logging
9+
from enum import Enum
10+
11+
class CurrencyFormat(Enum):
12+
WHOLE = 1
13+
NANO = 2
14+
15+
class CurrencyUnderflow(Exception):
16+
pass
17+
18+
class Currency():
19+
@classmethod
20+
def __nanocodas_from_int(_cls, n):
21+
return n * 1000000000
22+
23+
@classmethod
24+
def __nanocodas_from_string(_cls, s):
25+
segments = s.split('.')
26+
if len(segments) == 1:
27+
return int(segments[0])
28+
elif len(segments) == 2:
29+
[l, r] = segments
30+
if len(r) <= 9:
31+
return int(l + r + ('0' * (9 - len(r))))
32+
else:
33+
raise Exception('invalid coda currency format: %s' % s)
34+
35+
def __init__(self, value, format=CurrencyFormat.WHOLE):
36+
if format == CurrencyFormat.WHOLE:
37+
if isinstance(value, int):
38+
self.__nanocodas = Currency.__nanocodas_from_int(value)
39+
elif isinstance(value, float):
40+
self.__nanocodas = Currency.__nanocodas_from_string(str(value))
41+
elif isinstance(value, str):
42+
self.__nanocodas = Currency.__nanocodas_from_string(value)
43+
else:
44+
raise Exception('cannot construct whole Currency from %s' % type(value))
45+
elif format == CurrencyFormat.NANO:
46+
if isinstance(value, int):
47+
self.__nanocodas = value
48+
else:
49+
raise Exception('cannot construct nano Currency from %s' % type(value))
50+
else:
51+
raise Exception('invalid Currency format %s' % format)
52+
53+
def decimal_format(self):
54+
s = str(self.__nanocodas)
55+
if len(s) > 9:
56+
return s[:-9] + '.' + s[-9:]
57+
else:
58+
return '0.' + ('0' * (9 - len(s))) + s
59+
60+
def nanocodas(self):
61+
return self.__nanocodas
62+
63+
def __str__(self):
64+
return self.decimal_format()
65+
66+
def __repr__(self):
67+
return 'Currency(%s)' % self.decimal_format()
68+
69+
def __add__(self, other):
70+
if isinstance(other, Currency):
71+
return Currency(self.nanocodas() + other.nanocodas(), format=CurrencyFormat.NANO)
72+
else:
73+
raise Exception('cannot add Currency and %s' % type(other))
74+
75+
def __sub__(self, other):
76+
if isinstance(other, Currency):
77+
new_value = self.nanocodas() - other.nanocodas()
78+
if new_value >= 0:
79+
return Currency(new_value, format=CurrencyFormat.NANO)
80+
else:
81+
raise CurrencyUnderflow()
82+
else:
83+
raise Exception('cannot subtract Currency and %s' % type(other))
84+
85+
def __mul__(self, other):
86+
if isinstance(other, int):
87+
return Currency(self.nanocodas() * other, format=CurrencyFormat.NANO)
88+
elif isinstance(other, Currency):
89+
return Currency(self.nanocodas() * other.nanocodas(), format=CurrencyFormat.NANO)
90+
else:
91+
raise Exception('cannot multiply Currency and %s' % type(other))
992

1093
class Client():
1194
# Implements a GraphQL Client for the Coda Daemon
@@ -386,7 +469,7 @@ def set_current_snark_worker(self, worker_pk: str, fee: str) -> dict:
386469
res = self._send_mutation(query, variables)
387470
return res["data"]
388471

389-
def send_payment(self, to_pk: str, from_pk: str, amount: int, fee: int, memo: str) -> dict:
472+
def send_payment(self, to_pk: str, from_pk: str, amount: Currency, fee: Currency, memo: str) -> dict:
390473
"""Send a payment from the specified wallet to the specified target wallet.
391474
392475
Arguments:
@@ -424,8 +507,8 @@ def send_payment(self, to_pk: str, from_pk: str, amount: int, fee: int, memo: st
424507
variables = {
425508
"from": from_pk,
426509
"to": to_pk,
427-
"amount": amount,
428-
"fee": fee,
510+
"amount": amount.nanocodas(),
511+
"fee": fee.nanocodas(),
429512
"memo": memo
430513
}
431514
res = self._send_mutation(query, variables)
@@ -545,4 +628,4 @@ async def listen_new_blocks(self, callback):
545628
'''
546629
variables = {
547630
}
548-
await self._graphql_subscription(query, variables, callback)
631+
await self._graphql_subscription(query, variables, callback)

tests/test_currency.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from CodaClient import CurrencyFormat, CurrencyUnderflow, Currency
2+
3+
precision = 10 ** 9
4+
5+
def test_constructor_whole_int():
6+
n = 500
7+
assert Currency(n).nanocodas() == n * precision
8+
9+
def test_constructor_whole_float():
10+
n = 5.5
11+
assert Currency(n).nanocodas() == n * precision
12+
13+
def test_constructor_whole_string():
14+
n = "5.5"
15+
assert Currency(n).nanocodas() == float(n) * precision
16+
17+
def test_constructor_nano_int():
18+
n = 500
19+
assert Currency(n, format=CurrencyFormat.NANO)
20+
21+
def test_add():
22+
assert (Currency(5) + Currency(2)).nanocodas() == 7 * precision
23+
24+
def test_sub():
25+
assert (Currency(5) - Currency(2)).nanocodas() == 3 * precision
26+
27+
def test_sub_underflow():
28+
try:
29+
Currency(5) - Currency(7)
30+
raise Exception('no underflow')
31+
except CurrencyUnderflow:
32+
pass
33+
except:
34+
raise
35+
36+
def test_mul_int():
37+
assert (Currency(5) * 2).nanocodas() == 10 * precision
38+
39+
def test_mul_currency():
40+
assert (Currency(5) * Currency(2, format=CurrencyFormat.NANO)).nanocodas() == 10 * precision

0 commit comments

Comments
 (0)