What exactly is the function of Defender's Relay when using metatransactions?

So I'm new to Defender and Relay. I'm trying to create a very simple contract to learn the new concepts. I have been going through the sample codes in the workshop but I can't tell where Relay comes into the picture.

I do know what Relay is for and I've seen a client library for using it but I got confused when I saw signing functions in the sample workshop codes and they weren't using the relay client library. I thought Relay will handle the signing, so why do we have to do the signing ourselves? It's either I am missing something or they are two approaches to achieve the same result. I just need some clarifications please.

I will also appreciate links to resources that will help me understand better. Thanks

Hey @isoteriksoftware! First of all, if you haven't watched the video for the meta transactions workshop, I strongly suggest you go through it first, instead of jumping directly to the sample code.

When using meta-txs, in general, you have three parties: the user, the app, and the relayer. Your goal is to get the user to send a transaction to the application. However, you don't want the user to pay any gas. So, instead of having the user send the tx themselves, you ask them to sign a message using their private key (each user needs their own wallet with their own private key!). Then, in your contracts, the application manually verifies the message signature to see who's the sender of the message.

The missing part here is getting the signed message to the application contract. That's where the relayer comes in. The relayer is a service that just takes users' signed messages, wraps them in a transaction, and sends it to the application contract, paying the gas for it. You can use a Defender Relayer for it, an OpenGSN one, or anything that suits your needs.

Hope this helps clarify it! You can also check out the OpenGSN documentation about meta-txs.

1 Like

@spalladino thanks alot. I think I have a clearer picture now. You're the best!

1 Like

have you able to achive if yes plzz help me out too . as i know what to do but don't know how to do it?

@spalladino Thanks for the helpful workshop and thanks the OZ team for creating this much-needed product. To clarify, when you say

The relayer is a service that just takes users' signed messages, wraps them in a transaction, and sends it to the application contract , paying the gas for it.

That means the steps for using the Relayer API to submit meta txns would be:

  • App generate the unsigned txn
  • User signs the transaction
  • App sends the signed txn via the Defender Relay API to the /txs endpoint to be submitted on chain

The docs for that /txs endpoint suggest that the it only accepts unsigned requests, however, so am I missing something?

I'd like to note that I'm not using the defender-relay-client JS package but instead am writing my own client for the Relayer API in my server-side python web app. I know in your demo and also this more recent demo the steps involve using Autotasks, but I'm curious if there's a way for me to submit user-signed transactions directly to the relayer without calling an autotask.

EDIT: just saw you already answered this here. So to confirm, directly calling the /txs should work?

Yep! The relay-client package is just a convenience library for hitting that API. You can even check out its code here.

1 Like

so am trying out the /txs endpoint and am getting the following error response:

{'message': "'eyJra...LFQzOLA' not a valid key=value pair (missing equal-sign) in Authorization header: 'Bearer eyJra...LFQzOLA'."}

here is my request body:

{
    'value': 0,
    'gas': 101379,
    'chainId': 80001,
    'gasPrice': 50000000000,
    'from': '0xC52A1435fC79e130058d3Ab3040Cc149e50684fA',
    'nonce': 18,
    'to': '0x5714b30B1E511873700df2CC9aa3B88Eec51F0f1',
    'data': '0xe5f4e139000000000000000000000000000000000000000000000001158e460913d00000000000000000000000000000e11a86849d99f524cac3e7a0ec1241828e332c62',
    'signature': '0x7182d5c3c1c0a39f2588cd0d2a5db586673352cddce7a620e7c5b6282eab9df03b2b5622e12f799fc7e9e2bc4613cf55555ff7b9e5b10c6cea9d7d9f1b896e881b'
}

And here is the full code:

import requests
import boto3
from pycognito.aws_srp import AWSSRP


boto = boto3.client('cognito-idp', region_name="us-west-2")
aws = AWSSRP(
    username=os.environ.get("RELAYER_API_KEY"),
    password=os.environ.get("RELAYER_API_SECRET")
    pool_id="us-west-2_iLmIggsiy",
    client_id="1bpd19lcr33qvg5cr3oi79rdap"
    pool_region="us-west-2",
)
tokens = aws.authenticate_user()
bearer_token = tokens['AuthenticationResult']['AccessToken']
headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "X-Api-Key": os.environ.get("RELAYER_API_KEY"),
            "Authorization": f"Bearer {bearer_token}"
        }
payload = {
    'value': 0,
    'gas': 101379,
    'chainId': 80001,
    'gasPrice': 50000000000,
    'from': '0xC52A1435fC79e130058d3Ab3040Cc149e50684fA',
    'nonce': 18,
    'to': '0x5714b30B1E511873700df2CC9aa3B88Eec51F0f1',
    'data': '0xe5f4e139000000000000000000000000000000000000000000000001158e460913d00000000000000000000000000000e11a86849d99f524cac3e7a0ec1241828e332c62',
    'signature': '0x7182d5c3c1c0a39f2588cd0d2a5db586673352cddce7a620e7c5b6282eab9df03b2b5622e12f799fc7e9e2bc4613cf55555ff7b9e5b10c6cea9d7d9f1b896e881b'
}


response = requests.post(
                "https://api.defender.openzeppelin.com/relayer/txs",
                json=payload,
                headers=self.headers,
                timeout=8,
            ).json()

However, this same authorization and headers setup works sucessfully with GET requests like:

requests.get(
    "https://api.defender.openzeppelin.com/relayer/txs",
    headers=headers,
    timeout=8,
)

Anything I might be missing?

Hey @shanexavier313, I believe you're using a wrong path: the endpoint is https://api.defender.openzeppelin.com/txs, not https://api.defender.openzeppelin.com/relayer/txs (see here).

1 Like

yup that was it, thanks