Godex

Convert between all cryptocurrencies supported on Godex.io.

Note to Tails users: Tails 4.0 or later is required to run the script.

Usage:
Copy the script below and save it to a new file named godex.py.
Open the terminal in the folder and run python3 godex.py

You can view the status of an existing trade using
python3 godex.py view or python3 godex.py view --id yourgodexid

Example dialog:

amnesia@amnesia:~/Persistent$ python3 godex.py exchange
Getting available coins.
--------------
Available: LRC,DAI,ATOM,STEEM,WTC,NKN,SSC,ZEC,THETA,MFT,ARK,QTUM,ETN,KMD,LTC,PIVX,PAX,XEM,QKC,ADX,SRN,STORM,BAT,MTL,DNT,STORJ,EMC2,MCO,SOC,DPY,MITH,DTA,WIN,ETH,BCD,ARDR,DENT,LUN,GXS,POWR,GNT,OMG,IOST,DCR,EGT,XRP,PAY,XTZ,ONT,EDO,GBYTE,RDD,WAN,PPT,TCT,BNT,NANO,REP,RSR,ITC,BTT,NEO,XZC,INS,USDT,STRAT,DGD,SNM,EXP,NAV,HEDG,BSV,SWFTC,MATIC,HC,STC,MOF,NPXS,YEE,ALGO,ELF,POLY,XMR,VTC,BRD,ELA,RCN,SALT,XVG,MDT,VSYS,GAS,BTC,XAS,RVN,VET,CMCT,LAMB,AE,GUSD,TRIO,HOT,CVC,TRX,DLT,ADA,HPB,BCH,GVT,XHV,FAIR,MONA,TUSD,NBT,SC,MKR,ETC,CHAT,LINK,ZRX,TRUE,GO,GRS,INT,SNGLS,ZIL,WAVES,IOTX,IHT,EKT,BTS,NAS,LOOM,ICX,ENJ,EOS,SNT,ORS,DGB,LSK,ONG,BNB,SYS,ZEN,SWT,BTM,GTO,ZIP,RLC,LIGHT,MANA,MDA,BTG,DASH,DATA,BQX,VIB,WABI,FUN,CRW,BKX,REN,DOGE,IOTA
Disabled: 
--------------
coin to convert from: BTC
coin to convert to: XMR
amount of BTC to convert into XMR: 0.123
refund address (optional): 
destination address: 4XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx

------------ wait ------------
Your Godex id: XXXXXXXXXXXX
The exchange has just been created and it’s waiting for coins to reach the deposit wallet

Converting 0.123 BTC to 16.02 XMR
Rate: 1 BTC -> 130.12 XMR
Transaction fee: 0

Send 0.123 BTC to 3XXXXXXXXXXXXXXXXXXXXXXXXXXX
Only send one transaction.
------------ wait ------------

Script:
NOTE: Review the code yourself. It comes with ABSOLUTELY NO WARRANTY.

import argparse
from sys import argv
import requests

class Godex:
    URL = "https://api.godex.io/api/v1/"

    transaction_status = {
        "wait": "The exchange has just been created and it’s waiting for coins to reach the deposit wallet",
        "confirmation": "The transaction appears in mempool and now it is waiting for necessary network confirmations to start.",
        "confirmed": "User’s payment is confirmed, the exchange process is about to start.",
        "exchanging": "The exchange process is running.",
        "sending": "Currency is being sent to the recipient address.",
        "sending_confirmation": "Outgoing transaction is waiting for network confirmations.",
        "success": "The exchange is completed and currency is successfully sent to the recipient address.",
        "overdue": "Deposit receiving time for this transaction has expired.",
        "error": "Transaction has failed. In most cases, the amount was sent differs from specified one when creating the transaction.",
        "refunded": "Exchange was failed and coins were refunded to user's wallet."
    }

    def __init__(self, session=None):
        self.session = session or requests.session()
        self.session.headers.update({
            "Accept": "application/json",
            "Content-Type": "application/json"
        })

    def _request(self, method, endpoint, data=None):
        request_kwargs = {
            "method": method,
            "url": self.URL + endpoint,
        }
        if data:
            request_kwargs['json'] = data
        res = self.session.request(**request_kwargs)
        res = res.json()
        if 'error' in res:
            print(res['error'])
            exit(1)
        return res

    def coins(self):
        """
        This returns the list of coins, available for exchange with selected coin at given
        time, or gets entire list of coins. Response also contains such parameters as extra
        id name, if it exists, For example, destination tag for Ripple, message for XEM or
        payment id for Monero. In addition, it provides icon links.
        """
        return self._request('GET', 'coins')

    def info(self, coin_from, coin_to, amount):
        """
        Gets current rate, calculates final amount that user will get, 
        and also it sets minimum and maximum amounts for deposit.
        """
        data = {
            "from": coin_from,
            "to": coin_to,
            "amount": amount
        }
        return self._request('POST', 'info', data)

    def info_revert(self, coin_from, coin_to, amount):
        """
        This method is used when you want to get the exact amount of specific coins.
        For example, when a customer needs to pay the bill with 5 XMR (coin ‘to’), 
        and he needs to know how much BTC (coin ‘from’) he should send. The request 
        gets current rate, calculates the amount of coins that user should send to 
        receive the desired amount of ‘target’ currency, and also it sets the minimum 
        and the maximum amounts for the deposit.
        """
        data = {
            "from": coin_from,
            "to": coin_to,
            "amount": amount
        }
        return self._request('POST', 'info-revert', data)

    def transaction(self, coin_from, coin_to, deposit_amount, withdrawal, withdrawal_extra_id=None, refund=None, return_extra_id=None):
        """
        This call allows to request creating an exchange transaction. You choose coin
        pair you’d like to exchange, provide sender’s withdrawal address and specify 
        amount of coins you want to swap. Godex returns address and extra id (if needed)
        to deposit to, final amount, fee and some other parameters. 
        """
        data = {
            "coin_from": coin_from,
            "coin_to": coin_to,
            "deposit_amount": deposit_amount,
            "withdrawal": withdrawal,
            "withdrawal_extra_id": withdrawal_extra_id
        }
        if refund:
            data['return'] = refund
        if return_extra_id:
            data['return_extra_id'] = return_extra_id
        return self._request('POST', 'transaction', data)

    def get_transaction(self, id):
        """
        Gets detailed information about a single transaction. One of returned 
        parameters is ‘rate’, which is current rate offered by Godex. It may differ 
        from market rate, and also may change rapidly depending on the markets.
        """
        return self._request('GET', f'transaction/{id}')


