diff --git a/bin/MasterDistiller.js b/bin/MasterDistiller.js
deleted file mode 100755
index 47a73c7702aa13a450d328d1b136e08db1a140fc..0000000000000000000000000000000000000000
--- a/bin/MasterDistiller.js
+++ /dev/null
@@ -1,264 +0,0 @@
-#!/usr/bin/node
-
-"use strict"
-
-const   BigNumber   =   require('bignumber.js');
-const   Libbfa      =   require( process.env.BFAHOME + '/bin/libbfa.js');
-const   rl          =   require('readline').createInterface(
-        { input: process.stdin, output: process.stdout }
-    );
-var     web3;
-var     Distillery;
-var     bfa;
-var     notation    =   { "potency": 15 };
-
-function    init()
-{
-    var     table   =   [
-         6, 'Mwei',
-         9, 'Gwei',
-        12, 'micro',
-        15, 'finney',
-        18, 'ether',
-        21, 'kether',
-        24, 'grand',
-        27, 'mether',
-        30, 'gether',
-        33, 'tether'
-    ];
-    for (var i = 0; table[i]; i+=2)
-        if ( table[i] == notation.potency )
-            notation.name   =   table[i+1]
-    if ( undefined == notation.name )
-        notation    = { 'potency': 15, 'name': 'finney' };
-    notation.num    = BigNumber( 10 ).pow( notation.potency );
-    bfa             =   new Libbfa();
-    web3            =   bfa.newweb3();
-    Distillery      =   bfa.contract( web3, 'Distillery' );
-    requestDistBalance()
-    .then( requestBalances )
-}
-
-function    palletSort(a,b)
-{
-    if ( b == undefined )
-    {
-        if ( a == undefined )
-            return 0;
-        else
-            return -1;
-    }
-    if ( a == undefined )
-        return 1;
-    var strA    =   a.addr.toLowerCase();
-    var strB    =   b.addr.toLowerCase();
-    if ( strA < strB )
-        return -1;
-    if ( strA > strB )
-        return 1;
-    return 0;
-}
-
-async function    requestDistBalance()
-{
-    return web3.eth.getBalance(Distillery.contractaddress)
-    .then( (bal) => { return Distillery.contractbalance = new BigNumber( bal ) } )
-}
-
-async function    requestBalances()
-{
-    var     count   =   await Distillery.methods.numberOfBeneficiaries().call();
-    var     pallet  =   new Array;
-    var     i;
-    // Fetch addresses from the list in the contract.
-    for ( i=0; i<count; i++ )
-    {
-        var     addressandsetting    =   await Distillery.methods.atPosition(i).call();
-        var     addr    =   addressandsetting[0];
-        var     setting =   addressandsetting[1];
-        var     bal     =   await web3.eth.getBalance( addr );
-        pallet.push( { "addr": addr, "setting": new BigNumber(setting), "balance": new BigNumber(bal) } );
-    }
-    displayBalances(pallet);
-}
-
-function    editAccount( entry, pallet )
-{
-    if ( entry == undefined )
-        return;
-    var     acct;
-    var     value;
-    // it is an existing account address?
-    if ( bfa.isAddr(entry) )
-    {
-        var     i       =   0;
-        var     n       =   pallet.length;
-        while ( i < n )
-        {
-            if (String(pallet[i].addr).toLowerCase() == String(entry).toLowerCase() )
-                entry   =   i;
-            i++;
-        }
-    }
-    // it is a new account address?
-    if ( bfa.isAddr(entry) )
-    {
-        acct            =   entry;
-        value           =   0;
-    }
-    else
-    if  ( bfa.isNumeric(entry) && entry < pallet.length )
-    {
-        acct            =   pallet[entry].addr;
-        value           =   pallet[entry].setting;
-    }
-    else
-    if ( entry == "x" )
-    {
-        var     rcpt;
-        // trigger distribution
-        Distillery.methods.distribute().send( {"from": bfa.account, "gas": 4000000, gasPrice: 1000000000 } )
-        .on( 'error', (x) => { bfa.fatal( "Distribute returned errors: " + x ) } )
-        .on( 'confirmation', (n,x) =>
-            {
-                if ( undefined == rcpt )
-                {
-                    rcpt    =   x;
-                    console.log(
-                        "Distribute returned succesfully in block# "
-                        + rcpt.blockNumber
-                        + " using "
-                        + rcpt.gasUsed
-                        + " gas."
-                    );
-                    var beforeBal = Distillery.contractbalance;
-                    requestDistBalance()
-                    .then(
-                        function()
-                        {
-                            console.log(
-                                "Distributed "
-                                + beforeBal.minus( Distillery.contractbalance ).div( notation.num ).toFixed()
-                                + " "
-                                + notation.name
-                                + "."
-                            );
-                            requestBalances();
-                        }
-                    )
-                }
-            }
-        );
-        return;
-    }
-    else
-    if ( entry == "" )
-    {
-        // Do nothing, basically just update the display
-        return;
-    }
-    else
-    {
-        bfa.fatal("I don't know what to do with \""+entry+"\"." );
-    }
-    rl.question(
-        "Adjust the "
-        + notation.name
-        + " fill value of "
-        + acct
-        + " (setting to 0 is the same as deleting)\n"
-        + "Amount?: ",
-        (answer) => {
-            if ( bfa.isNumeric(answer) )
-            {
-                var     bi  =   BigNumber( answer ).multipliedBy( notation.num );
-                console.log("Sending update to the SC...");
-                var     rcpt;
-                Distillery.methods.setEtherAllowance(acct,web3.utils.toHex(bi))
-                .send( {"from": bfa.account, gasPrice: 1000000000, gas: 100000 } )
-                .on( 'error', (err) => {
-                        bfa.fatal(
-                            "\nMaybe you are not authorized:\n"
-                            +err
-                            +"\n\n\nI think you should leave now.\n"
-                        );
-                    }
-                )
-                .on( 'confirmation', (n,x) => {
-                        if ( undefined == rcpt )
-                        {
-                            rcpt    =   x;
-                            console.log("Update accepted.")
-                            requestBalances();
-                        }
-                    }
-                );
-            }
-            else
-                bfa.fatal( "I have no idea what to do with \""+answer+"\"." );
-            rl.close;
-        }
-    );
-}
-
-function    displayBalances( pallet )
-{
-    var     n       =   pallet.length;
-    var     i;
-    pallet.sort(palletSort);
-    console.log(
-        "The contract's account ("
-        + Distillery.contractaddress
-        + ") has "
-        + Distillery.contractbalance.div( notation.num ).toFixed()
-        + " "
-        + notation.name
-        + ".\n"
-    );
-    var     longest         =   1;
-    for ( i=0; i<n; i++ )
-    {
-        var len =   pallet[i].setting.div(notation.num).toFixed(notation.potency).length;
-        if ( len > longest )
-            longest = len;
-    }
-    for ( i=0; i<n; i++ )
-    {
-        var     entry   =   pallet[i];
-        if ( entry == undefined )
-            console.log( i+": <undef>" );
-        else
-        {
-            var     numstr  =   pallet[i].setting.div(notation.num).toFixed(notation.potency);
-            while ( numstr.length < longest )
-                numstr      =   " "+numstr;
-            console.log(
-                i
-                + ": "
-                + pallet[i].addr
-                + " fills to "
-                + numstr
-                + " "
-                + notation.name
-                + " (has "
-                + pallet[i].balance.div(notation.num).toFixed(notation.potency)
-                + ")."
-            );
-        }
-    }
-    console.log("\n[ Q=quit x=distribute ]");
-    rl.question("Which account to edit (enter index number of full account number)?: ",
-        (answer) => {
-            if ( answer != undefined )
-            {
-                if ( String(answer).toUpperCase() == 'Q' )
-                    process.exit( 0 );
-                else
-                    editAccount(answer, pallet);
-            }
-            rl.close;
-        }
-    )
-}
-
-init();
diff --git a/bin/MasterDistiller.py b/bin/MasterDistiller.py
new file mode 100755
index 0000000000000000000000000000000000000000..310153ead93e2fa1507253e8c10ec6028692aeb3
--- /dev/null
+++ b/bin/MasterDistiller.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python3
+
+import sys
+import os
+import json
+import re
+import decimal
+os.environ['BFAHOME']='/home/bfa/bfa'
+sys.path.append( os.path.join(os.environ['BFAHOME'],'bin' ))
+import libbfa
+bfa                 =   libbfa.Bfa('prod')
+notation            =   dict()
+janitor             =   None
+distillery          =   None
+
+def distbalance() -> int:
+    return bfa.w3.eth.getBalance(distillery.address)
+
+def editAccount(entry:str, pallet:list):
+    acct                =   None
+    # is it an account address?
+    if entry == '':
+        # Do nothing, basically just update the display
+        pass
+    elif re.search('^0x[0-9a-fA-F]{40}$', entry):
+        acct            =   entry.lower()
+    # is it a known account address?
+    elif re.search('^[0-9]+$', entry) and int(entry) < len(pallet):
+        acct            =   pallet[int(entry)].addr
+    elif entry == 'x':
+        # trigger distribution
+        beforeBal = distbalance()
+        rcpt = janitor.transact( web3=bfa.w3, function=distillery.functions.distribute, extragas=4000000)
+        print('Distribute returned succesfully in block# {} using {} gas.'.format(rcpt.blockNumber, rcpt.gasUsed))
+        afterBal = distbalance()
+        print('Distributed {} {}.'.format( int(decimal.Decimal(beforeBal - afterBal) / notation['num']), notation['name']))
+    else:
+        print('I do not know what to do with "{}".'.format(entry), file=sys.stderr)
+        exit(1)
+    if acct is None:
+        return
+    answer = input('Adjust the {} fill value of {} (setting to 0 is the same as deleting)\nAmount?: '.format(notation['name'], acct))
+    if re.search('^[0-9\.]+$', answer) is None:
+        print('I have no idea what to do with "{}".'.format(answer), file=sys.stderr)
+        exit(1)
+    print('Sending update to the SC...')
+    weilimit = float(answer) * int(notation['num'])
+    rcpt = janitor.transact( bfa.w3.toChecksumAddress(acct), int(weilimit), web3=bfa.w3, function=distillery.functions.setEtherAllowance)
+    if rcpt.status:
+        print('Update accepted.')
+    else:
+        print('Update failed.')
+
+def getPallet() -> list:
+    count                   =   distillery.functions.numberOfBeneficiaries().call()
+    pallet                  =   list()
+    # Fetch addresses from the list in the contract.
+    for i in range(count):
+        print("Indexing accounts ({}/{})...\x1B[J\r".format(i,count), end='')
+        (addr,topuplimit)   =   distillery.functions.atPosition(i).call()
+        bal                 =   bfa.w3.eth.getBalance( addr )
+        pallet.append( { "addr": addr, "topuplimit": topuplimit, "balance": bal } )
+    print("\r\x1B[J".format(i,count), end='')
+    s                       =   lambda x:x['addr'].lower()
+    pallet.sort(key=s)
+    return pallet
+
+def printPallet(pallet:list):
+    # find the length of the longest number-string
+    longestlimit            =   1
+    longestbalance          =   1
+    numformat               =   notation['strformat'].format
+    for i in range(len(pallet)):
+        entry               =   pallet[i]
+        numstr              =   numformat(decimal.Decimal(entry['topuplimit']) / notation['num'])
+        thislen             =   len(numstr)
+        if thislen > longestlimit:
+            longestlimit = thislen
+        numstr              =   numformat(decimal.Decimal(entry['balance']) / notation['num'])
+        thislen             =   len(numstr)
+        if thislen > longestbalance:
+            longestbalance = thislen
+    # print them all
+    theformat               =   '{:' + str(len(str(len(pallet)-1))) + '}: {} fills to {:' + str(longestlimit) + '.' + str(notation['potency']) + 'f} {} (has {:' + str(longestbalance) + '.' + str(notation['potency']) + 'f}).'
+    for i in range(len(pallet)):
+        entry               =   pallet[i]
+        numstr              =   numformat(decimal.Decimal(entry['topuplimit']) / notation['num'])
+        while len(numstr) < longestlimit:
+            numstr          =   ' ' + numstr
+        print(theformat.format(
+            i,
+            bfa.w3.toChecksumAddress(entry['addr']),
+            decimal.Decimal(entry['topuplimit'])/notation['num'],
+            notation['name'],
+            decimal.Decimal(entry['balance']) / notation['num']
+        ))
+
+def overview():
+    print( "The contract's account ({}) has {} {}.".format(
+        distillery.address,
+        int(decimal.Decimal(distbalance()) / notation['num']),
+        notation['name']
+    ))
+    pallet  =   getPallet()
+    printPallet(pallet)
+    answer = input("\n[ Q=quit x=distribute ]\nWhich account to edit (enter index number or full account number)?: ")
+    if answer is None or answer.upper() == 'Q':
+        exit( 0 )
+    editAccount(answer, pallet)
+
+def init():
+    global janitor, notation, distillery;
+    janitor                 =   libbfa.Account('0xd15dd6dbbe722b451156a013c01b29d91e23c3d6')
+    table   =   dict([
+        (6, 'Mwei'),
+        (9, 'Gwei'),
+        (12, 'micro'),
+        (15, 'finney'),
+        (18, 'ether'),
+        (21, 'kether'),
+        (24, 'grand'),
+        (27, 'mether'),
+        (30, 'gether'),
+        (33, 'tether'),
+    ])
+    potency                 =   18
+    notation['potency']     =   potency
+    notation['name']        =   table[potency]
+    notation['num']         =   pow(10, potency)
+    notation['strformat']   =   '{' + ':.{}f'.format(potency) + '}'
+    abifile = ''
+    if os.getenv('BFAHOME'):
+        abifile = os.path.join(os.path.join(os.environ['BFAHOME'], 'network'))
+    if os.getenv('BFANETWORKDIR'):
+        abifile = os.path.join(os.environ['BFANETWORKDIR'])
+    abifile = os.path.join(abifile, 'contracts', 'Distillery', 'abi')
+    with open(abifile, 'rt', encoding='utf-8') as infile:
+        abitxt = infile.read()
+        abi = json.loads(abitxt)
+    addr                    =   '0xECB6aFF6e38dC58C4d9AaE2F7927A282bcB77AC2'
+    distillery              =   bfa.w3.eth.contract(address=addr, abi=abi)
+
+init()
+while True:
+    overview()