Sending Multiple Payments in a Single Transaction

in #steem8 years ago

At SteemSports, we do hundreds of payouts weekly. Currently, each payout is done as a standalone transaction.

I was wondering if its possible to be more efficient, and send multiple payouts at once. It turns out, its not only possible, but very easy to do.

I've borrowed some code from piston to hack together these two helper functions:

def broadcast_multi(steem, wifs, ops, write_mode=False):
    tx = sign_multi_tx(steem, ops, wifs)
    if not write_mode:
        return tx
    return steem.broadcast(tx)


def sign_multi_tx(steem, ops, wifs):
    """Construct a transaction that contains multiple operations."""
    if not isinstance(wifs, list):
        wifs = [wifs]

    if not any(wifs) and not steem.unsigned:
        raise MissingKeyError

    ops = [transactions.Operation(x) for x in ops]
    expiration = transactions.formatTimeFromNow(steem.expiration)
    ref_block_num, ref_block_prefix = transactions.getBlockParams(steem.rpc)
    tx = transactions.Signed_Transaction(
        ref_block_num=ref_block_num,
        ref_block_prefix=ref_block_prefix,
        expiration=expiration,
        operations=ops
    )
    if not steem.unsigned:
        tx = tx.sign(wifs)

    return tx.json()

To send multiple payments at once, we need to create a list of operations. I am going to do this manually here for sake of simplicity and ease of understanding:

ops = [
        transactions.Transfer(
            **{"from": "fnait",
               "to": "furion",
               "amount": '{:.{prec}f} {asset}'.format(
                   float(0.1),
                   prec=3,
                   asset="SBD"
               ),
               "memo": "TEST MULTI"
               }
        ),
        transactions.Transfer(
            **{"from": "fnait",
               "to": "furion",
               "amount": '{:.{prec}f} {asset}'.format(
                   float(0.1),
                   prec=3,
                   asset="SBD"
               ),
               "memo": "TEST MULTI 2"
               }
        )
    ]

Now we can construct, sign and broadcast the transfers with a single line of code:

tx = broadcast_multi(steem, wifs, ops, write_mode=True)

Where do steem and wifs come from?
steem is just a piston Steem instance: steem = Steem() and wifs is a list of private keys (in this case, we just need the active private key).

If we set write_mode to False, it will run in simulation mode, without actually broadcasting the transaction to the network.

tx

Steemit

Note
Beware that the transaction has a hardcoded limit of 64k, so don't cram too many operations in one.



Don't miss out on the next post - follow me.
SteemSports | steemtools | steem.li | witness


Sort:  

When compiling a list of "wifs" you might use a bin packing algorithm before executing "broadcast_multi". That way you stay under 64k

Awesome, thanks for sharing @furion. I have thought about it but too lazy to try. Now I could steal your code to use it for my multivotes routine. :P Hope you don't mind ;)

Fairly confident it isn't stealing friend, when it has been shared openly.

Thank you for posting @furion. Steemsports is a winner blog....thank you for the Espirit de Corp you bring to Steemit and your professionalism. It is appreciated by many.

Needed something like this a month ago :(

Great job as always & Thank you for sharing.
Shared on twitter too.

Steem_Land Steemland.com tweeted @ 16 Nov 2016 - 16:59 UTC

Sending Multiple Payments in a Single Transaction — Steemit

steemit.com/steem/@furion/… / https://t.co/0ImRHnedg8

@SteemUps @SteemitPosts @steemit @steemiobot

Disclaimer: I am just a bot trying to be helpful.

This post has been ranked within the top 25 most undervalued posts in the second half of Nov 16. We estimate that this post is undervalued by $14.00 as compared to a scenario in which every voter had an equal say.

See the full rankings and details in The Daily Tribune: Nov 16 - Part II. You can also read about some of our methodology, data analysis and technical details in our initial post.

If you are the author and would prefer not to receive these comments, simply reply "Stop" to this comment.

Awesome work @furion. I think this will be very useful for many people in the future. I can think of many use already. I wish I would have voted before the first payout!

Is there anyway to add a list of usernames for "to", then the transfers would go down the list?

Thank you for sharing.

Here is a quick and dirty way: You could use dict comprehension to set a 'to' field from a list. I assume the other fields are the same for all payouts, then you can do something like:

list_of_recipients = ['user1', 'user2', 'user3']
transaction_template = {'from':'user0', ... }

transactions = [transactions.Transfer(**{**transaction_template, 'to': x}) for x in list_of_recipients]

Awesome I'll give it a try, thank you.
Gave you a witness vote :)

Coin Marketplace

STEEM 0.17
TRX 0.15
JST 0.028
BTC 57912.63
ETH 2348.79
USDT 1.00
SBD 2.37