Transaction Relay Service API Standard
Want to easily deploy a Universal Profile using our Developer Relayer API? See the Relayer Developer Access page.
This standard is the off-chain API that any transaction relay service can implement to be compatible with the Universal Profile controller apps. See the up_addTransactionRelayer
RPC endpoint for more.
To learn how to sign a transaction relay message see LSP25 Execute Relay Call.
POST /execute
Executes a signed transaction on behalf of a Universal Profile using executeRelayCall()
.
- Use signed message provided in request for authentication.
- Calculate and return the transaction hash in response.
{
"address": "0xBB645D97B0c7D101ca0d73131e521fe89B463BFD", // Address of the Universal Profile
"transaction": {
"abi": "0x7f23690c5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000596f357c6aa5a21984a83b7eef4cb0720ac1fcf5a45e9d84c653d97b71bbe89b7a728c386a697066733a2f2f516d624b43744b4d7573376741524470617744687a32506a4e36616f64346b69794e436851726d3451437858454b00000000000000",
"signature": "0x43c958b1729586749169599d7e776f18afc6223c7da21107161477d291d497973b4fc50a724b1b2ab98f3f8cf1d5cdbbbdf3512e4fbfbdc39732229a15beb14a1b",
"nonce": 1 // KeyManager nonce
}
}
{
"transactionHash": "0xBB645D97B0c7D101ca0d73131e521fe89B463BFD"
}
POST /quota
Returns the available quota left for a registered Universal Profile.
signature
is the message value signed by a controller key with theSIGN
permission of the Universal Profile. The hash to sign should be calculated as EIP-712 hash where the message iskeccack256(address, timestamp)
. Make sure that no matter the language or platform timestamp is of typeint
,int256
,uint
oruint256
. In the backend the message is reconstructed using soliditysha3() to verify the signature.
Web3.js and ethers.js both automatically hash when using their native sign functions. This may need to be done manually if using a different library.
timestamp
in seconds. Must be now +/- 5 seconds.
How to generate and verify the signature.
- Web3
- Ethers
import { soliditySha3 } from 'web3-utils';
import Web3 from 'web3';
const address = '0x1234...'; // The Universal Profile address
const timestamp = Math.round(Date.now() / 1000);
const message = soliditySha3(address, timestamp);
/**
* Generate the signature - client side
*/
const web3 = new Web3();
const privateKey = '0x123...'; // The private key of the EOA which has SIGN permission over the Universal Profile defined in address.
const signature = web3.eth.accounts.sign(message, privateKey).signature;
// 👉 This signature is used in the request payload.
/**
* Verify the signature - relayer side
*/
const signer = web3.eth.accounts.recover(message, signature.signature); // Signer will be the EOA that has signed the message.
// You need to verify if this EOA has a SIGN permission on the Universal Profile defined in address.
// ...
const address = '0x1234...'; // The Universal Profile address
const timestamp = Math.round(Date.now() / 1000);
const message = ethers.utils.solidityKeccak256(
['address', 'uint'],
[address, timestamp],
);
/**
* Generate the signature - client side
*/
// [... ethers signer setup...]
const signature = await ethersSigner.signMessage(arrayify(message));
// 👉 This signature is used in the request payload.
/**
* Verify the signature - relayer side
*/
const signer = ethers.utils.verifyMessage(arrayify(message), signature); // Signer will be the EOA that has signed the message.
// You need to verify if this EOA has a SIGN permission on the Universal Profile defined in address.
To verify if the signature was signed by an authorized EOA, please refer to the Sign-In With Ethereum guide.
{
"address": "0xBB645D97B0c7D101ca0d73131e521fe89B463BFD",
"timestamp": 1656408193,
"signature": "0xf480c87a352d42e49112257cc6afab0ff8365bb769424bb42e79e78cd11debf24fd5665b03407d8c2ce994cf5d718031a51a657d4308f146740e17e15b9747ef1b"
}
{
"quota": 1543091, // You have YYY left
"unit": "gas", // could be "lyx", "transactionCount"
"totalQuota": 5000000, // total gas for the month
"resetDate": 1656408193
}
quota
shows available balance left in units defined byunit
.unit
could begas
,lyx
ortransactionCount
depending on the business model.totalQuota
reflects total limit. i.e. available + used quota since reset.resetDate
gives date that available quota will reset, e.g. a monthly allowance.
Quota systems could also use a Pay As You Go model, in which case totalQuota and resetData can be omitted.