money.py 6.42 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
"""
Copyright  2016-2019 Maël Azimi <m.a@moul.re>

Silkaj is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Silkaj is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Silkaj. If not, see <https://www.gnu.org/licenses/>.
"""

18 19
from click import command, argument, pass_context

Moul's avatar
Moul committed
20
from silkaj.network_tools import ClientInstance, HeadBlock
21
from silkaj.tools import CurrencySymbol, message_exit, coroutine
22 23
from silkaj.auth import auth_method
from silkaj.wot import check_public_key
Moul's avatar
Moul committed
24
from duniterpy.api.bma import tx, blockchain
25
from duniterpy.documents.transaction import InputSource
26

27

28 29 30 31 32
@command("balance", help="Get wallet balance")
@argument("pubkeys", nargs=-1)
@pass_context
@coroutine
async def cmd_amount(ctx, pubkeys):
Moul's avatar
Moul committed
33
    client = ClientInstance().client
34 35 36 37 38 39 40 41
    if not (
        ctx.obj["AUTH_SCRYPT"]
        or ctx.obj["AUTH_FILE"]
        or ctx.obj["AUTH_SEED"]
        or ctx.obj["AUTH_WIF"]
    ):
        if not pubkeys:
            message_exit("You should specify one or many pubkeys")
42 43 44 45 46 47
        for pubkey in pubkeys:
            pubkey = check_public_key(pubkey, True)
            if not pubkey:
                return
        total = [0, 0]
        for pubkey in pubkeys:
Moul's avatar
Moul committed
48
            value = await get_amount_from_pubkey(pubkey)
Moul's avatar
Moul committed
49
            await show_amount_from_pubkey(pubkey, value)
50 51
            total[0] += value[0]
            total[1] += value[1]
Moul's avatar
Moul committed
52
        if len(pubkeys) > 1:
Moul's avatar
Moul committed
53
            await show_amount_from_pubkey("Total", total)
54
    else:
55
        key = auth_method()
56
        pubkey = key.pubkey
Moul's avatar
Moul committed
57 58
        await show_amount_from_pubkey(pubkey, await get_amount_from_pubkey(pubkey))
    await client.close()
59 60


Moul's avatar
Moul committed
61
async def show_amount_from_pubkey(pubkey, value):
62 63
    totalAmountInput = value[0]
    amount = value[1]
Moul's avatar
Moul committed
64
    currency_symbol = await CurrencySymbol().symbol
Moul's avatar
Moul committed
65
    ud_value = await UDValue().ud_value
Moul's avatar
Moul committed
66
    average, monetary_mass = await get_average()
67 68 69
    if totalAmountInput - amount != 0:
        print("Blockchain:")
        print("-----------")
70
        print("Relative     =", round(amount / ud_value, 2), "UD", currency_symbol)
Moul's avatar
Moul committed
71
        print("Quantitative =", round(amount / 100, 2), currency_symbol + "\n")
72 73 74

        print("Pending Transaction:")
        print("--------------------")
Moul's avatar
Moul committed
75 76 77 78 79 80 81 82 83 84 85
        print(
            "Relative     =",
            round((totalAmountInput - amount) / ud_value, 2),
            "UD",
            currency_symbol,
        )
        print(
            "Quantitative =",
            round((totalAmountInput - amount) / 100, 2),
            currency_symbol + "\n",
        )
86 87 88

    print("Total amount of: " + pubkey)
    print("----------------------------------------------------------------")
Moul's avatar
Moul committed
89 90 91 92 93 94
    print(
        "Total Relative     =",
        round(totalAmountInput / ud_value, 2),
        "UD",
        currency_symbol,
    )
95
    print("Total Quantitative =", round(totalAmountInput / 100, 2), currency_symbol)
Moul's avatar
Moul committed
96
    print(
97
        "Total Relative to average money share =",
98 99
        round(totalAmountInput / average, 2),
        "× M/N",
Moul's avatar
Moul committed
100
    )
101 102
    print(
        "Total Relative to monetary mass       =",
103 104
        round((totalAmountInput / monetary_mass) * 100, 3),
        "% M" + "\n",
105 106 107
    )


Moul's avatar
Moul committed
108 109
async def get_average():
    head = await HeadBlock().head_block
110 111 112 113
    monetary_mass = head["monetaryMass"]
    members_count = head["membersCount"]
    average = monetary_mass / members_count
    return average, monetary_mass
114 115


Moul's avatar
Moul committed
116 117
async def get_amount_from_pubkey(pubkey):
    listinput, amount = await get_sources(pubkey)
118 119 120

    totalAmountInput = 0
    for input in listinput:
121
        totalAmountInput += amount_in_current_base(input)
122 123 124
    return totalAmountInput, amount


Moul's avatar
Moul committed
125 126
async def get_sources(pubkey):
    client = ClientInstance().client
127
    # Sources written into the blockchain
Moul's avatar
Moul committed
128
    sources = await client(tx.sources, pubkey)
129

130
    listinput = list()
131
    amount = 0
Moul's avatar
Moul committed
132
    for source in sources["sources"]:
133
        if source["conditions"] == "SIG(" + pubkey + ")":
Moul's avatar
Moul committed
134
            listinput.append(
135 136 137 138 139 140 141
                InputSource(
                    amount=source["amount"],
                    base=source["base"],
                    source=source["type"],
                    origin_id=source["identifier"],
                    index=source["noffset"],
                )
Moul's avatar
Moul committed
142
            )
143
            amount += amount_in_current_base(listinput[-1])
144 145

    # pending source
Moul's avatar
Moul committed
146 147
    history = await client(tx.pending, pubkey)
    history = history["history"]
148 149
    pendings = history["sending"] + history["receiving"] + history["pending"]

150
    # add pending output
151
    pending_sources = list()
Moul's avatar
Moul committed
152
    for i, pending in enumerate(pendings):
153 154 155 156 157 158 159 160 161 162 163 164 165
        identifier = pending["hash"]
        for output in pending["outputs"]:
            outputsplited = output.split(":")
            if outputsplited[2] == "SIG(" + pubkey + ")":
                inputgenerated = InputSource(
                    amount=int(outputsplited[0]),
                    base=int(outputsplited[1]),
                    source="T",
                    origin_id=identifier,
                    index=i,
                )
                if inputgenerated not in listinput:
                    listinput.append(inputgenerated)
166

167
        for input in pending["inputs"]:
168
            pending_sources.append(InputSource.from_inline(input))
169 170 171 172 173

    # remove input already used
    for input in listinput:
        if input in pending_sources:
            listinput.remove(input)
174

175
    return listinput, amount
176 177


178 179 180
class UDValue(object):
    __instance = None

181
    def __new__(cls):
182 183 184 185
        if UDValue.__instance is None:
            UDValue.__instance = object.__new__(cls)
        return UDValue.__instance

186
    def __init__(self):
Moul's avatar
Moul committed
187 188 189 190 191 192 193 194
        self.ud_value = self.get_ud_value()

    async def get_ud_value(self):
        client = ClientInstance().client
        blockswithud = await client(blockchain.ud)
        NBlastUDblock = blockswithud["result"]["blocks"][-1]
        lastUDblock = await client(blockchain.block, NBlastUDblock)
        return lastUDblock["dividend"] * 10 ** lastUDblock["unitbase"]
195 196 197 198 199 200 201


def amount_in_current_base(source):
    """
    Get amount in current base from input or output source
    """
    return source.amount * 10 ** source.base