AI Agents
Intermediate topics
Send tokens with Agents
Bookmark

How to use agents to send tokens 📊💸

Introduction

Within agent-based decentralized systems, efficient communication and secure data exchange are essential. In this guide, we will walk you through the process of setting up two AI Agents utilizing the uagents library to establish a dynamic workflow where one agent periodically sends payment requests to another, which in turn processes these requests, executes transactions, and provides transaction information back to sending agent.

Let's get started!

Walk-through

  1. First of all, create a Python script for this task, and name it by running: touch sending_tokens.py

  2. Then, import the necessary modules from uagents, uagents.network, and uagents.setup. Let's then define two data models: PaymentRequest and TransactionInfo. We then need to set up the values for the AMOUNT and DENOM variables, which define the default amount and denomination for the payment requests:

    from uagents import Agent, Bureau, Context, Model
    from uagents.network import wait_for_tx_to_complete
    from uagents.setup import fund_agent_if_low
     
    class PaymentRequest(Model):
        wallet_address: str
        amount: int
        denom: str
     
    class TransactionInfo(Model):
        tx_hash: str
     
    AMOUNT = 100
    DENOM = "atestfet"
    • The PaymentRequest model represents a payment request which contains the wallet_address, amount, and denom.

    • The TransactionInfo model represents information about a transaction and contains a single attribute, tx_hash.

  3. Let's now define our agents, alice and bob. Ensure they have enough funds in their wallets to carry out transactions:

    alice = Agent(name="alice", seed="alice secret phrase")
    bob = Agent(name="bob", seed="bob secret phrase")
     
    fund_agent_if_low(bob.wallet.address(), min_balance=AMOUNT)
  4. We can now define our agents behaviour and functions:

    @alice.on_interval(period=10.0)
    async def request_funds(ctx: Context):
        await ctx.send(
            bob.address,
            PaymentRequest(
                wallet_address=str(alice.wallet.address()), amount=AMOUNT, denom=DENOM
            ),
        )

    This defines an event handler for alice using the .on_interval() decorator. This event handler is triggered at regular intervals of 10.0 seconds in this case. The event handler function is named request_funds() and takes a ctx parameter of type Context. Within the function, alice sends a payment request message to bob by using the ctx.send() method. The ctx.send() method is called with the recipient address, bob.address, which specifies that the message should be sent to bob. The message is an instance of the PaymentRequest() model. It contains alice's wallet address (alice.wallet.address()), the amount (AMOUNT), and the denomination (DENOM).

  5. We can now define a confirm_transaction() message handler for alice to handle incoming messages from bob of type TransactionInfo:

    @alice.on_message(model=TransactionInfo)
    async def confirm_transaction(ctx: Context, sender: str, msg: TransactionInfo):
        ctx.logger.info(f"Received transaction info from {sender}: {msg}")
        tx_resp = await wait_for_tx_to_complete(msg.tx_hash, ctx.ledger)
     
        coin_received = tx_resp.events["coin_received"]
        if (
            coin_received["receiver"] == str(alice.wallet.address())
            and coin_received["amount"] == f"{AMOUNT}{DENOM}"
        ):
            ctx.logger.info(f"Transaction was successful: {coin_received}")

    The event handler function is named confirm_transaction() and takes three parameters: ctx, sender, and msg. Within the function, alice logs an informational message using the ctx.logger.info() method, indicating the receipt of transaction information from the sender agent, bob, and displaying the msg object. The wait_for_tx_to_complete() function is used to await the completion of the transaction specified by the tx_hash received in the message.

    Once the transaction is completed, the code accesses the coin_received event from the transaction response using tx_resp.events[\"coin_received\"]. It checks if the receiver address matches alice's wallet address (alice.wallet.address()) and if the amount received matches the expected amount (AMOUNT + DENOM).

    If the conditions are met, alice logs another informational message indicating the success of the transaction and displaying the details of the received coins.

  6. Let's now define an event handler for bob. This event handler is triggered when bob receives a message of type PaymentRequest from alice:

    @bob.on_message(model=PaymentRequest, replies=TransactionInfo)
    async def send_payment(ctx: Context, sender: str, msg: PaymentRequest):
        ctx.logger.info(f"Received payment request from {sender}: {msg}")
     
        # send the payment
        transaction = ctx.ledger.send_tokens(
            msg.wallet_address, msg.amount, msg.denom, bob.wallet
        )
     
        # send the tx hash so alice can confirm
        await ctx.send(alice.address, TransactionInfo(tx_hash=transaction.tx_hash))

    The event handler function is named send_payment() and takes three parameters: ctx, sender, and msg. Within the function, bob logs an informational message using the ctx.logger.info() method, indicating the receipt of a payment request from the sender agent, bob, and displaying the msg object.

    Next, the code performs a payment transaction using the ctx.ledger.send_tokens() method. It takes the wallet address (msg.wallet_address), amount (msg.amount), denomination (msg.denom), and bob.wallet() as parameters. This method is responsible for sending the requested payment.

    Once the transaction is completed, bob sends a message back to alice to inform her about the transaction, using ctx.send(). The message is created using the TransactionInfo model with the tx_hash obtained from the transaction response. The ctx.send() method is used to send this message to alice using her address (alice.address).

  7. We are now ready to use the Bureau class to create a bureau object and add both uAgents to it so for them to be run together:

    bureau = Bureau()
    bureau.add(alice)
    bureau.add(bob)
     
    if __name__ == "__main__":
        bureau.run()

The overall script for this example should look as follows:

sending_tokens.py
from uagents import Agent, Bureau, Context, Model
from uagents.network import wait_for_tx_to_complete
from uagents.setup import fund_agent_if_low
 
class PaymentRequest(Model):
    wallet_address: str
    amount: int
    denom: str
 
class TransactionInfo(Model):
    tx_hash: str
 
AMOUNT = 100
DENOM = "atestfet"
 
alice = Agent(name="alice", seed="alice secret phrase")
bob = Agent(name="bob", seed="bob secret phrase")
 
fund_agent_if_low(bob.wallet.address(), min_balance=AMOUNT)
 
@alice.on_interval(period=10.0)
async def request_funds(ctx: Context):
    await ctx.send(
        bob.address,
        PaymentRequest(
            wallet_address=str(alice.wallet.address()), amount=AMOUNT, denom=DENOM
        ),
    )
 
@alice.on_message(model=TransactionInfo)
async def confirm_transaction(ctx: Context, sender: str, msg: TransactionInfo):
    ctx.logger.info(f"Received transaction info from {sender}: {msg}")
    tx_resp = await wait_for_tx_to_complete(msg.tx_hash, ctx.ledger)
 
    coin_received = tx_resp.events["coin_received"]
    if (
        coin_received["receiver"] == str(alice.wallet.address())
        and coin_received["amount"] == f"{AMOUNT}{DENOM}"
    ):
        ctx.logger.info(f"Transaction was successful: {coin_received}")
 
@bob.on_message(model=PaymentRequest, replies=TransactionInfo)
async def send_payment(ctx: Context, sender: str, msg: PaymentRequest):
    ctx.logger.info(f"Received payment request from {sender}: {msg}")
 
    # send the payment
    transaction = ctx.ledger.send_tokens(
        msg.wallet_address, msg.amount, msg.denom, bob.wallet
    )
 
    # send the tx hash so alice can confirm
    await ctx.send(alice.address, TransactionInfo(tx_hash=transaction.tx_hash))
 
bureau = Bureau()
bureau.add(alice)
bureau.add(bob)
 
if __name__ == "__main__":
    bureau.run()

Run the script

On your terminal, make sure to have activated the virtual environment.

Run the script: python sending_tokens.py

The output should be as follows:

[  bob]: Received payment request from agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a: wallet_address='fetch1967p3vkp0yngdfturv4ypq2p4g760ml705wcxy' amount=100 denom='atestfet'
[alice]: Received transaction info from agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50: tx_hash='DB662CCF415C7B0C9A02928967BE1817506D02A041AA05CA48DCE5CF87D5A638'
[alice]: Transaction was successful: {'receiver': 'fetch1967p3vkp0yngdfturv4ypq2p4g760ml705wcxy', 'amount': '100atestfet'}

Was this page helpful?

Bookmark