This guide goes over the exposed RIF Relay methods that dApps and wallets can consume to provide relaying as a service, with the purpose of allowing users to pay transaction fees with tokens in a particular system.
There are multiple ways to integrate RIF Relay into a system. These will be detailed down below.
Additionally, it’s important to note that not all of the RIF Relay components are needed for a successful integration, as explained in the following section.
These need to be deployed and their addresses known. For steps on how to do this, please refer to the Deploy contracts locally section of the Installation guide.
Once deployed, you’ll only need the RSK node to be running.
The RIF Relay Server is the off-chain component in charge of receiving transactions and sending them to the on-chain components, chiefly the RIF Relay Hub. The RIF Relay Hub manages information about the RIF Relay Workers and RIF Relay Managers, but also communicates with the rest of the on-chain components in turn: the Smart Wallets, Factory and Verifier contracts.
The RIF Relay Manager owns RIF Relay Worker accounts with funds in native coin. To relay a transaction, a Worker signs it and sends it to the RIF Relay Hub paying for the gas consumed. In the case of a happy flow, transactions will ultimately be relayed through the RIF Relay Hub, using the EIP-712 library.
For more details on this, please refer to the Architecture page.
Users can interact with the RIF Relay Server directly or indirectly. For the latter, a user can communicate with a RIF Relay Server through a RIF Relay Client. A RIF Relay Client knows the addresses of different RIF Relay Servers and it can send on-chain requests to any one of them. The RIF Relay Client then sends the transaction to be sponsored to the RIF Relay Server via HTTP request.
In any case, you’ll need to have the server installed and running. To achieve this please refer to the following guides:
The simplest option to use RIF Relay in your wallet or dApp is by calling the RIF Relay Server directly. The instructions for running a Relayer are here. The communication with the RIF Relay Server is done through HTTP requests.
The order of events for relaying transactions or deploying smart wallets through the RIF Relay Server is:
/relay
method using an HTTP POST request.Here’s an example of how the HTTP RIF Relay Request might look like:
{
"relayRequest": {
"request": {
"relayHub": "0x3bA95e1cccd397b5124BcdCC5bf0952114E6A701",
"to": "0xafa16a8d7a94550079014d537e9440ddb7765d29",
"data": "0x0a798f2400000000000000000000000020ff84b8da5034b51cf3dfdc7a92d2b7c3b6a2f300000000000000000000000074dc4471fa8c8fbe09c7a0c400a0852b0a9d04b200000000000000000000000000000000000000000000000000000000000001f4",
"from": "0x20ff84b8da5034b51cf3dfdc7a92d2b7c3b6a2f3",
"value": "0x0",
"nonce": "0",
"gas": "92705",
"tokenAmount": "6",
"tokenGas": "16368",
"tokenContract": "0x1Af2844A588759D0DE58abD568ADD96BB8B3B6D8"
},
"relayData": {
"gasPrice": "60000000",
"callVerifier": "0x74Dc4471FA8C8fBE09c7a0C400a0852b0A9d04b2",
"domainSeparator": "0xee8f106669d0f00ba21e4d25a7b02337c48fef88b142c67e6c9db7b2bc5b45d3",
"callForwarder": "0xD13377bAaE7D7Ef60bfeb95B6e4E6e66ca371618",
"relayWorker": "0x20bd539d672b605278f98cef7ee94d59bc3f1f17"
}
},
"metadata": {
"relayHubAddress": "0x3bA95e1cccd397b5124BcdCC5bf0952114E6A701",
"signature": "0xa28883f3072c3a5a5f77153540382e8bdfd2c91b74614b9e8cb84ecd3ba588f03c20793e44c567cfefebdf4e34872da83706c7f55af5c467aa47e622d08718511b",
"relayMaxNonce": 5
}
}
Here are some useful resources to help you manually put these structures together:
sign
implementationrelayHandler
implementationEach relayed transaction is signed by a Relay Worker account. The worker accounts are controlled by the Relay Manager. When a relay worker signs and relays a transaction, the cost for that transaction is paid using the funds in that worker’s account. If the transaction is not subsidized, then the worker is compensated with tokens.
Worker accounts must always have some minimum balance to pay gas for the transaction. These balances can be managed by implementing a replenishment strategy. The Relay Manager can use the strategy to top off a relay worker’s account when the balance gets too low.
We provide a default implementation for a replenishment strategy. RIF Relay solution integrators can implement their own replenish strategy.
To implement and use your own replenish strategy:
src/relayserver
, open ReplenishFunction.ts
with a text editor.replenishStrategy
write your new replenish strategy.yarn && yarn prepare
--customReplenish
when running a RIF Relay Server or change the config json file to set customReplenish
on true.Another option is to use RIF RIF Relay through a RIF Relay Provider. A RIF Relay Provider is a web3 provider and all transactions and calls are handled through it. Under the hood, the RIF Relay Provider uses a RIF Relay Client instance to interact with the RIF Relay Server.
Here’s a sample typescript snippet for deploying a Smart Wallet address as well as relaying a transaction through the use of the RIF Relay Provider.
import { RelayProvider, resolveConfiguration } from "@rsksmart/rif-relay";
import Web3 from "web3";
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
const web3 = new Web3("http://localhost:4444");
const smartWalletFactoryAbi = {
// JSON data containing the abi of the smart wallet factory contract
};
const smartWalletFactoryAddress = "0x3bA95e1cccd397b5124BcdCC5bf0952114E6A701"; // the smart wallet factory contract address (can be retrieved from the deployment summary)
const smartWalletIndex = 0; // the index of the smart wallet to use (leave as 0 for default behavior)
const smartWalletAddress = await new web3.eth.Contract(
smartWalletFactoryAbi,
smartWalletFactoryAddress
).methods.getSmartWalletAddress(
account.address,
ZERO_ADDRESS,
smartWalletIndex
).call(); // this will generate an address for the Smart Wallet to be deployed
const relayVerifierAddress = "0x74Dc4471FA8C8fBE09c7a0C400a0852b0A9d04b2"; // the relay verifier contract address (can be retrieved from the deployment summary)
const deployVerifierAddress = "0x1938517B0762103d52590Ca21d459968c25c9E67"; // the deploy verifier contract address (can be retrieved from the deployment summary)
const config = await resolveConfiguration(web3.currentProvider,
{
verbose: true,
onlyPreferredRelays: true,
preferredRelays: ["http://localhost:8090"], // replace with your own if necessary
factory: smartWalletFactoryAddress,
gasPriceFactorPercent: 0,
relayLookupWindowBlocks: 1e5,
chainId: 33, // regtest
relayVerifierAddress,
deployVerifierAddress,
smartWalletFactoryAddress
});
config.relayHubAddress = "0x3bA95e1cccd397b5124BcdCC5bf0952114E6A701"; // the relay hub contract address (can be retrieved from the deployment summary)
const provider = new RelayProvider(web3.currentProvider, config);
provider.addAccount(account); // see note down below
web3.setProvider(provider);
// Deploy Smart Wallet
const tokenContract = "0x0E569743F573323F430B6E14E5676EB0cCAd03D9"; // token address to use on smart wallet
const tokenAmount = "100"; // total token amount for the smart wallet, the smart wallet address should have a balance greater than this number before calling the deploy
const deployTransaction = await provider.deploySmartWallet({
from: account.address,
to: ZERO_ADDRESS,
gas: "0x27100",
value: "0",
callVerifier: deployVerifierAddress,
callForwarder: smartWalletFactoryAddress,
tokenContract,
tokenAmount,
data: "0x",
index: smartWalletIndex,
recoverer: ZERO_ADDRESS,
isSmartWalletDeploy: true,
onlyPreferredRelays: true,
smartWalletAddress
});
// RIF Relay Transaction
const unsigned_tx = {
// some common web3 transaction with the usual parameters, for example:
"nonce": "0x0",
"to": "0xAfA16A8d7a94550079014D537e9440ddB7765d29",
"value": "0x00",
"data": "0x0a798f2400000000000000000000000020ff84b8da5034b51cf3dfdc7a92d2b7c3b6a2f30000000000000000000000001938517b0762103d52590ca21d459968c25c9e6700000000000000000000000000000000000000000000000000000000000001f4",
"chainId": "33"
};
const tokenAmountForRelay = "10"; // how many tokens will be used to pay for the relaying. if left at 0, transaction will be sponsored
const relayTransaction = web3.eth.sendTransaction({
from: account.address,
callVerifier: relayVerifierAddress,
callForwarder: smartWalletAddress,
isSmartWalletDeploy: false,
onlyPreferredRelays: true,
tokenAmount: tokenAmountForRelay,
tokenContract,
...unsigned_tx,
});
Note: in the example above the account
object is assumed as an object containing the address (as string) and the privateKey (as buffer). This is just an example, DO NOT use this in production:
decryptedAccount = web3.eth.accounts.privateKeyToAccount(_privateKey);
const account = {
address: decryptedAccount.address,
privateKey: Buffer.from(
decryptedAccount.privateKey.replaceAll("0x", ""),
"hex"
),
privateKeyString: decryptedAccount.privateKey,
}
Before running this example, you need to know of a few requirements:
tokenAmount
to 0
(or remove it) to make a subsidized deploy instead.acceptToken
, and it can be successfully called only from the contract deployer account (if you are running this in regtest, then accounts[0]
is the owner).You can allow tokens by calling the relay verifier and deploy verifier (for both wallets, smart wallet and custom smart wallet) contracts manually with web3.
Here is an example of how to allow tokens using web3 on truffle console:
const smartWalletDeployVerifierAbi = require("../src/cli/compiled/DeployVerifier.json").abi;
const customSmartWalletDeployVerifierAbi = require("../src/cli/compiled/CustomSmartWalletDeployVerifier.json").abi;
const relayVerifierAbi = require("../src/cli/compiled/RelayVerifier.json").abi;
const relayVerifierAddress = "0x74Dc4471FA8C8fBE09c7a0C400a0852b0A9d04b2"; // the relay verifier contract address (can be retrieved from the deployment summary)
const deployVerifierAddress = "0x1938517B0762103d52590Ca21d459968c25c9E67"; // the deploy verifier contract address (can be retrieved from the deployment summary)
const customRelayVerifierAddress = "0x74Dc4471FA8C8fBE09c7a0C400a0852b0A9d04b2"; // the custom smart wallet relay verifier contract address (can be retrieved from the deployment summary)
const customDeployVerifierAddress = "0x1938517B0762103d52590Ca21d459968c25c9E67"; // the custom smart wallet deploy verifier contract address (can be retrieved from the deployment summary)
const smartWalletDeployVerifier = await new web3.eth.Contract(smartWalletDeployVerifierAbi, deployVerifierAddress);
const smartWalletRelayVerifier = await new web3.eth.Contract(relayVerifierAbi, relayVerifierAddress);
const customSmartWalletDeployVerifier = await new web3.eth.Contract(customSmartWalletDeployVerifierAbi, customDeployVerifierAddress);
const customSmartWalletRelayVerifier = await new web3.eth.Contract(relayVerifierAbi, customRelayVerifierAddress);
const accounts = await web3.eth.getAccounts();
const tokenAddress = "0x0E569743F573323F430B6E14E5676EB0cCAd03D9"; // token address to allow
await smartWalletDeployVerifier.methods.acceptToken(tokenAddress).send({from: accounts[0]});
await smartWalletRelayVerifier.methods.acceptToken(tokenAddress).send({from: accounts[0]});
await customSmartWalletDeployVerifier.methods.acceptToken(tokenAddress).send({from: accounts[0]});
await customSmartWalletRelayVerifier.methods.acceptToken(tokenAddress).send({from: accounts[0]});
Development of an SDK for the RIF RIF Relay project is underway, and will be released soon.
Go to top