def get_tor_session():
    session = requests.session()
    session.proxies={'https': 'socks5h://127.0.0.1:9050'}
    session.headers.clear()
    return session

def view_trade(args):
    if not args.id:
        args.id = input("Godex id: ")

    api = Godex(session=get_tor_session())
    display_transaction(api.get_transaction(args.id))

def display_transaction(t):
    print(f"\n------------ {t['status']} ------------")
    print(f"Your Godex id: {t['transaction_id']}")
    print(Godex.transaction_status[t['status']] + '\n')

    if t['status'] == 'wait':
        print(f"Converting {t['deposit_amount']} {t['coin_from']} to {t['withdrawal_amount']} {t['coin_to']}")
        print(f"Rate: 1 {t['coin_from']} -> {t['rate']} {t['coin_to']}")
        print(f"Transaction fee: {t['fee']}\n")

        print(f"Send {t['deposit_amount']} {t['coin_from']} to {t['deposit']}")
        if t['deposit_extra_id']:
            print(f"Important: Include the following message/payment id/destination tag with your transfer: {t['deposit_extra_id']}")
        print("Only send one transaction.")
    elif t['status'] in ['confirmation', 'confirmed', 'exchanging']:
        print(f"Received: {t['real_deposit_amount']} {t['coin_from']}")
        print(f"Incoming txid: {t.get('hash_in', '')}")
    elif t['status'] in ['sending', 'sending_confirmation', 'success']:
        print(f"Converted {t['real_deposit_amount']} {t['coin_from']} into {t['real_withdrawal_amount']} {t['coin_to']}")
        print(f"Outgoing txid: {t.get('hash_out', '')}")

    print(f"------------ {t['status']} ------------")

def exchange(args):
    api = Godex(session=get_tor_session())
    print("Getting available coins.")
    coins = api.coins()
    coins = {coin['code']: coin for coin in coins}
    disabled_coins = set([coin['code'] for coin in coins.values() if coin['disabled']])
    available_coins = set(coins) - disabled_coins
    print('--------------')
    print("Available: " + ','.join(sorted(available_coins)))
    print("Disabled: " + ','.join(disabled_coins))
    print('--------------')
    coin_from = input("coin to convert from: ").upper().strip()
    coin_to = input("coin to convert to: ").upper().strip()
    if coin_from not in available_coins or coin_to not in available_coins:
        print("Error: Coin not available.")
        exit(1)
    if coin_from == "XMR":
        print("Warning: your refund address may identify you."
              "Make sure you use a new subaddress as the refund address.")

    amount = float(input(f"amount of {coin_from} to convert into {coin_to}: ").strip().replace(',','.'))

    refund_addr = input("refund address (optional): ")
    dest_addr = input("destination address: ")
    if not dest_addr:
        print("Error: Please enter a destination address.")
        exit(1)

    transaction = api.transaction(coin_from, coin_to, amount, dest_addr, refund=refund_addr)
    display_transaction(transaction)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(prog='python3 godex.py')
    subparsers = parser.add_subparsers()

    parser_view = subparsers.add_parser('view', help='Fetch an existing trade')
    parser_view.add_argument('--id', type=str, help='ID of trade to lookup')
    parser_view.set_defaults(func=view_trade)

    parser_exchange = subparsers.add_parser('exchange', help='Exchange with Godex')
    parser_exchange.set_defaults(func=exchange)

    args = parser.parse_args()
    if argv[1:]:
        args.func(args)
    else:
        exchange(args)