Relay API Reference
The Relay API is split into 2 modules:
-
Relay Client
-
Execute create, read, and update operations across all relayers within an account
-
Authenticates with bearer token generated using Team API Key/Secret (available when Team API Key is created)
-
-
Relay Signer
-
Execute send, sign, and other operations using a specific relayer
-
Authenticates with bearer token generated using Relayer API Key/Secret (available when relayer is created)
-
For more information on authentication, refer to the authentication section.
You can use the defender-relay-client npm package for simplifying interactions with the Relay API. |
It is not recommended to use the defender-relay-client npm package in a browser environment as sensitive keys would be exposed publicly. |
Relay Client Module Reference
The Relay Client Module exposes the following endpoints:
-
list
: list all relayers associated with the account -
create
: create a new relayer -
retrieve
: retrieve data for a single relayer -
update
: update an existing relayer
List Relayers
To list existing relayers, you can call the list
function on the client, which returns a ListRelayerResponse
object:
await client.list();
The relayers/summary
endpoint is used to retrieve a list of existing relayers via a GET
request.
curl \
-X GET \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
"https://defender-api.openzeppelin.com/relayer/relayers/summary"
An example response:
[
{
relayerId: '201832e2-c612-4283-97c5-31849242c1fe',
name: 'MyRelayer',
address: '0x623e258760220127d4856fa24ff126a230560749',
network: 'rinkeby',
createdAt: '2022-04-05T16:57:37.154Z',
paused: false,
policies: {},
minBalance: '100000000000000000',
pendingTxCost: '0'
}
]
List Relayer Keys
To list keys associated with an existing relayer, you can call the listKeys
function with a relayerId
on the client, which returns an array of RelayerKey
objects:
await client.listKeys('58b3d255-e357-4b0d-aa16-e86f745e63b9');
The /relayers/${relayerId}/keys
endpoint is used to retrieve a list of relayer keys via a GET
request.
curl \
-X GET \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
"https://defender-api.openzeppelin.com/relayer/relayers/58b3d255-e357-4b0d-aa16-e86f745e63b9/keys"
An example response:
[
{
apiKey: '5Kk12pMA6LjPSppaRAsfAJRAjYeFJzr6',
createdAt: '2022-04-26T19:49:10.292Z',
relayerId: '58b3d255-e357-4b0d-aa16-e86f745e63b9',
keyId: '898a952b-415b-4d4b-882b-3958f11pbeea'
}
]
Create Relayers
To create a new relayer, you need to provide the parameters as described directly below in interface CreateRelayerRequest
.
interface CreateRelayerRequest {
name: string;
useAddressFromRelayerId?: string;
network: Network;
minBalance: string;
policies?: UpdateRelayerPoliciesRequest;
}
interface UpdateRelayerPoliciesRequest {
gasPriceCap?: string;
whitelistReceivers?: string[];
EIP1559Pricing?: boolean;
privateTransactions?: boolean;
}
type Network =
| 'mainnet'
| 'goerli'
| 'sepolia'
| 'xdai'
| 'sokol'
| 'fuse'
| 'bsc'
| 'bsctest'
| 'fantom'
| 'fantomtest'
| 'moonbase'
| 'moonriver'
| 'moonbeam'
| 'matic'
| 'mumbai'
| 'avalanche'
| 'fuji'
| 'optimism'
| 'optimism-kovan'
| 'optimism-goerli'
| 'arbitrum'
| 'arbitrum-nova'
| 'arbitrum-rinkeby'
| 'arbitrum-goerli'
| 'celo'
| 'alfajores'
| 'harmony-s0'
| 'harmony-test-s0'
| 'aurora'
| 'auroratest'
| 'zksync'
| 'zksync-goerli'
| 'base-goerli'
| 'linea-goerli';
Using the defender-relay-client
package:
const requestParameters = {
name: 'MyNewRelayer',
network: 'rinkeby',
minBalance: BigInt(1e17).toString(),
policies: {
whitelistReceivers: ['0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'],
},
};
await client.create(requestParameters);
You can also clone an existing relayer to a different network by referencing an existing relayerId
in the useAddressFromRelayerId
parameter:
const requestParameters = {
name: 'MyClonedRelayer',
useAddressFromRelayerId: '201832e2-c612-4283-97c5-31849242c1fe',
network: 'mainnet',
minBalance: BigInt(1e17).toString()
};
await client.create(requestParameters);
Making a POST request to the /relayers
endpoint:
curl \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d '{...}' \
"https://defender-api.openzeppelin.com/relayer/relayers"
Relayer Policies
The following relayer policies may be optionally included in both create and update calls:
-
gasPriceCap
: Maximum gasPrice or maxFeePerGas in wei -
whitelistReceivers
: An array of all valid addresses with which this relayer can interact; the relayer can send transactions to any address if this policy is undefined. -
EIP1559Pricing
: A boolean indicating the default pricing. If true, transactions will be priced as EIP1559, otherwise, as legacy transactions. -
privateTransactions
: A boolean indicating the default transaction mempool visibility. If true, transactions will be sent via private mempool.
EIP1559Pricing flag is only available for relayers on EIP1559 networks |
Private transactions are only enabled for goerli and mainnet by using the Flashbots Protect RPC. So, the same key considerations might apply while sending private transactions through Defender (such as uncle bandit risk) |
Create Relayer Key
A relayer created via the API will not have any associated relayer keys by default. To create one, you can call the createKey
method on the client, which returns a RelayerKey
(including the secretKey
- available on creation only):
await client.createKey('58b3d255-e357-4b0d-aa16-e86f745e63b9');
Making a POST request to the /relayers/${relayerId}/keys
endpoint:
curl \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
"https://defender-api.openzeppelin.com/relayer/relayers/58b3d255-e357-4b0d-aa16-e86f745e63b9/keys"
An example response:
[
{
apiKey: '5Kk12pMA6LjPSppaRAsfAJRAjYeFJzr6',
createdAt: '2022-04-26T19:49:10.292Z',
relayerId: '58b3d255-e357-4b0d-aa16-e86f745e63b9',
keyId: '898a952b-415b-4d4b-882b-3958f11pbeea',
secretKey: '1pDxUoWYtrAK4xMsKmOITpd1PQofWDFRRTNp2jshDhZ1XhYxDCfAvQ5vyKprFhXQ'
}
]
Update Relayers
To update a relayer, you can call the update
function on the client. This will require the relayer ID and a UpdateRelayerRequest
object as parameters:
interface UpdateRelayerRequest {
name?: string;
minBalance?: string;
policies?: UpdateRelayerPoliciesRequest;
}
Using the defender-relay-client
package:
await client.update('201832e2-c612-4283-97c5-31849242c1fe', { name: 'My Updated Name' });
Making a PUT request to the /relayers
endpoint:
curl \
-X PUT \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d '{...}' \
"https://defender-api.openzeppelin.com/relayer/relayers"
If making the request directly (not with defender-relay-client ), any policy updates will need to be made via requests to the endpoint directly below with a body type of UpdateRelayerPoliciesRequest .
|
curl \
-X PUT \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d '{...}' \
"https://defender-api.openzeppelin.com/relayer/relayers/{relayerId}"
Delete Relayer Key
To delete a key associated with a relayer, you can call the deleteKey
method on the client, which returns a message indicating deletion status:
await client.deleteKey('58b3d255-e357-4b0d-aa16-e86f745e63b9', 'j3bru93-k32l-3p1s-pp56-u43f675e92p1');
The second argument to deleteKey is the keyId , not the apiKey . One way to differentiate the two is the keyId contains hyphens. This can be fetched via the list operation described above and is also available in the response on key creation.
|
Making a DELETE request to the /relayers/${relayerId}/keys/${keyId}
endpoint:
curl \
-X DELETE \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
"https://defender-api.openzeppelin.com/relayer/relayers/58b3d255-e357-4b0d-aa16-e86f745e63b9/keys/j3bru93-k32l-3p1s-pp56-u43f675e92p1"
An example response:
{
message: 'j3bru93-k32l-3p1s-pp56-u43f675e92p1 deleted'
}
Deletion of a relayer (not just a key) is only available via the Defender console.
Relayer Signer Module Reference
The Relayer Signer Module exposes the following endpoints:
-
txs
: send transactions to the Ethereum blockchain and query their status -
sign
: sign arbitrary data with relayer’s private key -
relayer
: retrieve information from the relayer -
jsonrpc
: make json-rpc calls to the Ethereum network
Transactions Endpoint
The /txs
endpoint is used for both sending transactions and for retrieving transaction information given a transaction identifier.
Send Transaction
To send a transaction to the Ethereum blockchain submit a POST
request with the desired payload. The payload format is as follows:
type Address = string;
type BigUInt = string | number;
type Hex = string;
type Speed = 'safeLow' | 'average' | 'fast' | 'fastest';
interface SendBaseTransactionRequest {
to?: Address;
value?: BigUInt;
data?: Hex;
gasLimit: BigUInt;
validUntil?: string;
isPrivate?: boolean;
}
interface SendSpeedTransactionRequest extends SendBaseTransactionRequest {
speed: Speed;
}
interface SendLegacyTransactionRequest extends SendBaseTransactionRequest {
gasPrice: BigUInt;
}
interface SendEIP1559TransactionRequest extends SendBaseTransactionRequest {
maxFeePerGas: BigUInt;
maxPriorityFeePerGas: BigUInt;
}
type RelayerTransactionPayload =
| SendBaseTransactionRequest
| SendSpeedTransactionRequest
| SendLegacyTransactionRequest
| SendEIP1559TransactionRequest;
An example of the request:
DATA='{ "to": "0x179810822f56b0e79469189741a3fa5f2f9a7631", "value": "1", "speed": "fast", "gasLimit": "21000" }'
curl \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d "$DATA" \
"https://api.defender.openzeppelin.com/txs"
You would receive a response in the following format:
type Address = string;
type BigUInt = string | number;
type Hex = string;
type Speed = 'safeLow' | 'average' | 'fast' | 'fastest';
type Status = 'pending' | 'sent' | 'submitted' | 'inmempool' | 'mined' | 'confirmed' | 'failed';
interface RelayerTransactionBase {
transactionId: string;
hash: string;
to: Address;
from: Address;
value?: string;
data?: string;
speed: Speed;
gasLimit: number;
nonce: number;
status: Status;
chainId: number;
validUntil: string;
createdAt: string;
sentAt?: string;
pricedAt?: string;
isPrivate?: boolean;
}
interface RelayerLegacyTransaction extends RelayerTransactionBase {
gasPrice: number;
}
interface RelayerEIP1559Transaction extends RelayerTransactionBase {
maxPriorityFeePerGas: number;
maxFeePerGas: number;
}
export type TransactionResponse = RelayerLegacyTransaction | RelayerEIP1559Transaction;
Note the extra transactionId
field, which is an internal Defender identifier for the transaction, which is used for querying and replacing.
Transaction Status
Every transaction response will have an status assigned depending on the last time the backend has seen it. Each time Defender monitors a transaction, the status might be updated.
The available status and their corresponding description are the following:
Status pending , sent and submitted are subject to be merged in the future. Avoid to rely on them since backwards compatibility is not assured.
|
type Status =
/**
* Temporary pre-sent status.
* The transaction has been received but not yet signed.
*/
| 'pending'
/**
* Sent to queue.
* The transaction is already signed, enqueued and waiting to be broadcasted to the network.
*/
| 'sent'
/**
* Transaction broadcasted.
* The transaction has been broadcasted to the network providers.
*/
| 'submitted'
/**
* Seen on mempool.
* It's confirmed that the transaction is in the mempool of at least one of the nodes of the network.
*/
| 'inmempool'
/**
* Transaction mined.
* A mined block has included the transaction.
*/
| 'mined'
/**
* Enough confirmations passed.
* The transaction has being kept in a mined block after N new mined blocks, where N depends on the chain.
*/
| 'confirmed'
/**
* Terminal failure.
* The transaction couldn't get processed for unexpected reasons.
*/
| 'failed';
Replace Transaction
To replace a pending
transaction make a PUT
request to the txs
endpoint with the Defender transactionId
or nonce
, not with the transaction hash.
An example of the request:
curl \
-X PUT \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d "$DATA" \
"https://api.defender.openzeppelin.com/txs/$IDorNONCE"
The request payload and the response are the same as that of sending a transaction.
Query Transaction
To retrieve a transaction status and data make a GET
request to the txs
endpoint with the Defender transactionId
, not with the transaction hash.
An example of the request:
curl \
-X GET \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
"https://api.defender.openzeppelin.com/txs/$ID"
An example of the response:
{
"chainId":4,
"hash":"0xcef95469a9f02757f0968ec8c11449ae5e7486073075381dcd62bacec9e5d627",
"transactionId":"affba150-e563-441e-ae49-04bd6050979a",
"value":"0x1",
"maxFeePerGas":1000000000,
"maxPriorityFeePerGas":150000000,
"gasLimit":21000,
"to":"0x179810822f56b0e79469189741a3fa5f2f9a7631",
"from":"0xbce0b5b71668e42d908e387b68dba91789c932b8",
"data":"0x",
"nonce":160,
"status":"mined",
"speed":"fast"
"validUntil": "2022-10-27T03:22:09.566Z",
"sentAt": "2022-10-26T19:22:10.067Z",
"createdAt": "2022-10-26T19:22:10.067Z",
"isPrivate": false
}
List Transactions
To retrieve a list of recent transactions sent from your relayer, make a GET
request to the txs
. You can optionally set since
, limit
, and status
(mined
, pending
, or failed
) as query parameters.
An example of the request:
curl \
-X GET \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
"https://api.defender.openzeppelin.com/txs?status=pending&limit=5"
An example of the response:
[{
"chainId":4,
"hash":"0xcef95469a9f02757f0968ec8c11449ae5e7486073075381dcd62bacec9e5d627",
"transactionId":"affba150-e563-441e-ae49-04bd6050979a",
"value":"0x1",
"maxFeePerGas":1000000000,
"maxPriorityFeePerGas":150000000,
"gasLimit":21000,
"to":"0x179810822f56b0e79469189741a3fa5f2f9a7631",
"from":"0xbce0b5b71668e42d908e387b68dba91789c932b8",
"data":"0x",
"nonce":160,
"status":"mined",
"speed":"fast"
"validUntil": "2022-10-27T03:22:09.566Z",
"sentAt": "2022-10-26T19:22:10.067Z",
"createdAt": "2022-10-26T19:22:10.067Z",
"isPrivate": false
}]
Sign Endpoint
To sign arbitrary messages according to the EIP-191 Standard (prefixed by \x19Ethereum Signed Message:\n
) with your Relay private key make a POST
request to /sign
with a payload containing the hex string to sign. The payload format is:
interface SignMessagePayload {
message: Hex;
}
An example of the request:
DATA='{ "message": "0x0123456789abcdef" }'
curl \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d "$DATA" \
"https://api.defender.openzeppelin.com/sign"
You would receive a response in the following format:
interface SignedMessagePayload {
sig: Hex;
r: Hex;
s: Hex;
v: number;
}
An example of the response:
{
"r":"0x819b2645a0b73494724dac355e6ecfc983d94597b533d34fe3ecd0277046a1eb",
"s":"0x3b73c695b47dd275d17246d86bbfe35f112a7bdb5bf4a5a1a8e22fe37dfd005a",
"v":44,
"sig":"0x819b2645a0b73494724dac355e6ecfc983d94597b533d34fe3ecd0277046a1eb3b73c695b47dd275d17246d86bbfe35f112a7bdb5bf4a5a1a8e22fe37dfd005a2c"
}
Sign Typed Data Endpoint
To sign typed data according to the [EIP-712 Specification](https://eips.ethereum.org/EIPS/eip-712) with your Relay private key make a POST
request to /sign
with a payload containing the domainSeparator
and the hashStruct(message)
. Both should be 32-bytes long as they’re hashes theirselves. The payload format is:
interface SignTypedDataPayload {
domainSeparator: Hex;
hashStructMessage: Hex;
}
An example of the request:
DATA='{ "domainSeparator": "0x0123456789abcdef...", "hashStructMessage": "0x0123456789abcdef..." }'
curl \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d "$DATA" \
"https://api.defender.openzeppelin.com/sign-typed-data"
You would receive a response in the following format:
interface SignedMessagePayload {
sig: Hex;
r: Hex;
s: Hex;
v: number;
}
An example of the response:
{
"r":"0x819b2645a0b73494724dac355e6ecfc983d94597b533d34fe3ecd0277046a1eb",
"s":"0x3b73c695b47dd275d17246d86bbfe35f112a7bdb5bf4a5a1a8e22fe37dfd005a",
"v":44,
"sig":"0x819b2645a0b73494724dac355e6ecfc983d94597b533d34fe3ecd0277046a1eb3b73c695b47dd275d17246d86bbfe35f112a7bdb5bf4a5a1a8e22fe37dfd005a2c"
}
Relayer Endpoint
To retrieve a relayer’s data with the Relay API make a GET
request to the /relayer
endpoint.
An example of the request:
curl \
-X GET \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
"https://api.defender.openzeppelin.com/relayer"
You would receive a response in the following format:
interface RelayerModel {
relayerId: string;
name: string;
address: string;
network: string;
paused: boolean;
createdAt: string;
pendingTxCost: string;
}
An example of the response:
{
"relayerId":"d5484fb1-df83-4659-9903-16d57d41f188",
"name":"Rinkeby",
"address":"0x71764d6450c2b710fc3e4ee5b7a038d1e7e4fc29",
"network":"rinkeby",
"createdAt":"2020-11-02T18:00:00.212Z",
"paused":false,
"pendingTxCost":"0"
}
JSON RPC Endpoint
To make a JSON RPC call to the network of your Relay, make a POST
request to the /relayer/jsonrpc
endpoint with the method name and parameters. Note that event filter methods and websocket subscriptions are not supported.
An example of the request:
DATA='{ "jsonrpc":"2.0","method":"eth_getBalance","params":["0x407d73d8a49eeb85d32cf465507dd71d507100c1","latest"],"id":1 }'
curl \
-X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H "X-Api-Key: $KEY" \
-H "Authorization: Bearer $TOKEN" \
-d "$DATA" \
"https://api.defender.openzeppelin.com/relayer/jsonrpc"
An example of the response:
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x0234c8a3397aab58"
}