# Overview

## What is RallyProtocol? <a href="#rallyprotocol" id="rallyprotocol"></a>

RallyProtocol is an all-in-one web3 mobile toolkit that enables developers to create end-to-end onchain experiences for native mobile apps. RallyProtocol is comprised of two key components:

## Natively embedded wallets <a href="#natively-embedded-wallets" id="natively-embedded-wallets"></a>

1. Create and store secure, self-custodial wallets natively on-device.
2. Enable cloud backup for easy recovery.
3. Import and export wallets.

## **Gasless transactions** <a href="#gasless-transactions" id="gasless-transactions"></a>

1. Provide end-users with gas-free onchain transactions.
2. Enable end-users to transfer tokens, claim NFTs, and even deploy smart contracts from within mobile apps gaslessly.

## Supported mobile platforms <a href="#supported-mobile-platforms" id="supported-mobile-platforms"></a>

* Android version 23 and above
* iOS all versions

## Why RallyProtocol?

1. Native device custody&#x20;
   1. Embedded wallets are generated and stored locally on user's mobile device.
   2. Embedded wallets are backed up to the user's cloud for easy recovery.
2. Mobile-first design
   1. Enables single-app experiences, ensuring that users never need to switch apps.
   2. Enables pop-upless onchain experiences, removing web3 friction.
   3. Native device embedded wallet generation and secure storage.
3. Developer friendly
   1. Eliminates the need for third-party services to facilitate wallet management.&#x20;
   2. Creating natively embedded wallets is permissionless, open-source, and free
   3. Simple to use - Create a natively embedded wallet in just 1 line of code.

## Goal

RallyProtocol serves as a bridge between the intricate world of onchain technology and the familiar realm of UX in native mobile apps. It is designed to empower developers to seamlessly blend in-app experiences with onchain features, reducing web3 friction and enabling them to focus on what they do best—creating delightful mobile apps.

## Mobile SDKs

<table data-column-title-hidden data-view="cards" data-full-width="false"><thead><tr><th></th><th></th><th></th><th data-hidden data-type="content-ref"></th><th data-hidden data-type="files"></th><th data-hidden data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Flutter</strong></td><td></td><td><a href="https://docs.rallyprotocol.com/v/flutter">https://docs.rallyprotocol.com/v/flutter</a></td><td><a href="https://docs.rallyprotocol.com/v/flutter/getting-started/flutter-overview">https://docs.rallyprotocol.com/v/flutter/getting-started/flutter-overview</a></td><td></td><td></td><td><a href="/spaces/RlXS9gkeJHYO9hP4jPvy/pages/yKGFDNrPQsyCazWNc3de">/spaces/RlXS9gkeJHYO9hP4jPvy/pages/yKGFDNrPQsyCazWNc3de</a></td></tr><tr><td><strong>React Native / Expo</strong></td><td></td><td><a href="https://docs.rallyprotocol.com/v/react-native-expo">https://docs.rallyprotocol.com/v/react-native-expo</a></td><td><a href="https://docs.rallyprotocol.com/v/react-native-expo">https://docs.rallyprotocol.com/v/react-native-expo</a></td><td></td><td></td><td><a href="/spaces/gygansI8FVmEVCfbdtlA/pages/yKGFDNrPQsyCazWNc3de">/spaces/gygansI8FVmEVCfbdtlA/pages/yKGFDNrPQsyCazWNc3de</a></td></tr><tr><td><strong>Unity</strong></td><td></td><td><a href="https://docs.rallyprotocol.com/v/unity">https://docs.rallyprotocol.com/v/unity</a></td><td><a href="https://docs.rallyprotocol.com/v/unity/getting-started/unity-overview">https://docs.rallyprotocol.com/v/unity/getting-started/unity-overview</a></td><td></td><td></td><td><a href="/spaces/BjPYra5OMZmag6kpj6D5/pages/yKGFDNrPQsyCazWNc3de">/spaces/BjPYra5OMZmag6kpj6D5/pages/yKGFDNrPQsyCazWNc3de</a></td></tr></tbody></table>

##

<br>


# Embedded wallets

Create and manage secure, on-device wallets natively embedded in your app.

### Features:

1. Instant natively embedded wallet creation.
2. Secure on-device storage utilizing the device's secure enclave.&#x20;
3. Cloud backup and recovery.
4. Import and export mnemonic.
5. Developer-friendly APIs for signing both traditional user transactions and gasless transactions on a mobile device, for use with [gasless transactions](/rallyprotocol/gasless-transasctions).&#x20;

### With embedded wallets, you can:&#x20;

1. Create embedded wallets for end-users anywhere in the user journey.
2. Create embedded wallets for end-users under the hood, so they don't have to sign up or switch apps.
3. Enable cross-device wallet access when using the same cloud account.
4. Allow importing and exporting of wallets.

### Embedded smart accounts \[coming soon]

1. Dev friendly API to deploy and manage on-chain Smart Accounts wallets, utilizing the EOA generated by the SDK.
2. Developer friendly API to sign gasless transactions on a mobile device to utilize gasless transactions.&#x20;

## Mobile SDKS

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Flutter</strong></td><td></td><td><a href="https://docs.rallyprotocol.com/v/flutter">https://docs.rallyprotocol.com/v/flutter</a></td><td></td></tr><tr><td><strong>React Native / Expo</strong></td><td></td><td><a href="https://docs.rallyprotocol.com/v/react-native-expo/">https://docs.rallyprotocol.com/v/react-native-expo/</a></td><td></td></tr><tr><td><strong>Unity</strong></td><td></td><td><a href="https://docs.rallyprotocol.com/v/unity">https://docs.rallyprotocol.com/v/unity</a></td><td></td></tr></tbody></table>


# Gasless transasctions

Gasless transactions are a fully hosted transactions infrastruture for signing, validating, and relaying transactions to the blockchain.&#x20;

It removes the requirement for developers to manage nodes, bundlers, and deploy contracts. Additionally, it takes care of funding balances for gasless transactions, monitors transactions, and provides enhanced secops.&#x20;

## With gasless transactions, you can:&#x20;

1. Enable end-users to transfer tokens, claim NFTs, and even deploy smart contracts from within mobile apps gaslessly.
2. Use outside native-mobile environments - developers can send transactions from their backend, web dapp, desktop, etc and get all the benefits listed above.

## Get an API key for gasless transactions

An API key is required to use the gasless transaction infrastructure. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;


# Example dapps


# Claim NFT dapp

{% hint style="info" %}
Here is an example dapp using Flutter & Unity
{% endhint %}

This example dapp demonstrates how to use the RallyMobile SDK in a mobile app. The dapp enables users to generate an EOA, which is then securely encrypted, saved on the device, and can optionally be backed up to the cloud. Additionally, it facilitates gasless transactions, allowing users to claim an ERC20 token and use it to purchase an NFT, all without incurring gas fees.

<https://github.com/rally-dfs/flutter-example-app>\
\
<https://github.com/rally-dfs/unity-example>


# Videos


# Intro to embedded wallets

{% hint style="info" %}
Here is a video using React Native Expo.
{% endhint %}

{% embed url="<https://youtu.be/8rWuG_8pV74?si=bRPEptwJxbw5_d8R>" %}

Get started by building an React Native Expo App.  This mobile web3 dapp will show you how to integrate the React Native SDK into your Expo app, create an embedded wallet seamlessly, and create the base application you need to get started building your Mobile Web3 dapp.


# Intro to gasless transactions

{% hint style="info" %}
Here is a video using React Native Expo.
{% endhint %}

{% embed url="<https://youtu.be/5EWSdlpnBlY?si=2OC1OXyyhF5Rw7aR>" %}

Learn how to perform gasless transactions. Build React Native Expo App that transfers tokens without a user needing to sign for any transactions. Learn how to integrate the SDK into your Expo app and understand the fundamentals of implementing easy gasless transactions for your dapp users.


# RallyProtocol dashboard basics

{% hint style="info" %}
Here is a video using React Native Expo.
{% endhint %}

{% embed url="<https://youtu.be/fvsyXdpqHU0?si=v-LF9_ZXWgRs6vu8>" %}

Understand how to setup an account with RallyProtocol (<https://app.rallyprotocol.com/signup>) and understand the fundamentals how an account and gasless transactions can be made.

This video will guide you through setting up an account, initializing an API Token, and how to quickly interact with the API via a NodeJS Script.


# Guides


# Add ERC2771Recipient

## Overview <a href="#overview" id="overview"></a>

Contracts that are [ERC2771](https://eips.ethereum.org/EIPS/eip-2771) compatible are natively supported by the RallyMobile SDK to perform gasless transactions. Contracts that are not ERC2771 compatible, but are whitelisted by us, can also perform gasless transactions

## If you are able to update the contract: <a href="#if-you-are-able-to-update-the-contract" id="if-you-are-able-to-update-the-contract"></a>

1. [Add `ERC2771Recipient` to your token contract](https://docs.opengsn.org/contracts/#install-opengsn-contracts) to make it compatible with our SDK.
2. Set the trusted forwarder:
   1. Amoy: `0x0ae8FC9867CB4a124d7114B8bd15C4c78C4D40E5`
   2. Mainnet: `0xB2b5841DBeF766d4b521221732F9B618fCf34A87`
3. [View code example](https://gist.github.com/DonGambas/230389f11dc5f10137a510527175e01e)

For ERC721 contracts, follow [Opensea's guide](https://docs.opensea.io/docs/polygon-basic-integration) to enable meta transactions for your NFTs.

## If you are unable to update the contract: <a href="#if-you-are-unable-to-update-the-contract" id="if-you-are-unable-to-update-the-contract"></a>

Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and [gasless transaction method](https://docs.rallyprotocol.com/v/flutter/gasless-transactions/guides/determining-the-gasless-transaction-method) (`permit` or `executeMetaTransaction`) to have it reviewed and whitelisted by our team.


# Gasless transaction methods

## **Overview** <a href="#overview" id="overview"></a>

Contracts that have the `executeMetaTransaction` or the `Permit` method for gasless transactions are natively supported by our SDK to perform gasless transactions.

Does your contract use a different gasless transaction method? Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and method to find a solution.

## executeMetaTransaction <a href="#executemetatransaction" id="executemetatransaction"></a>

Here is how to check if your contract supports the executeMetaTransaction method for gasless transactions:

## Method 1 <a href="#method-1" id="method-1"></a>

Verifying the Contract Name

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Code tab
4. If the Contract Name displays `UChildERC20Proxy`, it supports the `executeMetaTransaction` method.

Here is an example of the RLY Token on [Polygonscan](https://polygonscan.com/token/0x76b8d57e5ac6afac5d415a054453d1dd2c3c0094#code)

![](https://docs.rallyprotocol.com/~gitbook/image?url=https%3A%2F%2F112151647-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FRlXS9gkeJHYO9hP4jPvy%252Fuploads%252FDMDDu7e3uQMC2r2PQfNv%252FScreenshot%25202024-01-30%2520at%25201.44.31%25E2%2580%25AFPM.png%3Falt%3Dmedia%26token%3D4bd8bbcc-ef73-4a39-b867-5479d585292b\&width=768\&dpr=4\&quality=100\&sign=9a1120a2\&sv=1)

UChildERC20Proxy contracts inherit from the UChildERC20 contract that natively supports the executeMetaTransaction method.

## Method 2 <a href="#method-2" id="method-2"></a>

Verify the implementation contract

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Read Contract
4. Navigate to the contract address under the implementation dropdown
5. Click on the Contract tab
6. Click on the Write Contract
7. Check that the contract has the executeMetaTransaction method

Here is an example of the RLY Token's implementation contract on [Polygonscan](https://polygonscan.com/address/0x93d4a27C64b9Dba29ed328c5801120a68B331C30#writeContract)

![](https://docs.rallyprotocol.com/~gitbook/image?url=https%3A%2F%2F112151647-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FRlXS9gkeJHYO9hP4jPvy%252Fuploads%252F4iPazVw7Ng2zr1nwuMsq%252FScreenshot%25202024-01-31%2520at%25201.38.01%25E2%2580%25AFPM.png%3Falt%3Dmedia%26token%3D2f78e7ac-0e1b-4807-91a1-51c3982c26c6\&width=768\&dpr=4\&quality=100\&sign=9239ee54\&sv=1)

## Permit <a href="#permit" id="permit"></a>

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Write Contract
4. Check that the contract has the permit method

Here is an example of the TAKI Token's implementation contract on [Polygonscan](https://polygonscan.com/token/0xe78aee6ccb05471a69677fb74da80f5d251c042b#writeContract)

![](https://docs.rallyprotocol.com/~gitbook/image?url=https%3A%2F%2F112151647-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FRlXS9gkeJHYO9hP4jPvy%252Fuploads%252FCPSRSKrMcFiWxbG4uL6Q%252FScreenshot%25202024-01-31%2520at%25201.39.47%25E2%2580%25AFPM.png%3Falt%3Dmedia%26token%3Dbe49261c-4c88-41d5-98cc-205968430fff\&width=768\&dpr=4\&quality=100\&sign=c0fc916d\&sv=1)[<br>](https://docs.rallyprotocol.com/v/flutter/gasless-transactions/guides/add-erc2771recipient)


# Supported tokens

The following is a list of tokens that have been whitelisted for gasless token transfers. If you'd like your chosen token to be whitelisted, please contact us on [Discord](https://discord.gg/rlynetwork).

<table><thead><tr><th width="177">Token</th><th>Address</th></tr></thead><tbody><tr><td>USDC.e</td><td>0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174</td></tr><tr><td>RLY</td><td>0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094</td></tr><tr><td>TAKI</td><td>0xd872b7fFca41a67eDA85b04A9185c6b270006b58</td></tr><tr><td>TAKITOKEN</td><td>0xe78aeE6CCb05471a69677fB74da80F5d251c042B</td></tr><tr><td>WINS</td><td>0x1A49E59aB7101D6a66D6B2Fa6d09932079cC80EC</td></tr><tr><td>SAVE</td><td>0xE537C642aC8c3136EB8914A047ab3B232F9e695d</td></tr></tbody></table>


# Community libraries

The following is a list of open-source community-supported libraries. These libraries aren’t supported or maintained by RallyProtocol, so we cannot guarantee completeness. If you have a library you'd like to contribute or have one to share, please let us know on [discord](https://discord.gg/rlynetwork).

| Language | Project                                        | Author |
| -------- | ---------------------------------------------- | ------ |
| Python   | [py-rally](https://github.com/Tee-py/py-rally) | teepy  |
|          |                                                |        |
|          |                                                |        |


# FAQs

## Embedded wallets <a href="#are-embedded-wallets-a-separate-application" id="are-embedded-wallets-a-separate-application"></a>

#### Are embedded wallets a separate application? <a href="#are-embedded-wallets-a-separate-application" id="are-embedded-wallets-a-separate-application"></a>

An embedded wallet is not a standalone wallet app like Metamask or Coinbase Wallet. Instead, it serves as the wallet layer with which your dApp provides an interface to interact with the blockchain. Simply put, your dapp is the wallet.

#### Can I use the same crypto account for testnet (Amoy) and mainnet Polygon? <a href="#can-i-use-the-same-crypto-account-for-testnet-amoy-and-mainnet-polygon" id="can-i-use-the-same-crypto-account-for-testnet-amoy-and-mainnet-polygon"></a>

A crypto account on Amoy will work on Mainnet Polygon and vice versa. Please make sure you select the correct network if funds are not appearing correctly.

#### Is the SDK open source? <a href="#is-the-sdk-open-source" id="is-the-sdk-open-source"></a>

Yes! Visit us at <https://github.com/rally-dfs/>

## Gasless Transactions

#### How can I tell if a token is supported for sponsored gasless transactions? <a href="#how-can-i-tell-if-a-token-is-supported-for-sponsored-gasless-transactions" id="how-can-i-tell-if-a-token-is-supported-for-sponsored-gasless-transactions"></a>

We support tokens that use the following methods to enable gasless transactions:

`executeMetaTransaction`

ERC20 tokens using the official [Polygon token mapper](https://mapper.polygon.technology/) prior to January 2023 automatically inherited `executeMetaTransact`. This can be verified by checking that a token's Contract Name is *UChildERC20Proxy*. [RLY Token for reference.](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094#code)

`permit()`

#### Are all contracts supported? <a href="#are-all-contracts-supported" id="are-all-contracts-supported"></a>

Not all contracts are supported by our SDK out of the box. Here are some error messages you may encounter:

1. `Paymaster rejected in server: isTrustedForwarder: reverted.`
   1. In order to enable sponsored gasless transactions, the contract must be [ERC2771 compatible](https://docs.opengsn.org/contracts/#install-opengsn-contracts) or whitelisted on our paymaster. View the [Add ERC2771Recipient guide](https://docs.rallyprotocol.com/v/flutter/gasless-transactions/guides/add-erc2771recipient).
2. `Paymaster rejected in server: invalid forwarder for recipient.`
   1. External contracts are required to set the trusted forwarder address to
      1. Amoy: `0x0ae8FC9867CB4a124d7114B8bd15C4c78C4D40E5`
      2. Mainnet: `0xB2b5841DBeF766d4b521221732F9B618fCf34A87`
   2. View the [code example](https://gist.github.com/DonGambas/230389f11dc5f10137a510527175e01e)

#### Do you support tokens that do not use `executeMetaTransaction` and `permit()`for sponsored gasless transactions? <a href="#do-you-support-tokens-that-do-not-use-executemetatransaction-and-permit-for-sponsored-gasless-transa" id="do-you-support-tokens-that-do-not-use-executemetatransaction-and-permit-for-sponsored-gasless-transa"></a>

Please contact us at <partnerships@rallyprotocol.com> in order to find a solution that works.

#### Will you support a specific blockchain? <a href="#will-you-support-a-specific-blockchain" id="will-you-support-a-specific-blockchain"></a>

Please contact us at <partnerships@rallyprotocol.com> in order to find a solution that works.

[<br>](https://docs.rallyprotocol.com/v/flutter/gasless-transactions/guides/determining-the-gasless-transaction-method)

[<br>](https://docs.rallyprotocol.com/v/flutter/embedded-wallets/how-it-works)


# Overview

## What is RallyProtocol? <a href="#rallyprotocol" id="rallyprotocol"></a>

RallyProtocol is an all-in-one web3 mobile toolkit that enables developers to create end-to-end onchain experiences for native mobile apps. RallyProtocol is comprised of two key components:

## Natively embedded wallets <a href="#natively-embedded-wallets" id="natively-embedded-wallets"></a>

1. Create and store secure, self-custodial wallets natively on-device and in your app.
2. Enable cloud backup for easy recovery.
3. Import and export wallets.

## **Gasless transactions** <a href="#gasless-transactions" id="gasless-transactions"></a>

1. Provide end-users with gas-free onchain transactions.
2. Enable end-users to transfer tokens, claim NFTs, and even deploy smart contracts from within mobile apps gaslessly.

## Supported mobile platforms <a href="#supported-mobile-platforms" id="supported-mobile-platforms"></a>

* Android version 23 and above
* iOS all versions

## Goal <a href="#goal" id="goal"></a>

RallyProtocol serves as a bridge between the intricate world of onchain technology and the familiar realm of UX in native mobile apps. It is designed to empower developers to seamlessly blend in-app experiences with onchain features, reducing web3 friction and enabling them to focus on what they do best—creating delightful mobile apps.

### &#x20; <a href="#mobile-sdks" id="mobile-sdks"></a>


# Installation

## Requirements

* Android version 23 and above
* iOS all versions

## Natively embedded wallets

### Install the Flutter SDK&#x20;

```dart
flutter pub add rly_network_flutter_sdk
```

<https://pub.dev/packages/rly_network_flutter_sdk>

### Get an API key for gasless transactions

An API key is required to use infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;


# Overview

Wallet management is facilitated by the [SDK](/flutter#rallyprotocol), serving as the accounts layer for on-device EOA and on-chain Smart Account operations. It consists of the following features:

### Embedded wallets

1. Secure EOA wallet generation & on-device storage, cloud backup & recovery, and the ability to import external EOA wallets.&#x20;
2. Developer-friendly APIs for signing both traditional user transactions and [gasless transactions](/flutter/gasless-transactions/overview) on a mobile device.

### Smart Account wallets \[coming soon]

1. Dev friendly API to deploy and manage on-chain Smart Accounts wallets, utilizing the EOA generated by the SDK.
2. Developer friendly API to sign [gasless transactions](/flutter/gasless-transactions/overview) on a mobile device to utilize gasless transactions.


# Getting started

## Requirements

* Android version 23 and above
* iOS all versions

## Install the Flutter SDK&#x20;

```dart
flutter pub add rly_network_flutter_sdk
```

<https://pub.dev/packages/rly_network_flutter_sdk>

##


# Create wallets

## Generate an EOA wallet&#x20;

EOA wallets are generated on-device at device + application level and automatically backed up to the user's cloud account.

```dart
import 'package:rly_network_flutter_sdk/wallet_manager.dart';

//create an account
final account = await WalletManager.getInstance().createWallet();
```

## Get the public address

```dart
import 'package:rly_network_flutter_sdk/wallet_manager.dart';

//get current user account address
final address = await WalletManager.getInstance().getPublicAddress();
```

{% hint style="info" %}
EOA wallets share the same address for both Amoy and Mainnet Polygon.&#x20;
{% endhint %}

## Get the seed phrase

Display the seed phrase to enable users to export their EOA wallet.&#x20;

```dart
import 'package:rly_network_flutter_sdk/wallet_manager.dart';
final mnemonic = WalletManager.getInstance().getAccountPhrase();
```


# Manage wallets

## Import an existing wallet

Optionally import a mnemonic string of an existing EOA wallet and store it on the user's device.

```dart
final someExistingMnemonic = "some value from other product or IO...";
final wallet = await WalletManager.getInstance().importExistingWallet(someExistingMnemonic);
```

## Reading balances

View the Gasless Transaction section on [balances](/flutter/gasless-transactions/using-with-embedded-wallets#read-balances).

## Recovery and persistence

By default, EOA wallets are generated on-device and backed up to the user's cloud.  Read about [key persistence](/flutter/embedded-wallets/how-it-works#persistence) here.

## Gasless transactions

View the [Gasless Transaction](/flutter/gasless-transactions/overview) section on how to transfer tokens, deploy contracts, and transact with contracts gaslessly.

## Full API Reference&#x20;

View the [full API reference](https://pub.dev/documentation/rly_network_flutter_sdk/latest/)


# How it works

## EOA wallets

EOA wallets, or Externally Owned Accounts, serve as the primary interface for users to interact with web3. The creation of an EOA wallet involves generating a private key, which acts as a unique signature providing access and control to the account. EOA wallets generated are user-custodied, meaning that they are created and stored on a user's device.&#x20;

## Generating EOAs

The EOA wallet generation process uses the [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) mnemonic generation to create a hierarchical-deterministic (HD) wallet. This mnemonic is used to extract a private key from the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) path "m/44'/60'/0'/0/0" which is the Ethereum default path.

This generation process uses the same methodologies as [ethers js](https://docs.ethers.org/v5/api/utils/hdnode/), with a couple of key differences:

* The generation of the mnemonic and the extraction of the private key are both done in native code, with C being used on the iOS side and Kotlin on the Android side.
* The private key from our [NativeModule](https://reactnative.dev/docs/native-modules-intro) is passed over to the JavaScript side to instantiate an ethers js wallet using the private key.

The generated ethers js wallet does not have access to the mnemonic, it is not able to create a second wallet with a different path.&#x20;

## Encryption & Storage

Private key storage makes use of hardware encryption and low level OS key storage technology on device.&#x20;

## Persistence&#x20;

By default, createAccount stores the private keys at the device + cloud level. [ ](https://docs.rallyprotocol.com/rally-mobile-sdk/full-api-reference#createaccount)

### **iOS**

* **Device + iCloud storage**
  * Requirements
    * Device must be logged into an Apple ID to back up keys to iCloud.
  * Use cases
    * Logging on to different devices with the same Apple ID and mobile app will restore the original crypto account created by that Apple ID.&#x20;
    * Deleting and reinstalling a mobile application will restore the original crypto account.
* **Device only storage**
  * Use cases
    * Deleting and reinstalling a mobile application will restore the original crypto account.&#x20;

### **Android**

* **Device + Google Cloud storage**
  * Requirements
    * End to End encryption is supported on devices running Android 9 and above.&#x20;
    * Device must have a screen lock set with a PIN, pattern, or password for end to end encryption to be enabled.
    * Device must be logged into a Google account to back up keys to Google Cloud.&#x20;
  * Use cases
    * Logging on to different devices with the same Google account and mobile app will restore the original crypto account created by that Google account.
    * Deleting and reinstalling a mobile application will restore original crypto account.
* **Device only storage**
  * Use cases
    * Deleting and reinstalling a mobile application will generate a new private key due to SharedPreferences being deleted along with the app bundle. **The original crypto account will no longer be recoverable**.&#x20;


# Natively embedded wallets


# Embedded smart accounts

&#x20;


# Getting Started

Coming soon


# Overview

## Account Abstraction (ERC-4337)

Smart Account wallets, also known as Smart Contract Accounts/Wallets, utilize Account Abstraction (ERC-4337) to create a wallet managed through a smart contract. With the programmability inherent in smart contracts, they provide capabilities that EOA wallets lack, such as enhanced security, customizable limits, flexible recovery, and more.&#x20;

Creating and managing a Smart Account wallet initially requires an EOA wallet. RallyProtocol streamlines this process by utilizing the EOA wallet generated by the SDK.

<br>


# Overview

Gasless transactions are facilitated by the [RallyTransact](/flutter#rallyprotocol), functioning as the fully hosted transactions infrastruture for signing, validating, and relaying transactions to the blockchain.&#x20;

RallyTransact removes the requirement for developers to manage nodes, bundlers, and deploy contracts. Additionally, RallyTransact takes care of funding balances for gasless transactions, monitors transactions, and provides enhanced secops.&#x20;

Although RallyMobile provides dev friendly APIs to interact with the transaction infrastructure, RallyTransact can also be used [outside of the SDK](/flutter/gasless-transactions/using-without-embedded-wallets). Devs can send transactions from their backend, web dapp, desktop, etc and get all the benefits listed above.

###


# Getting Started

## Requirements

This section focuses on enabling gasless transactions on mobile device using natively embedded wallets generated by the [SDK](/flutter/embedded-wallets/getting-started). If you would like to utilize gasless transactions on a non-native mobile device such as web app, or backend, visit [Using without embedded wallets](/flutter/gasless-transactions/using-without-embedded-wallets).&#x20;

## Get an API key for gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;

## Network

This section utilizes Polygon's testnet Amoy network. To switch between Amoy and Mainnet Polygon, update the network config using <https://github.com/rally-dfs/flutter-sdk/blob/main/lib/src/network.dart>.

## RLY Token&#x20;

In this section, we leverage the RLY token ([explorer](https://www.oklink.com/amoy/token/0x846D8a5fb8a003b431b67115f809a9B9FFFe5012)) to simplify the developer experience. We maintain a token faucet and manage token contracts to ensure that developers can execute their first gasless transaction without the extra steps.

Once you are ready to use your preferred token on production, refer to the subsequent 'your chosen ERC-20 token' sections.&#x20;


# Read balances

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Check balances with RLY token

{% hint style="success" %}
In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) and our faucet to enable developers to read on-chain balances in just seconds. Use the following code to claim test RLY tokens and check the RLY balance of wallets.
{% endhint %}

```dart
import 'package:rly_network_flutter_sdk/network.dart';

//get Amoy config
final amoy = rlyAmoyNetwork;

//add your API Key
amoy.setApiKey(env.API_KEY);

//claim 10 test RLY tokens gaslessly for testing
await amoy.claimRly();

//get balance of RLY token on Amoy testnet for display purposes
await amoy.getBalance();

//get exact balance of RLY token on Amoy testnet for transfer purposes
await amoy.getExactBalance();
```

## Check balances with your chosen ERC-20 token

Use the following code to check the chosen token balance of wallets.&#x20;

```dart
import 'package:rly_network_flutter_sdk/network.dart';

//get Amoy config
final amoy = rlyAmoyNetwork;

//add your API Key
amoy.setApiKey(env.API_KEY);

//hex address of token contract.
final tokenAddress = "0x...." 

//get balance of any ERC20 token on Amoy testnet for display purposes
await amoy.getBalance(tokenAddress);

//get exact balance of any ERC20 token on Amoy testnet for transfer purposes
await amoy.getExactBalance(tokenAddress);
```

<br>


# Transfer tokens gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Gasless transactions with RLY token&#x20;

{% hint style="success" %}
In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) and our faucet to enable developers to complete a gasless token transfer in just seconds. Use the following code to claim test RLY tokens and transfer it to a destination wallet of your choosing.&#x20;
{% endhint %}

<pre class="language-dart"><code class="lang-dart">import 'package:rly_network_flutter_sdk/network.dart';

//get Amoy config
final amoy = rlyAmoyNetwork;

//add your API Key
amoy.setApiKey(env.API_KEY);

//claim 10 test RLY tokens gaslessly for testing
await amoy.claimRly();

//destination address for testing
final destinationAddress = "0x8D9F6c7Ab1316B613BDdcD1fDbac6025A1323569" 

<strong>//transfer RLY token using ExecuteMetaTransaction
</strong>await amoy.transferExact(
    destinationAddress, 
    BigInt.parse(1), 
    MetaTxMethod.ExecuteMetaTransaction
    );    
</code></pre>

## Getting started with your chosen ERC-20 token

### Prerequisites&#x20;

1. **Contract is compatible with the RallyMobile SDK**
   1. Contracts that are [ERC2771 compatible](/flutter/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract) are natively supported by the RallyMobile SDK. Those that are not ERC2771 compatible must be whitelisted, [view the guide here.](/flutter/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract)
2. **Contract natively supports gasless transactions**
   1. Contracts that have the `executeMetaTransaction` or `Permit` method for gasless transactions are natively supported by our SDK. To determine which method is supported by your contract, [view the guide here](/flutter/gasless-transactions/guides/determining-the-gasless-transaction-method).&#x20;

{% hint style="info" %}
View our [supported tokens list](https://docs.rallyprotocol.com/resources/supported-tokens) to see if your chosen token is already supported.
{% endhint %}

## Gasless transactions with your chosen ERC-20 token

Use the following code to transfer your chosen token gaslessly.

```dart
import 'package:rly_network_flutter_sdk/network.dart';

//get Amoy config for rally protocol sdk
final amoy = rlyAmoyNetwork;

//add your API Key
amoy.setApiKey(env.API_KEY);

//destination address 
final destinationAddress = "0x...." 

//hex address of token contract
final tokenAddress = "0x...." 

//transfer tokens if the token contract supports executeMetaTransaction 
await amoy.transferExact(
    destinationAddress, 
    BigInt.parse(1), 
    MetaTxMethod.ExecuteMetaTransaction,
    {tokenAddress}
);

//transfer tokens if the token contract supports Permit
await amoy.transferExact(
    destinationAddress, 
    BigInt.parse(1), 
    MetaTxMethod.Permit,
    {tokenAddress}
);
```


# Deploy a contract gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Deploy contracts with the relay method

To gaslessly deploy a contract, create a [GSN transaction object](https://github.com/rally-dfs/rly-network-mobile-sdk/blob/736e1de0859bec5230abba80e978f76f43fe2123/src/gsnClient/utils.ts#L16)  and use the `relay()` method to send the transaction to our relayer.

```dart
final gsnTx = GsnTransactionDetails(
    from: accountAddress,
    data: tx.data,
    value: "0",
    to: contractAddress,
    gas: gas.toString(),
    maxFeePerGas: maxFeePerGas.toString(),
    maxPriorityFeePerGas: maxPriorityFeePerGas.toString(),
    );

await amoy.relay(gsnTx)


// relay arbitrary tx through our gasless relayer. see complete Flutter example at
// https://github.com/rally-dfs/flutter-example-app/tree/main/app/lib/services/nft.dart
```


# Transact with a contract gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Contract requirements &#x20;

1. **Contract is compatible with the RallyMobile SDK**
   1. Contracts that are [ERC2771 compatible](/flutter/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract) are natively supported by the RallyMobile SDK. Those that are not ERC2771 compatible must be whitelisted by us, [view the guide here.](/flutter/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract)

## Transact with contracts with the relay method

To gaslessly execute a transaction with a contract, create a [GSN transaction object](https://github.com/rally-dfs/rly-network-mobile-sdk/blob/736e1de0859bec5230abba80e978f76f43fe2123/src/gsnClient/utils.ts#L16) and use the `relay()` method to send the transaction to our relayer.

```dart
final gsnTx = GsnTransactionDetails(
    from: accountAddress,
    data: tx.data,
    value: "0",
    to: contractAddress,
    gas: gas.toString(),
    maxFeePerGas: maxFeePerGas.toString(),
    maxPriorityFeePerGas: maxPriorityFeePerGas.toString(),
    );

await amoy.relay(gsnTx)


// relay arbitrary tx through our gasless relayer. see complete example at
// https://github.com/rally-dfs/flutter-example-app/tree/main/app/lib/services/nft.dart
```


# Using with embedded wallets


# Using without embedded wallets


# Getting started

## Overview

Gasless transactions can be utilized outside of the mobile device such as from a backend, web dapp, desktop, etc. Submitting transactions is a 2 step process:&#x20;

1. Sign a transaction on the client (web, backend, etc)
2. Create a HTTP request containing a JSON payload with details about the transaction you'd like to relay.&#x20;

## Get an API key for gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;

## Using an existing Gas Station Network Client Library.

Our hosted transaction infrastructure honors the [Gas Station Network](https://opengsn.org/) interface with the addition of an authentication HTTP header containing your API key. If you're already using a GSN client the following steps should be all you need to change:&#x20;

1. Set `https://api.rallyprotocol.com` as your relay URL.&#x20;
2. Add the following header to the HTTP request `Authorization: Bearer <YOUR_API_TOKEN>`&#x20;

## Submitting Requests Without a GSN Library

*Examples coming soon. Reach out to us on* [*Discord*](https://discord.gg/rlynetwork) *and we'll happily walk you through it*


# Typescript Example

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Required payload:

For definitions, refer to: <https://docs.opengsn.org/jsdoc/jsdoc-client.html>

```typescript
// This typescript type represents the full JSON payload required
// by our relay API endpoint. 
type RlyTransactionPayload = {
  relayRequest: {
    request: {
      from: string;
      to: string;
      value: string;
      gas: string;
      nonce: string;
      data: string;
      validUntilTime: string;
    };
    relayData: {
      maxFeePerGas: string;
      maxPriorityFeePerGas: string;
      transactionCalldataGasUsed: string;
      relayWorker: string;
      paymaster: string;
      forwarder: string;
      paymasterData: string;
      clientId: string;
    };
  };
  metadata: {
    maxAcceptanceBudget: string;
    relayHubAddress: string;
    signature: string;
    approvalData: string;
    relayMaxNonce: number;
    relayLastKnownNonce: number;
    domainSeparatorName: string;
    relayRequestId: string;
  };
};

// Usage example:
const examplePayload: RlyTransactionPayload = {
  relayRequest: {
    request: {
      from: '0x1e7D51f413Dd60508352846fCa063f05cB423F8C',
      to: '0x1C7312Cb60b40cF586e796FEdD60Cf243286c9E9',
      value: '0',
      gas: '73380',
      nonce: '1',
      data: '0x0c53c51c0000000000000000000000001e7d51f413dd60508352846fca063f05cb423f8c00000000000000000000000000000000000000000000000000000000000000a017fa37ac5967c642ba02b250f188da83d5613d0e5c0286241ff9aece0503662c2a6146626ce6d182f8b277723d3d638ec12c1fed1f15954ab344a5ecdfba5fd2000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000e75625f0c8659f18caf845eddae30f5c2a49cb000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000',
      validUntilTime: '1691256746',
    },
    relayData: {
      maxFeePerGas: '1650000000',
      maxPriorityFeePerGas: '1650000000',
      transactionCalldataGasUsed: '19296',
      relayWorker: '0xb9950b71ec94cbb274aeb1be98e697678077a17f',
      paymaster: '0x8b3a505413Ca3B0A17F077e507aF8E3b3ad4Ce4d',
      forwarder: '0xB2b5841DBeF766d4b521221732F9B618fCf34A87',
      paymasterData: '0x',
      clientId: '1',
    },
  },
  metadata: {
    maxAcceptanceBudget: '285252',
    relayHubAddress: '0xe213A20A9E6CBAfd8456f9669D8a0b9e41Cb2751',
    signature:
      '0xd3f7b35fc4985b2a52e4c1b47091b1f5ba947a2159d2da07c7c33b3e54fd5d5e3166847e5a6460fb9e69a90e6531399c5c258e332b6fe1c8157c8e06d527452e1b',
    approvalData: '0x',
    relayMaxNonce: 1424,
    relayLastKnownNonce: 1421,
    domainSeparatorName: 'GSN Relayed Transaction',
    relayRequestId:
      '0x000000001204aa7c5e04714775ca00cad9d6f298f8d43caa291b8f8d1b237e39',
  },
};
const authHeader = `Bearer: ${yourApiToken}`;
const response = axios.post(
  'https://api.rallyprotocol.com/relay',
  examplePayload,
  { headers: { Authorization: authHeader } }
);
```

## Example from the SDK client:&#x20;

<https://github.com/rally-dfs/rly-network-mobile-sdk/blob/main/src/gsnClient/gsnClient.ts>

```javascript
import type { GsnTransactionDetails, AccountKeypair } from './utils';

import type { RelayRequest } from './EIP712/RelayRequest';
import { handleGsnResponse } from './gsnTxHelpers';

import axios from 'axios';

import type { NetworkConfig } from 'src/network_config/network_config';

import {
  estimateGasWithoutCallData,
  estimateCalldataCostForRequest,
  getSenderNonce,
  signRequest,
  getRelayRequestID,
  //getClientId,
} from './gsnTxHelpers';

import { ethers, providers } from 'ethers';

interface GsnServerConfigPayload {
  relayWorkerAddress: string;
  relayManagerAddress: string;
  relayHubAddress: string;
  ownerAddress: string;
  minMaxPriorityFeePerGas: string;
  maxMaxFeePerGas: string;
  minMaxFeePerGas: string;
  maxAcceptanceBudget: string;
  chainId: string;
  networkId: string;
  ready: boolean;
  version: string;
}

const authHeader = (config: NetworkConfig) => {
  return {
    Authorization: `Bearer ${config.relayerApiKey || ''}`,
  };
};

const updateConfig = async (
  config: NetworkConfig,
  transaction: GsnTransactionDetails
) => {
  const response = await axios.get(`${config.gsn.relayUrl}/getaddr`, {
    headers: authHeader(config),
  });
  const serverConfigUpdate = response.data as GsnServerConfigPayload;

  config.gsn.relayWorkerAddress = serverConfigUpdate.relayWorkerAddress;

  setGasFeesForTransaction(transaction, serverConfigUpdate);

  return { config, transaction };
};

const setGasFeesForTransaction = (
  transaction: GsnTransactionDetails,
  serverConfigUpdate: GsnServerConfigPayload
) => {
  const serverSuggestedMinPriorityFeePerGas = parseInt(
    serverConfigUpdate.minMaxPriorityFeePerGas,
    10
  );

  const paddedMaxPriority = Math.round(
    serverSuggestedMinPriorityFeePerGas * 1.4
  );
  transaction.maxPriorityFeePerGas = paddedMaxPriority.toString();

  //Special handling for Amoy because of quirk with gas estimate returned by GSN for Amoy
  if (serverConfigUpdate.chainId === '80001') {
    transaction.maxFeePerGas = paddedMaxPriority.toString();
  } else {
    transaction.maxFeePerGas = serverConfigUpdate.maxMaxFeePerGas;
  }
};

const buildRelayRequest = async (
  transaction: GsnTransactionDetails,
  config: NetworkConfig,
  account: AccountKeypair,
  web3Provider: providers.JsonRpcProvider
) => {
  //remove call data cost from gas estimate as tx will be called from contract
  transaction.gas = estimateGasWithoutCallData(
    transaction,
    config.gsn.gtxDataNonZero,
    config.gsn.gtxDataZero
  );

  const secondsNow = Math.round(Date.now() / 1000);
  const validUntilTime = (
    secondsNow + config.gsn.requestValidSeconds
  ).toString();

  const senderNonce = await getSenderNonce(
    account.address,
    config.gsn.forwarderAddress,
    web3Provider
  );

  const relayRequest: RelayRequest = {
    request: {
      from: transaction.from,
      to: transaction.to,
      value: transaction.value || '0',
      gas: parseInt(transaction.gas, 16).toString(),
      nonce: senderNonce,
      data: transaction.data,
      validUntilTime,
    },
    relayData: {
      maxFeePerGas: transaction.maxFeePerGas,
      maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
      transactionCalldataGasUsed: '',
      relayWorker: config.gsn.relayWorkerAddress,
      paymaster: config.gsn.paymasterAddress,
      forwarder: config.gsn.forwarderAddress,
      paymasterData: transaction.paymasterData?.toString() || '0x',
      clientId: '1',
    },
  };

  const transactionCalldataGasUsed = await estimateCalldataCostForRequest(
    relayRequest,
    config.gsn
  );

  relayRequest.relayData.transactionCalldataGasUsed = parseInt(
    transactionCalldataGasUsed,
    16
  ).toString();

  return relayRequest;
};

const buildRelayHttpRequest = async (
  relayRequest: RelayRequest,
  config: NetworkConfig,
  account: AccountKeypair,
  web3Provider: providers.JsonRpcProvider
) => {
  const signature = await signRequest(
    relayRequest,
    config.gsn.domainSeparatorName,
    config.gsn.chainId,
    account
  );

  const approvalData = '0x';

  const wallet = new ethers.VoidSigner(
    relayRequest.relayData.relayWorker,
    web3Provider
  );
  const relayLastKnownNonce = await wallet.getTransactionCount();
  const relayMaxNonce = relayLastKnownNonce + config.gsn.maxRelayNonceGap;

  const metadata = {
    maxAcceptanceBudget: config.gsn.maxAcceptanceBudget,
    relayHubAddress: config.gsn.relayHubAddress,
    signature,
    approvalData,
    relayMaxNonce,
    relayLastKnownNonce,
    domainSeparatorName: config.gsn.domainSeparatorName,
    relayRequestId: '',
  };
  const httpRequest = {
    relayRequest,
    metadata,
  };

  return httpRequest;
};

export const relayTransaction = async (
  account: AccountKeypair,
  config: NetworkConfig,
  transaction: GsnTransactionDetails
) => {
  const web3Provider = new ethers.providers.JsonRpcProvider(config.gsn.rpcUrl);
  const updatedConfig = await updateConfig(config, transaction);
  const relayRequest = await buildRelayRequest(
    updatedConfig.transaction,
    updatedConfig.config,
    account,
    web3Provider
  );
  const httpRequest = await buildRelayHttpRequest(
    relayRequest,
    updatedConfig.config,
    account,
    web3Provider
  );

  const relayRequestId = getRelayRequestID(
    httpRequest.relayRequest,
    httpRequest.metadata.signature
  );

  //update request metadata with relayrequestid

  httpRequest.metadata.relayRequestId = relayRequestId;

  const res = await axios.post(`${config.gsn.relayUrl}/relay`, httpRequest, {
    headers: authHeader(config),
  });
  return handleGsnResponse(res, web3Provider);
};
```


# Guides

###


# Add ERC2771Recipient

## Overview

Contracts that are [ERC2771](https://eips.ethereum.org/EIPS/eip-2771) compatible are natively supported by the RallyMobile SDK to perform gasless transactions. Contracts that are not ERC2771 compatible, but are whitelisted by us, can also perform gasless transactions

## If you are able to update the contract:

1. [Add `ERC2771Recipient` to your token contract](https://docs.opengsn.org/contracts/#install-opengsn-contracts) to make it compatible with our SDK.&#x20;
2. Set the trusted forwarder:
   1. Amoy: `0x0ae8FC9867CB4a124d7114B8bd15C4c78C4D40E5`  &#x20;
   2. Mainnet: `0xB2b5841DBeF766d4b521221732F9B618fCf34A87`
3. [View code example](https://gist.github.com/DonGambas/230389f11dc5f10137a510527175e01e)

{% hint style="info" %}
For ERC721 contracts, follow [Opensea's guide](https://docs.opensea.io/docs/polygon-basic-integration) to enable meta transactions for your NFTs.
{% endhint %}

## If you are unable to update the contract:

Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and [gasless transaction method](/flutter/gasless-transactions/guides/determining-the-gasless-transaction-method) (`permit` or `executeMetaTransaction`) to have it reviewed and whitelisted by our team. &#x20;


# Determining the gasless transaction method

## **Overview**

Contracts that have the `executeMetaTransaction` or the `Permit` method for gasless transactions are natively supported by our SDK to perform gasless transactions.&#x20;

{% hint style="info" %}
Does your contract use a different gasless transaction method? Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and method to find a solution.
{% endhint %}

## executeMetaTransaction

Here is how to check if your contract supports the executeMetaTransaction method for gasless transactions:&#x20;

### Method 1

Verifying the Contract Name

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Code tab
4. If the Contract Name displays `UChildERC20Proxy`, it supports the `executeMetaTransaction` method.&#x20;

Here is an example of the RLY Token on [Polygonscan](https://polygonscan.com/token/0x76b8d57e5ac6afac5d415a054453d1dd2c3c0094#code)

<div align="left" data-full-width="false"><figure><img src="/files/l9JXLRln3BKTG1Lk7Uas" alt=""><figcaption></figcaption></figure></div>

{% hint style="info" %}
UChildERC20Proxy contracts inherit from the UChildERC20 contract that natively supports the executeMetaTransaction method.&#x20;
{% endhint %}

### Method 2

Verify the implementation contract&#x20;

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Read Contract
4. Navigate to the contract address under the implementation dropdown
5. Click on the Contract tab&#x20;
6. Click on the Write Contract&#x20;
7. Check that the contract has the executeMetaTransaction method

Here is an example of the RLY Token's implementation contract on [Polygonscan](https://polygonscan.com/address/0x93d4a27C64b9Dba29ed328c5801120a68B331C30#writeContract)

<div align="left"><figure><img src="/files/UXUD2yEdD9p1FCqIg1IQ" alt="" width="375"><figcaption></figcaption></figure></div>

## Permit

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Write Contract
4. Check that the contract has the permit method

Here is an example of the TAKI Token's implementation contract on [Polygonscan](https://polygonscan.com/token/0xe78aee6ccb05471a69677fb74da80f5d251c042b#writeContract)

<div align="left"><figure><img src="/files/dNxdXs92iw25wpMaIZpM" alt="" width="375"><figcaption></figcaption></figure></div>


# Overview

## What is RallyProtocol? <a href="#rallyprotocol" id="rallyprotocol"></a>

RallyProtocol is an all-in-one web3 mobile toolkit that enables developers to create end-to-end onchain experiences for native mobile apps. RallyProtocol is comprised of two key components:

## Natively embedded wallets <a href="#natively-embedded-wallets" id="natively-embedded-wallets"></a>

1. Create and store secure, self-custodial wallets natively on-device.
2. Enable cloud backup for easy recovery.
3. Import and export wallets.

## **Gasless transactions** <a href="#gasless-transactions" id="gasless-transactions"></a>

1. Provide end-users with gas-free onchain transactions.
2. Enable end-users to transfer tokens, claim NFTs, and even deploy smart contracts from within mobile apps gaslessly.

## Supported mobile platforms <a href="#supported-mobile-platforms" id="supported-mobile-platforms"></a>

* Android version 23 and above
* iOS all versions

## Goal <a href="#goal" id="goal"></a>

RallyProtocol serves as a bridge between the intricate world of onchain technology and the familiar realm of UX in native mobile apps. It is designed to empower developers to seamlessly blend in-app experiences with onchain features, reducing web3 friction and enabling them to focus on what they do best—creating delightful mobile apps.

### &#x20; <a href="#mobile-sdks" id="mobile-sdks"></a>


# Installation

## Requirements

* Android version 23 and above
* iOS all versions

## Natively embedded wallets&#x20;

### Install the React Native / Expo SDK

{% tabs %}
{% tab title="React Native" %}

```javascript
npm install @rly-network/mobile-sdk

npx pod-install
```

{% endtab %}

{% tab title="Expo" %}

```javascript
npm install @rly-network/mobile-sdk

npm install --save expo-secure-store
npm install --save react-native-get-random-values
```

{% endtab %}
{% endtabs %}

<https://www.npmjs.com/package/@rly-network/mobile-sdk>

## Gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;


# Overview

Wallet management is facilitated by the SDK, serving as the accounts layer for on-device EOA and on-chain Smart Account operations. It consists of the following features:

### EOA wallets

1. Secure EOA wallet generation & on-device storage, cloud backup & recovery, and the ability to import external EOA wallets.&#x20;
2. Developer-friendly APIs for signing both traditional user transactions and [gasless transactions](/react-native-expo/gasless-transactions/use-with-embedded-wallets/overview) on a mobile device.

### Smart Account wallets \[coming soon]

1. Dev friendly API to deploy and manage on-chain Smart Accounts wallets, utilizing the EOA generated by the SDK.
2. Developer friendly API to sign [gasless transactions](/react-native-expo/gasless-transactions/use-with-embedded-wallets/overview) on a mobile device to utilize gasless transactions.


# Getting started

## Requirements

* Android version 23 and above
* iOS all versions

## Natively embedded wallets&#x20;

### Install the React Native / Expo SDK

{% tabs %}
{% tab title="React Native" %}

```javascript
npm install @rly-network/mobile-sdk

npx pod-install
```

{% endtab %}

{% tab title="Expo" %}

```javascript
npm install @rly-network/mobile-sdk

npm install --save expo-secure-store
npm install --save react-native-get-random-values
```

{% endtab %}
{% endtabs %}

<https://www.npmjs.com/package/@rly-network/mobile-sdk>


# Create wallets

## Generate an EOA wallet&#x20;

EOA wallets are generated on-device at device + application level and automatically backed up to the user's cloud account.

```javascript
import { createAccount } from '@rly-network/mobile-sdk';

//create an account
const newAccount = await createAccount();
```

## Get the public address

```javascript
import { getAccount } from '@rly-network/mobile-sdk';

// get current user account address
const account = await getAccount();
```

{% hint style="info" %}
EOA wallets share the same address for both Amoy and Mainnet Polygon.&#x20;
{% endhint %}

## Get the seed phrase

Display the seed phrase to enable users to export their EOA wallet.&#x20;

```javascript
import { getAccountPhrase } from '@rly-network/mobile-sdk';

const mnemonic = await getAccountPhrase();
```


# Manage wallets

## Import an existing wallet

Optionally import a mnemonic string of an existing EOA wallet and store it on the user's device.

```javascript
import { importExistingAccount } from '@rly-network/mobile-sdk';

const existingMnemonic = 'huge remain palm vanish ...';
const walletAddress = await importExistingAccount(existingMnemonic);
```

## Reading balances

View the Gasless Transaction section on [balances](/react-native-expo/gasless-transactions/use-with-embedded-wallets#read-balances).

## Recovery and persistence

By default, EOA wallets are generated on-device and backed up to the user's cloud.  Read about [key persistence](/react-native-expo/embedded-wallets/how-it-works#persistence) here.

## Gasless transactions

View the [Gasless Transaction](/react-native-expo/gasless-transactions/getting-started) section on how to transfer tokens, deploy contracts, and transact with contracts gaslessly.

## Full API Reference&#x20;

View the [full API reference](https://pub.dev/documentation/rly_network_flutter_sdk/latest/)


# How it works

## EOA wallets

EOA wallets, or Externally Owned Accounts, serve as the primary interface for users to interact with web3. The creation of an EOA wallet involves generating a private key, which acts as a unique signature providing access and control to the account. EOA wallets generated by the [RallyMobile SDK](/react-native-expo#rallyprotocol) are user-custodied, meaning that they are created and stored on a user's device.&#x20;

## Generating EOAs

The EOA wallet generation process uses the [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) mnemonic generation to create a hierarchical-deterministic (HD) wallet. This mnemonic is used to extract a private key from the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) path "m/44'/60'/0'/0/0" which is the Ethereum default path.

This generation process uses the same methodologies as [ethers js](https://docs.ethers.org/v5/api/utils/hdnode/), with a couple of key differences:

* The generation of the mnemonic and the extraction of the private key are both done in native code, with C being used on the iOS side and Kotlin on the Android side.
* The private key from our [NativeModule](https://reactnative.dev/docs/native-modules-intro) is passed over to the JavaScript side to instantiate an ethers js wallet using the private key.

The generated ethers js wallet does not have access to the mnemonic, it is not able to create a second wallet with a different path.&#x20;

## Encryption & Storage

Private key storage makes use of hardware encryption and low level OS key storage technology on device.&#x20;

## Persistence&#x20;

By default, createAccount stores the private keys at the device + cloud level. [ ](https://docs.rallyprotocol.com/rally-mobile-sdk/full-api-reference#createaccount)

### **iOS**

* **Device + iCloud storage**
  * Requirements
    * Device must be logged into an Apple ID to back up keys to iCloud.
  * Use cases
    * Logging on to different devices with the same Apple ID and mobile app will restore the original crypto account created by that Apple ID.&#x20;
    * Deleting and reinstalling a mobile application will restore the original crypto account.
* **Device only storage**
  * Use cases
    * Deleting and reinstalling a mobile application will restore the original crypto account.&#x20;

### **Android**

* **Device + Google Cloud storage**
  * Requirements
    * End to End encryption is supported on devices running Android 9 and above.&#x20;
    * Device must have a screen lock set with a PIN, pattern, or password for end to end encryption to be enabled.
    * Device must be logged into a Google account to back up keys to Google Cloud.&#x20;
  * Use cases
    * Logging on to different devices with the same Google account and mobile app will restore the original crypto account created by that Google account.
    * Deleting and reinstalling a mobile application will restore original crypto account.
* **Device only storage**
  * Use cases
    * Deleting and reinstalling a mobile application will generate a new private key due to SharedPreferences being deleted along with the app bundle. **The original crypto account will no longer be recoverable**.&#x20;


# Embedded EOA wallets


# Embedded smart accounts

&#x20;


# Getting Started

Coming soon


# Overview

## Account Abstraction (ERC-4337)

Smart Account wallets, also known as Smart Contract Accounts/Wallets, utilize Account Abstraction (ERC-4337) to create a wallet managed through a smart contract. With the programmability inherent in smart contracts, they provide capabilities that EOA wallets lack, such as enhanced security, customizable limits, flexible recovery, and more.&#x20;

Creating and managing a Smart Account wallet initially requires an EOA wallet. The SDK streamlines this process by utilizing the EOA wallet generated by the SDK.

<br>


# Getting Started

## Requirements

This section focuses on enabling gasless transactions on mobile device using natively embedded wallets generated by the [SDK](/react-native-expo/embedded-wallets/getting-started). If you would like to utilize gasless transactions on a non-native mobile device such as web app or backend, visit [Using without embedded wallets](/react-native-expo/gasless-transactions/using-without-embedded-wallets).&#x20;

## Get an API key for gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;

## Network

This section utilizes Polygon's testnet Amoy network. To switch between Amoy and Mainnet Polygon, update the network config using <https://github.com/rally-dfs/rly-network-mobile-sdk/blob/main/src/network.ts>.

## RLY Token&#x20;

In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) to simplify the developer experience. We maintain a token faucet and manage token contracts to ensure that developers can execute their first gasless transaction without the extra steps.

Once you are ready to use your preferred token on production, refer to the subsequent 'your chosen ERC-20 token' sections.&#x20;


# Read balances

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Check balances with RLY token

{% hint style="success" %}
In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) and our faucet to enable developers to read on-chain balances in just seconds. Use the following code to claim test RLY tokens and check the RLY balance of wallets.
{% endhint %}

```javascript
import { 
    Network,
    RlyAmoyNetwork, 
    getAccount, 
} from '@rly-network/mobile-sdk';

//get Amoy config
const rlyNetwork: Network = RlyAmoyNetwork;

//add your API Key
rlyNetwork.setApiKey(YourAPIKey...)

//claim 10 test RLY tokens gaslessly for testing
await rlyNetwork.claimRly();

//get balance of RLY token on Amoy testnet for display purposes
const balance = await rlyNetwork.getDisplayBalance(); 

//get exact balance of RLY token on Amoy testnet for transfer purposes
const balance = await rlyNetwork.getExactBalance(); 
```

## Check balances with your chosen ERC-20 token

Use the following code to check the chosen token balance of wallets.&#x20;

```javascript
import { 
    Network,
    RlyAmoyNetwork, 
    getAccount, 
} from '@rly-network/mobile-sdk';

//get Amoy config
const rlyNetwork: Network = RlyAmoyNetwork;

//add your API Key
rlyNetwork.setApiKey(YourAPIKey...)

//hex address of token contract
const tokenAddress = "0x...." 

//get balance of RLY token on Amoy testnet for display purposes
const balance = await rlyNetwork.getDisplayBalance(tokenAddress); 

//get exact balance of RLY token on Amoy testnet for transfer purposes
const balance = await rlyNetwork.getExactBalance(tokenAddress); 
```

<br>


# Transfer tokens gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Gasless transactions with RLY token&#x20;

{% hint style="success" %}
In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) and our faucet to enable developers to complete a gasless token transfer in just seconds. Use the following code to claim test RLY tokens and transfer it to a destination wallet of your choosing.&#x20;
{% endhint %}

```javascript
import { 
    RlyAmoyNetwork, 
    getAccount, 
    Network } from '@rly-network/mobile-sdk';

const rlyNetwork: Network = RlyAmoyNetwork;

//add your API Key
rlyNetwork.setApiKey(YourAPIKey...)

//claim 10 test RLY tokens gaslessly for testing
await rlyNetwork.claimRly();

//destination address for testing
const destinationAddress = "0x8D9F6c7Ab1316B613BDdcD1fDbac6025A1323569"

//transfer 5 RLY token expressed in wei using executeMetaTransaction
await rlyNetwork.transferExact(
    destinationAddress, 
    5000000000000000000, 
    MetaTxMethod.ExecuteMetaTransaction
); 
```

## Getting started with your chosen ERC-20 token

### Contract requirements&#x20;

1. **Contract is compatible with the RallyMobile SDK**
   1. Contracts that are [ERC2771 compatible](/react-native-expo/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract) are natively supported by the RallyMobile SDK. Those that are not ERC2771 compatible must be whitelisted, [view the guide here.](/react-native-expo/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract)
2. **Contract natively supports gasless transactions**
   1. Contracts that have the `executeMetaTransaction` or `Permit` method for gasless transactions are natively supported by our SDK. To determine which method is supported by your contract, [view the guide here](/react-native-expo/gasless-transactions/guides/determining-the-gasless-transaction-method).&#x20;

{% hint style="info" %}
View our [supported tokens list](https://docs.rallyprotocol.com/resources/supported-tokens) to see if your chosen token is already supported.
{% endhint %}

## Gasless transactions with your chosen ERC-20 token

Use the following code to transfer your chosen token gaslessly.

```javascript
import { 
    RlyAmoyNetwork, 
    getAccount, 
    Network } from '@rly-network/mobile-sdk';

const rlyNetwork: Network = RlyAmoyNetwork;

//add your API Key
rlyNetwork.setApiKey(YourAPIKey...)

//destination address for testing
const destinationAddress = "0x...."

//hex address of token contract
final tokenAddress = "0x...." 

//transfer 5 ERC20 token expressed in wei using executeMetaTransaction
await rlyNetwork.transferExact(
    destinationAddress, 
    5000000000000000000, 
    erc20TokenAddress,
    MetaTxMethod.ExecuteMetaTransaction
); 

//transfer 5 ERC20 token expressed in wei using permit
await rlyNetwork.transferExact(
    destinationAddress, 
    5000000000000000000, 
    erc20TokenAddress,
    MetaTxMethod.Permit
); 
```


# Deploy a contract gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Deploy contracts with the relay method

To gaslessly deploy a contract, create a [GSN transaction object](https://github.com/rally-dfs/rly-network-mobile-sdk/blob/736e1de0859bec5230abba80e978f76f43fe2123/src/gsnClient/utils.ts#L16)  and use the `relay()` method to send the transaction to our relayer.

```dart
import { RlyAmoyNetwork } from 'rly-network-mobile-sdk';
import ethers from 'ethers';
....

//get web3 provider

const provider = new ethers.providers.JsonRpcProvider(providerUrl);

//get instance of your contract 

const myContract =  new ethers.Contract(contractAddress, contractAbi, signer);

//populate raw transaction object
const tx = await myContract.populateTransaction.myMethod.(
    param1,
    param2
  );

//get gas estimate for transaction
  const gas = await myContract.estimateGas.myMethod.(param1, param2);

//get current network fee data
  const { maxFeePerGas, maxPriorityFeePerGas } = await provider.getFeeData();

//create relay tx object

  const gsnTx = {
    from: account.address,
    data: tx.data,
    to: tx.to,
    gas: gas._hex,
    maxFeePerGas: maxFeePerGas._hex,
    maxPriorityFeePerGas: maxPriorityFeePerGas._hex,
  } as GsnTransactionDetails;

// relay transaction 

await RlyAmoyNetwork.relay(gsnTx);
```


# Transact with a contract gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Contract requirements&#x20;

1. **Contract is compatible with the RallyMobile SDK**
   1. Contracts that are [ERC2771 compatible](/react-native-expo/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract) are natively supported by the RallyMobile SDK. Those that are not ERC2771 compatible must be whitelisted by us, [view the guide here.](/react-native-expo/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract)

## Transact with contracts with the relay method

To gaslessly execute a transaction with a contract, create a [GSN transaction object](https://github.com/rally-dfs/rly-network-mobile-sdk/blob/736e1de0859bec5230abba80e978f76f43fe2123/src/gsnClient/utils.ts#L16)  and use the `relay()` method to send the transaction to our relayer.

```dart
import { RlyAmoyNetwork } from 'rly-network-mobile-sdk';
import ethers from 'ethers';
....

//get web3 provider

const provider = new ethers.providers.JsonRpcProvider(providerUrl);

//get instance of your contract 

const myContract =  new ethers.Contract(contractAddress, contractAbi, signer);

//populate raw transaction object
const tx = await myContract.populateTransaction.myMethod.(
    param1,
    param2
  );

//get gas estimate for transaction
  const gas = await myContract.estimateGas.myMethod.(param1, param2);

//get current network fee data
  const { maxFeePerGas, maxPriorityFeePerGas } = await provider.getFeeData();

//create relay tx object

  const gsnTx = {
    from: account.address,
    data: tx.data,
    to: tx.to,
    gas: gas._hex,
    maxFeePerGas: maxFeePerGas._hex,
    maxPriorityFeePerGas: maxPriorityFeePerGas._hex,
  } as GsnTransactionDetails;

// relay transaction 

await RlyAmoyNetwork.relay(gsnTx);
```


# Use with embedded wallets


# Overview

Gasless transactions are facilitated by the [RallyTransact](/react-native-expo#rallyprotocol), functioning as the fully hosted transactions infrastruture for signing, validating, and relaying transactions to the blockchain.&#x20;

RallyTransact removes the requirement for developers to manage nodes, bundlers, and deploy contracts. Additionally, RallyTransact takes care of funding balances for gasless transactions, monitors transactions, and provides enhanced secops.&#x20;

Although RallyMobile provides dev friendly APIs to interact with the transaction infrastructure, RallyTransact can also be used [outside of the SDK](/react-native-expo/gasless-transactions/using-without-embedded-wallets). Devs can send transactions from their backend, web dapp, desktop, etc and get all the benefits listed above.

###


# Using without embedded wallets


# Getting started

## Overview

Gasless transactions can be utilized outside of the mobile device such as from a backend, web dapp, desktop, etc. Submitting transactions is a 2 step process:&#x20;

1. Sign a transaction on the client (web, backend, etc)
2. Create a HTTP request containing a JSON payload with details about the transaction you'd like to relay.&#x20;

## Get an API key for gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;

## Using an existing Gas Station Network Client Library.

Our hosted transaction infrastructure honors the [Gas Station Network](https://opengsn.org/) interface with the addition of an authentication HTTP header containing your API key. If you're already using a GSN client the following steps should be all you need to change:&#x20;

1. Set `https://api.rallyprotocol.com` as your relay URL.&#x20;
2. Add the following header to the HTTP request `Authorization: Bearer <YOUR_API_TOKEN>`&#x20;

## Submitting Requests Without a GSN Library

*Examples coming soon. Reach out to us on* [*Discord*](https://discord.gg/rlynetwork) *and we'll happily walk you through it*


# Typescript Example

## Required payload:

For definitions, refer to: <https://docs.opengsn.org/jsdoc/jsdoc-client.html>

```typescript
// This typescript type represents the full JSON payload required
// by our relay API endpoint. 
type RlyTransactionPayload = {
  relayRequest: {
    request: {
      from: string;
      to: string;
      value: string;
      gas: string;
      nonce: string;
      data: string;
      validUntilTime: string;
    };
    relayData: {
      maxFeePerGas: string;
      maxPriorityFeePerGas: string;
      transactionCalldataGasUsed: string;
      relayWorker: string;
      paymaster: string;
      forwarder: string;
      paymasterData: string;
      clientId: string;
    };
  };
  metadata: {
    maxAcceptanceBudget: string;
    relayHubAddress: string;
    signature: string;
    approvalData: string;
    relayMaxNonce: number;
    relayLastKnownNonce: number;
    domainSeparatorName: string;
    relayRequestId: string;
  };
};

// Usage example:
const examplePayload: RlyTransactionPayload = {
  relayRequest: {
    request: {
      from: '0x1e7D51f413Dd60508352846fCa063f05cB423F8C',
      to: '0x1C7312Cb60b40cF586e796FEdD60Cf243286c9E9',
      value: '0',
      gas: '73380',
      nonce: '1',
      data: '0x0c53c51c0000000000000000000000001e7d51f413dd60508352846fca063f05cb423f8c00000000000000000000000000000000000000000000000000000000000000a017fa37ac5967c642ba02b250f188da83d5613d0e5c0286241ff9aece0503662c2a6146626ce6d182f8b277723d3d638ec12c1fed1f15954ab344a5ecdfba5fd2000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000e75625f0c8659f18caf845eddae30f5c2a49cb000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000',
      validUntilTime: '1691256746',
    },
    relayData: {
      maxFeePerGas: '1650000000',
      maxPriorityFeePerGas: '1650000000',
      transactionCalldataGasUsed: '19296',
      relayWorker: '0xb9950b71ec94cbb274aeb1be98e697678077a17f',
      paymaster: '0x8b3a505413Ca3B0A17F077e507aF8E3b3ad4Ce4d',
      forwarder: '0xB2b5841DBeF766d4b521221732F9B618fCf34A87',
      paymasterData: '0x',
      clientId: '1',
    },
  },
  metadata: {
    maxAcceptanceBudget: '285252',
    relayHubAddress: '0xe213A20A9E6CBAfd8456f9669D8a0b9e41Cb2751',
    signature:
      '0xd3f7b35fc4985b2a52e4c1b47091b1f5ba947a2159d2da07c7c33b3e54fd5d5e3166847e5a6460fb9e69a90e6531399c5c258e332b6fe1c8157c8e06d527452e1b',
    approvalData: '0x',
    relayMaxNonce: 1424,
    relayLastKnownNonce: 1421,
    domainSeparatorName: 'GSN Relayed Transaction',
    relayRequestId:
      '0x000000001204aa7c5e04714775ca00cad9d6f298f8d43caa291b8f8d1b237e39',
  },
};
const authHeader = `Bearer: ${yourApiToken}`;
const response = axios.post(
  'https://api.rallyprotocol.com/relay',
  examplePayload,
  { headers: { Authorization: authHeader } }
);
```

## Example from the SDK client:&#x20;

<https://github.com/rally-dfs/rly-network-mobile-sdk/blob/main/src/gsnClient/gsnClient.ts>

```javascript
import type { GsnTransactionDetails, AccountKeypair } from './utils';

import type { RelayRequest } from './EIP712/RelayRequest';
import { handleGsnResponse } from './gsnTxHelpers';

import axios from 'axios';

import type { NetworkConfig } from 'src/network_config/network_config';

import {
  estimateGasWithoutCallData,
  estimateCalldataCostForRequest,
  getSenderNonce,
  signRequest,
  getRelayRequestID,
  //getClientId,
} from './gsnTxHelpers';

import { ethers, providers } from 'ethers';

interface GsnServerConfigPayload {
  relayWorkerAddress: string;
  relayManagerAddress: string;
  relayHubAddress: string;
  ownerAddress: string;
  minMaxPriorityFeePerGas: string;
  maxMaxFeePerGas: string;
  minMaxFeePerGas: string;
  maxAcceptanceBudget: string;
  chainId: string;
  networkId: string;
  ready: boolean;
  version: string;
}

const authHeader = (config: NetworkConfig) => {
  return {
    Authorization: `Bearer ${config.relayerApiKey || ''}`,
  };
};

const updateConfig = async (
  config: NetworkConfig,
  transaction: GsnTransactionDetails
) => {
  const response = await axios.get(`${config.gsn.relayUrl}/getaddr`, {
    headers: authHeader(config),
  });
  const serverConfigUpdate = response.data as GsnServerConfigPayload;

  config.gsn.relayWorkerAddress = serverConfigUpdate.relayWorkerAddress;

  setGasFeesForTransaction(transaction, serverConfigUpdate);

  return { config, transaction };
};

const setGasFeesForTransaction = (
  transaction: GsnTransactionDetails,
  serverConfigUpdate: GsnServerConfigPayload
) => {
  const serverSuggestedMinPriorityFeePerGas = parseInt(
    serverConfigUpdate.minMaxPriorityFeePerGas,
    10
  );

  const paddedMaxPriority = Math.round(
    serverSuggestedMinPriorityFeePerGas * 1.4
  );
  transaction.maxPriorityFeePerGas = paddedMaxPriority.toString();

  //Special handling for Amoy because of quirk with gas estimate returned by GSN for Amoy
  if (serverConfigUpdate.chainId === '80001') {
    transaction.maxFeePerGas = paddedMaxPriority.toString();
  } else {
    transaction.maxFeePerGas = serverConfigUpdate.maxMaxFeePerGas;
  }
};

const buildRelayRequest = async (
  transaction: GsnTransactionDetails,
  config: NetworkConfig,
  account: AccountKeypair,
  web3Provider: providers.JsonRpcProvider
) => {
  //remove call data cost from gas estimate as tx will be called from contract
  transaction.gas = estimateGasWithoutCallData(
    transaction,
    config.gsn.gtxDataNonZero,
    config.gsn.gtxDataZero
  );

  const secondsNow = Math.round(Date.now() / 1000);
  const validUntilTime = (
    secondsNow + config.gsn.requestValidSeconds
  ).toString();

  const senderNonce = await getSenderNonce(
    account.address,
    config.gsn.forwarderAddress,
    web3Provider
  );

  const relayRequest: RelayRequest = {
    request: {
      from: transaction.from,
      to: transaction.to,
      value: transaction.value || '0',
      gas: parseInt(transaction.gas, 16).toString(),
      nonce: senderNonce,
      data: transaction.data,
      validUntilTime,
    },
    relayData: {
      maxFeePerGas: transaction.maxFeePerGas,
      maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
      transactionCalldataGasUsed: '',
      relayWorker: config.gsn.relayWorkerAddress,
      paymaster: config.gsn.paymasterAddress,
      forwarder: config.gsn.forwarderAddress,
      paymasterData: transaction.paymasterData?.toString() || '0x',
      clientId: '1',
    },
  };

  const transactionCalldataGasUsed = await estimateCalldataCostForRequest(
    relayRequest,
    config.gsn
  );

  relayRequest.relayData.transactionCalldataGasUsed = parseInt(
    transactionCalldataGasUsed,
    16
  ).toString();

  return relayRequest;
};

const buildRelayHttpRequest = async (
  relayRequest: RelayRequest,
  config: NetworkConfig,
  account: AccountKeypair,
  web3Provider: providers.JsonRpcProvider
) => {
  const signature = await signRequest(
    relayRequest,
    config.gsn.domainSeparatorName,
    config.gsn.chainId,
    account
  );

  const approvalData = '0x';

  const wallet = new ethers.VoidSigner(
    relayRequest.relayData.relayWorker,
    web3Provider
  );
  const relayLastKnownNonce = await wallet.getTransactionCount();
  const relayMaxNonce = relayLastKnownNonce + config.gsn.maxRelayNonceGap;

  const metadata = {
    maxAcceptanceBudget: config.gsn.maxAcceptanceBudget,
    relayHubAddress: config.gsn.relayHubAddress,
    signature,
    approvalData,
    relayMaxNonce,
    relayLastKnownNonce,
    domainSeparatorName: config.gsn.domainSeparatorName,
    relayRequestId: '',
  };
  const httpRequest = {
    relayRequest,
    metadata,
  };

  return httpRequest;
};

export const relayTransaction = async (
  account: AccountKeypair,
  config: NetworkConfig,
  transaction: GsnTransactionDetails
) => {
  const web3Provider = new ethers.providers.JsonRpcProvider(config.gsn.rpcUrl);
  const updatedConfig = await updateConfig(config, transaction);
  const relayRequest = await buildRelayRequest(
    updatedConfig.transaction,
    updatedConfig.config,
    account,
    web3Provider
  );
  const httpRequest = await buildRelayHttpRequest(
    relayRequest,
    updatedConfig.config,
    account,
    web3Provider
  );

  const relayRequestId = getRelayRequestID(
    httpRequest.relayRequest,
    httpRequest.metadata.signature
  );

  //update request metadata with relayrequestid

  httpRequest.metadata.relayRequestId = relayRequestId;

  const res = await axios.post(`${config.gsn.relayUrl}/relay`, httpRequest, {
    headers: authHeader(config),
  });
  return handleGsnResponse(res, web3Provider);
};
```


# Guides

###


# Add ERC2771Recipient

## Overview

Contracts that are [ERC2771](https://eips.ethereum.org/EIPS/eip-2771) compatible are natively supported by the RallyMobile SDK to perform gasless transactions. Contracts that are not ERC2771 compatible, but are whitelisted by us, can also perform gasless transactions

## If you are able to update the contract:

1. [Add `ERC2771Recipient` to your token contract](https://docs.opengsn.org/contracts/#install-opengsn-contracts) to make it compatible with our SDK.&#x20;
2. Set the trusted forwarder:
   1. Amoy: `0x0ae8FC9867CB4a124d7114B8bd15C4c78C4D40E5`  &#x20;
   2. Mainnet: `0xB2b5841DBeF766d4b521221732F9B618fCf34A87`
3. [View code example](https://gist.github.com/DonGambas/230389f11dc5f10137a510527175e01e)

{% hint style="info" %}
For ERC721 contracts, follow [Opensea's guide](https://docs.opensea.io/docs/polygon-basic-integration) to enable meta transactions for your NFTs.
{% endhint %}

## If you are unable to update the contract:

Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and [gasless transaction method](/react-native-expo/gasless-transactions/guides/determining-the-gasless-transaction-method) (`permit` or `executeMetaTransaction`) to have it reviewed and whitelisted by our team. &#x20;


# Determining the gasless transaction method

## **Overview**

Contracts that have the `executeMetaTransaction` or the `Permit` method for gasless transactions are natively supported by our SDK to perform gasless transactions.&#x20;

{% hint style="info" %}
Does your contract use a different gasless transaction method? Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and method to find a solution.
{% endhint %}

## executeMetaTransaction

Here is how to check if your contract supports the executeMetaTransaction method for gasless transactions:&#x20;

### Method 1

Verifying the Contract Name

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Code tab
4. If the Contract Name displays `UChildERC20Proxy`, it supports the `executeMetaTransaction` method.&#x20;

Here is an example of the RLY Token on [Polygonscan](https://polygonscan.com/token/0x76b8d57e5ac6afac5d415a054453d1dd2c3c0094#code)

<div align="left" data-full-width="false"><figure><img src="/files/l9JXLRln3BKTG1Lk7Uas" alt=""><figcaption></figcaption></figure></div>

{% hint style="info" %}
UChildERC20Proxy contracts inherit from the UChildERC20 contract that natively supports the executeMetaTransaction method.&#x20;
{% endhint %}

### Method 2

Verify the implementation contract&#x20;

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Read Contract
4. Navigate to the contract address under the implementation dropdown
5. Click on the Contract tab&#x20;
6. Click on the Write Contract&#x20;
7. Check that the contract has the executeMetaTransaction method

Here is an example of the RLY Token's implementation contract on [Polygonscan](https://polygonscan.com/address/0x93d4a27C64b9Dba29ed328c5801120a68B331C30#writeContract)

<div align="left"><figure><img src="/files/UXUD2yEdD9p1FCqIg1IQ" alt="" width="375"><figcaption></figcaption></figure></div>

## Permit

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Write Contract
4. Check that the contract has the permit method

Here is an example of the TAKI Token's implementation contract on [Polygonscan](https://polygonscan.com/token/0xe78aee6ccb05471a69677fb74da80f5d251c042b#writeContract)

<div align="left"><figure><img src="/files/dNxdXs92iw25wpMaIZpM" alt="" width="375"><figcaption></figcaption></figure></div>


# Overview

## What is RallyProtocol? <a href="#rallyprotocol" id="rallyprotocol"></a>

RallyProtocol is an all-in-one web3 mobile toolkit that enables developers to create end-to-end onchain experiences for native mobile apps. RallyProtocol is comprised of two key components:

## Natively embedded wallets <a href="#natively-embedded-wallets" id="natively-embedded-wallets"></a>

1. Create and store secure, self-custodial wallets natively on-device.
2. Enable cloud backup for easy recovery.
3. Import and export wallets.

## **Gasless transactions** <a href="#gasless-transactions" id="gasless-transactions"></a>

1. Provide end-users with gas-free onchain transactions.
2. Enable end-users to transfer tokens, claim NFTs, and even deploy smart contracts from within mobile apps gaslessly.

## Supported mobile platforms <a href="#supported-mobile-platforms" id="supported-mobile-platforms"></a>

* Android version 23 and above
* iOS all versions

## Goal <a href="#goal" id="goal"></a>

RallyProtocol serves as a bridge between the intricate world of onchain technology and the familiar realm of UX in native mobile apps. It is designed to empower developers to seamlessly blend in-app experiences with onchain features, reducing web3 friction and enabling them to focus on what they do best—creating delightful mobile apps.

### &#x20; <a href="#mobile-sdks" id="mobile-sdks"></a>


# Installation

## Requirements

* Unity 2022 and above
* Android version 23 and above
* iOS all versions

## Natively embedded wallets

### Install the Unity SDK

Download and install the latest package from <https://github.com/rally-dfs/rly-network-unity-sdk/releases>&#x20;

## Get an API key for gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;


# Overview

Wallet management is facilitated by the [RallyMobile SDK](/unity#rallyprotocol), serving as the accounts layer for on-device EOA and on-chain Smart Account operations. It consists of the following features:

### EOA wallets

1. Secure EOA wallet generation & on-device storage, cloud backup & recovery, and the ability to import external EOA wallets.&#x20;
2. Developer-friendly APIs for signing both traditional user transactions and [gasless transactions](/unity/gasless-transactions/overview) on a mobile device, for use with [RallyTransact](/unity/gasless-transactions/overview)

### Smart Account wallets \[coming soon]

1. Dev friendly API to deploy and manage on-chain Smart Accounts wallets, utilizing the EOA generated by the RallyMobile SDK.
2. Developer friendly API to sign [gasless transactions](/unity/gasless-transactions/overview) on a mobile device to utilize gasless transactions powered by [RallyTransact](/unity#rallyprotocol).&#x20;


# Getting started

## Requirements

* Unity 2022 and above
* Android version 23 and above
* iOS all versions

## Install RallyUnity SDK&#x20;

### Unity

Download and install the latest package from <https://github.com/rally-dfs/rly-network-unity-sdk/releases>.&#x20;


# Create wallets

## Generate an EOA wallet&#x20;

EOA wallets are generated on-device at device + application level and automatically backed up to the user's cloud account.

```csharp
//create an account
var account = await AccountManager.CreateAccount();
```

## Get the public address

```csharp
//get current user account address
var address = await AccountManager.GetPublicAddress();
```

{% hint style="info" %}
EOA wallets share the same address for both Amoy and Mainnet Polygon.&#x20;
{% endhint %}

## Get the seed phrase

Display the seed phrase to enable users to export their EOA wallet.&#x20;

```csharp
var mnemonic = await AccountManager.GetAccountPhrase();
```


# Manage wallets

## Import an existing wallet

Optionally import a mnemonic string of an existing EOA wallet and store it on the user's device.

```csharp
final someExistingMnemonic = "some value from other product or IO...";
final wallet = await AccountManager.ImportExistingAccount(someExistingMnemonic);
```

## Reading balances

View the Gasless Transaction section on [balances](/unity/gasless-transactions/using-with-embedded-wallets#read-balances).

## Recovery and persistence

By default, EOA wallets are generated on-device and backed up to the user's cloud.  Read about [key persistence](/unity/embedded-wallets/how-it-works#persistence) here.

## Transfer tokens gaslessly

View the [Gasless Transaction](/unity/gasless-transactions/overview) section on how to transfer tokens, deploy contracts, and transact with contracts gaslessly.

## Full API Reference&#x20;

Coming soon


# How it works

## EOA wallets

EOA wallets, or Externally Owned Accounts, serve as the primary interface for users to interact with web3. The creation of an EOA wallet involves generating a private key, which acts as a unique signature providing access and control to the account. EOA wallets generated by the SDK are user-custodied, meaning that they are created and stored on a user's device.&#x20;

## Generating EOAs

The EOA wallet generation process uses the [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) mnemonic generation to create a hierarchical-deterministic (HD) wallet. This mnemonic is used to extract a private key from the [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) path "m/44'/60'/0'/0/0" which is the Ethereum default path.

This generation process uses the same methodologies as [ethers js](https://docs.ethers.org/v5/api/utils/hdnode/), with a couple of key differences:

* The generation of the mnemonic and the extraction of the private key are both done in native code, with C being used on the iOS side and Kotlin on the Android side.
* The private key from our [NativeModule](https://reactnative.dev/docs/native-modules-intro) is passed over to the JavaScript side to instantiate an ethers js wallet using the private key.

The generated ethers js wallet does not have access to the mnemonic, it is not able to create a second wallet with a different path.&#x20;

## Encryption & Storage

Private key storage makes use of hardware encryption and low level OS key storage technology on device.&#x20;

## Persistence&#x20;

By default, createAccount stores the private keys at the device + cloud level. [ ](https://docs.rallyprotocol.com/rally-mobile-sdk/full-api-reference#createaccount)

### **iOS**

* **Device + iCloud storage**
  * Requirements
    * Device must be logged into an Apple ID to back up keys to iCloud.
  * Use cases
    * Logging on to different devices with the same Apple ID and mobile app will restore the original crypto account created by that Apple ID.&#x20;
    * Deleting and reinstalling a mobile application will restore the original crypto account.
* **Device only storage**
  * Use cases
    * Deleting and reinstalling a mobile application will restore the original crypto account.&#x20;

### **Android**

* **Device + Google Cloud storage**
  * Requirements
    * End to End encryption is supported on devices running Android 9 and above.&#x20;
    * Device must have a screen lock set with a PIN, pattern, or password for end to end encryption to be enabled.
    * Device must be logged into a Google account to back up keys to Google Cloud.&#x20;
  * Use cases
    * Logging on to different devices with the same Google account and mobile app will restore the original crypto account created by that Google account.
    * Deleting and reinstalling a mobile application will restore original crypto account.
* **Device only storage**
  * Use cases
    * Deleting and reinstalling a mobile application will generate a new private key due to SharedPreferences being deleted along with the app bundle. **The original crypto account will no longer be recoverable**.&#x20;


# Natively embedded wallets


# Embedded smart accounts

&#x20;


# Getting Started

Coming soon


# Overview

## Account Abstraction (ERC-4337)

Smart Account wallets, also known as Smart Contract Accounts/Wallets, utilize Account Abstraction (ERC-4337) to create a wallet managed through a smart contract. With the programmability inherent in smart contracts, they provide capabilities that EOA wallets lack, such as enhanced security, customizable limits, flexible recovery, and more.&#x20;

Creating and managing a Smart Account wallet initially requires an EOA wallet. RallyProtocol streamlines this process by utilizing the EOA wallet generated by the SDK.

<br>


# Overview

Gasless transactions are facilitated by the [RallyTransact](/unity#rallyprotocol), functioning as the fully hosted transactions infrastruture for signing, validating, and relaying transactions to the blockchain.&#x20;

RallyTransact removes the requirement for developers to manage nodes, bundlers, and deploy contracts. Additionally, RallyTransact takes care of funding balances for gasless transactions, monitors transactions, and provides enhanced secops.&#x20;

Although RallyMobile provides dev friendly APIs to interact with the transaction infrastructure, RallyTransact can also be used [outside of the SDK](/unity/gasless-transactions/using-without-embedded-wallets). Devs can send transactions from their backend, web dapp, desktop, etc and get all the benefits listed above.

###


# Getting started

## Requirements

This section focuses on enabling gasless transactions on mobile device using natively embedded wallets generated by the [SDK](/unity/embedded-wallets/getting-started). If you would like to utilize gasless transactions on a non-native mobile device such as web app or backend, visit [Using without embedded wallets](/unity/gasless-transactions/using-without-embedded-wallets).&#x20;

## Get an API key for gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;

## Network

This section utilizes Polygon's testnet Amoy network. To switch between Amoy and Mainnet Polygon, simply use a different Network via the NetworkProvider<br>

```csharp
var polygon = NetworkProvider.RlyPolygon;
var amoy = NetworkProvider.RlyAmoy;
```

## RLY Token&#x20;

In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) to simplify the developer experience. We maintain a token faucet and manage token contracts to ensure that developers can execute their first gasless transaction without the extra steps.

Once you are ready to use your preferred token on production, refer to the subsequent 'your chosen ERC-20 token' sections.&#x20;


# Read balances

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Check balances with RLY token

{% hint style="success" %}
In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) and our faucet to enable developers to read on-chain balances in just seconds. Use the following code to claim test RLY tokens and check the RLY balance of wallets.
{% endhint %}

```csharp
//get amoy config
var amoy = NetworkProvider.RlyAmoy;

//add your API Key (can also be chained)
amoy.WithApiKey(env.API_KEY);

//claim 10 test RLY tokens gaslessly for testing
await amoy.ClaimRly();

// get balance of RLY token
await amoy.GetBalance()

//get exact balance of RLY token on Amoy testnet for transfer purposes
await amoy.GetExactBalance();
```

## Check balances with your chosen ERC-20 token

Use the following code to check the chosen token balance of wallets.&#x20;

```csharp
//get amoy config
var amoy = NetworkProvider.RlyAmoy;

//add your API Key (can also be chained)
amoy.WithApiKey(env.API_KEY);

//hex address of token contract.
var tokenAddress = "0x...." 

// get balance of specified token
await amoy.GetBalance(tokenAddress)

//get exact balance of specified token on Amoy testnet for transfer purposes
await amoy.GetExactBalance(tokenAddress);
```

<br>


# Transfer tokens gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Gasless transactions with RLY token&#x20;

{% hint style="success" %}
In this section, we leverage the RLY token ([polygonscan](https://polygonscan.com/token/0x76b8D57e5ac6afAc5D415a054453d1DD2c3C0094)) and our faucet to enable developers to complete a gasless token transfer in just seconds. Use the following code to claim test RLY tokens and transfer it to a destination wallet of your choosing.&#x20;
{% endhint %}

```csharp
//get amoy config
var amoy = NetworkProvider.RlyAmoy;

//add your API Key (can also be chained)
amoy.WithApiKey(env.API_KEY);

//claim 10 test RLY tokens gaslessly for testing
await amoy.ClaimRly();

//destination address for testing
var destinationAddress = "0x8D9F6c7Ab1316B613BDdcD1fDbac6025A1323569" 

//transfer RLY token
await amoy.TransferExact(
  destinationAddress,
  new BigInteger(1),
  MetaTxMethod.ExecuteMetaTransaction
);
```

## Getting started with your chosen ERC-20 token

### Contract requirements&#x20;

1. **Contract is compatible with the RallyMobile SDK**
   1. Contracts that are [ERC2771 compatible](/unity/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract) are natively supported by the RallyMobile SDK. Those that are not ERC2771 compatible must be whitelisted, [view the guide here.](/unity/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract)
2. **Contract natively supports gasless transactions**
   1. Contracts that have the `executeMetaTransaction` or `Permit` method for gasless transactions are natively supported by our SDK. To determine which method is supported by your contract, [view the guide here](/unity/gasless-transactions/guides/determining-the-gasless-transaction-method).&#x20;

{% hint style="info" %}
View our [supported tokens list](https://docs.rallyprotocol.com/resources/supported-tokens) to see if your token is already supported.
{% endhint %}

## Gasless transactions with your chosen ERC-20 token

Use the following code to transfer your chosen token gaslessly.

```csharp
//get amoy config
var amoy = NetworkProvider.RlyAmoy;

//add your API Key (can also be chained)
amoy.WithApiKey(env.API_KEY);

//destination address for testing
var destinationAddress = "0x...." 

//hex address of token contract
var tokenAddress = "0x...." 

//transfer tokens if the token contract supports executeMetaTransaction 
await amoy.TransferExact(
  destinationAddress,
  new BigInteger(1),
  MetaTxMethod.ExecuteMetaTransaction,
  {tokenAddress}
);

//transfer tokens if the token contract supports Permit 
await amoy.TransferExact(
  destinationAddress,
  new BigInteger(1),
  MetaTxMethod.Permit,
  {tokenAddress}
);
```


# Deploy a contract gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Deploy contracts with the relay method

To gaslessly deploy a contract, create a [GSN transaction object](https://github.com/rally-dfs/rly-network-mobile-sdk/blob/736e1de0859bec5230abba80e978f76f43fe2123/src/gsnClient/utils.ts#L16)  and use the `relay()` method to send the transaction to our relayer.

```csharp
var gsnTx = new GsnTransactionDetails(
  from: accountAddress,
  data: tx.data,
  value: "0",
  to: contractAddress,
  gas: gas.ToString(),
  maxFeePerGas: maxFeePerGas.ToString(),
  maxPriorityFeePerGas: maxPriorityFeePerGas.ToString(),
);

await amoy.Relay(gsnTx)


// relay arbitrary tx through our gasless relayer. see a complete Flutter example at
// https://github.com/rally-dfs/flutter-example-app/tree/main/app/lib/services/nft.dart
```


# Transact with a contract gaslessly

{% hint style="info" %}
An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;
{% endhint %}

## Contract requirements&#x20;

1. **Contract is compatible with the RallyMobile SDK**
   1. Contracts that are [ERC2771 compatible](/unity/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract) are natively supported by the RallyMobile SDK. Those that are not ERC2771 compatible must be whitelisted by us, [view the guide here.](/unity/gasless-transactions/guides/add-erc2771recipient#if-you-are-able-to-update-the-contract)

## Transact with contracts with the relay method

To gaslessly execute a transaction with a contract, create a [GSN transaction object](https://github.com/rally-dfs/rly-network-mobile-sdk/blob/736e1de0859bec5230abba80e978f76f43fe2123/src/gsnClient/utils.ts#L16)  and use the `relay()` method to send the transaction to our relayer.

```csharp
var gsnTx = new GsnTransactionDetails(
  from: accountAddress,
  data: tx.data,
  value: "0",
  to: contractAddress,
  gas: gas.ToString(),
  maxFeePerGas: maxFeePerGas.ToString(),
  maxPriorityFeePerGas: maxPriorityFeePerGas.ToString(),
);

await amoy.Relay(gsnTx)


// relay arbitrary tx through our gasless relayer. see a complete Flutter example at
// https://github.com/rally-dfs/flutter-example-app/tree/main/app/lib/services/nft.dart
```


# Using with embedded wallets


# Using without embedded wallets


# Getting started

## Overview

RallyTransact's gasless transactions can be utilized outside of the RallyMobile SDK such as from a backend, web dapp, desktop, etc. Submitting transactions to RallyTransact is a 2 step process:&#x20;

1. Sign a transaction on the client (web, backend, etc)
2. Create a HTTP request containing a JSON payload with details about the transaction you'd like to relay.&#x20;

## Get an API key for gasless transactions

An API key is required to use the infrastructure to power gasless transactions. Visit  <https://app.rallyprotocol.com/> to generate both Amoy and Mainnet Polygon API keys.&#x20;

## Using an existing Gas Station Network Client Library.

Our hosted transaction infrastructure honors the [Gas Station Network](https://opengsn.org/) interface with the addition of an authentication HTTP header containing your API key. If you're already using a GSN client the following steps should be all you need to change:&#x20;

1. Set `https://api.rallyprotocol.com` as your relay URL.&#x20;
2. Add the following header to the HTTP request `Authorization: Bearer <YOUR_API_TOKEN>`&#x20;

## Submitting Requests Without a GSN Library

*Examples coming soon. Reach out to us on* [*Discord*](https://discord.gg/rlynetwork) *and we'll happily walk you through it*


# Typescript Example

## Required payload:

For definitions, refer to: <https://docs.opengsn.org/jsdoc/jsdoc-client.html>

```typescript
// This typescript type represents the full JSON payload required
// by our relay API endpoint. 
type RlyTransactionPayload = {
  relayRequest: {
    request: {
      from: string;
      to: string;
      value: string;
      gas: string;
      nonce: string;
      data: string;
      validUntilTime: string;
    };
    relayData: {
      maxFeePerGas: string;
      maxPriorityFeePerGas: string;
      transactionCalldataGasUsed: string;
      relayWorker: string;
      paymaster: string;
      forwarder: string;
      paymasterData: string;
      clientId: string;
    };
  };
  metadata: {
    maxAcceptanceBudget: string;
    relayHubAddress: string;
    signature: string;
    approvalData: string;
    relayMaxNonce: number;
    relayLastKnownNonce: number;
    domainSeparatorName: string;
    relayRequestId: string;
  };
};

// Usage example:
const examplePayload: RlyTransactionPayload = {
  relayRequest: {
    request: {
      from: '0x1e7D51f413Dd60508352846fCa063f05cB423F8C',
      to: '0x1C7312Cb60b40cF586e796FEdD60Cf243286c9E9',
      value: '0',
      gas: '73380',
      nonce: '1',
      data: '0x0c53c51c0000000000000000000000001e7d51f413dd60508352846fca063f05cb423f8c00000000000000000000000000000000000000000000000000000000000000a017fa37ac5967c642ba02b250f188da83d5613d0e5c0286241ff9aece0503662c2a6146626ce6d182f8b277723d3d638ec12c1fed1f15954ab344a5ecdfba5fd2000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000e75625f0c8659f18caf845eddae30f5c2a49cb000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000',
      validUntilTime: '1691256746',
    },
    relayData: {
      maxFeePerGas: '1650000000',
      maxPriorityFeePerGas: '1650000000',
      transactionCalldataGasUsed: '19296',
      relayWorker: '0xb9950b71ec94cbb274aeb1be98e697678077a17f',
      paymaster: '0x8b3a505413Ca3B0A17F077e507aF8E3b3ad4Ce4d',
      forwarder: '0xB2b5841DBeF766d4b521221732F9B618fCf34A87',
      paymasterData: '0x',
      clientId: '1',
    },
  },
  metadata: {
    maxAcceptanceBudget: '285252',
    relayHubAddress: '0xe213A20A9E6CBAfd8456f9669D8a0b9e41Cb2751',
    signature:
      '0xd3f7b35fc4985b2a52e4c1b47091b1f5ba947a2159d2da07c7c33b3e54fd5d5e3166847e5a6460fb9e69a90e6531399c5c258e332b6fe1c8157c8e06d527452e1b',
    approvalData: '0x',
    relayMaxNonce: 1424,
    relayLastKnownNonce: 1421,
    domainSeparatorName: 'GSN Relayed Transaction',
    relayRequestId:
      '0x000000001204aa7c5e04714775ca00cad9d6f298f8d43caa291b8f8d1b237e39',
  },
};
const authHeader = `Bearer: ${yourApiToken}`;
const response = axios.post(
  'https://api.rallyprotocol.com/relay',
  examplePayload,
  { headers: { Authorization: authHeader } }
);
```

## Example from the SDK client:&#x20;

<https://github.com/rally-dfs/rly-network-mobile-sdk/blob/main/src/gsnClient/gsnClient.ts>

```javascript
import type { GsnTransactionDetails, AccountKeypair } from './utils';

import type { RelayRequest } from './EIP712/RelayRequest';
import { handleGsnResponse } from './gsnTxHelpers';

import axios from 'axios';

import type { NetworkConfig } from 'src/network_config/network_config';

import {
  estimateGasWithoutCallData,
  estimateCalldataCostForRequest,
  getSenderNonce,
  signRequest,
  getRelayRequestID,
  //getClientId,
} from './gsnTxHelpers';

import { ethers, providers } from 'ethers';

interface GsnServerConfigPayload {
  relayWorkerAddress: string;
  relayManagerAddress: string;
  relayHubAddress: string;
  ownerAddress: string;
  minMaxPriorityFeePerGas: string;
  maxMaxFeePerGas: string;
  minMaxFeePerGas: string;
  maxAcceptanceBudget: string;
  chainId: string;
  networkId: string;
  ready: boolean;
  version: string;
}

const authHeader = (config: NetworkConfig) => {
  return {
    Authorization: `Bearer ${config.relayerApiKey || ''}`,
  };
};

const updateConfig = async (
  config: NetworkConfig,
  transaction: GsnTransactionDetails
) => {
  const response = await axios.get(`${config.gsn.relayUrl}/getaddr`, {
    headers: authHeader(config),
  });
  const serverConfigUpdate = response.data as GsnServerConfigPayload;

  config.gsn.relayWorkerAddress = serverConfigUpdate.relayWorkerAddress;

  setGasFeesForTransaction(transaction, serverConfigUpdate);

  return { config, transaction };
};

const setGasFeesForTransaction = (
  transaction: GsnTransactionDetails,
  serverConfigUpdate: GsnServerConfigPayload
) => {
  const serverSuggestedMinPriorityFeePerGas = parseInt(
    serverConfigUpdate.minMaxPriorityFeePerGas,
    10
  );

  const paddedMaxPriority = Math.round(
    serverSuggestedMinPriorityFeePerGas * 1.4
  );
  transaction.maxPriorityFeePerGas = paddedMaxPriority.toString();

  //Special handling for Amoy because of quirk with gas estimate returned by GSN for Amoy
  if (serverConfigUpdate.chainId === '80001') {
    transaction.maxFeePerGas = paddedMaxPriority.toString();
  } else {
    transaction.maxFeePerGas = serverConfigUpdate.maxMaxFeePerGas;
  }
};

const buildRelayRequest = async (
  transaction: GsnTransactionDetails,
  config: NetworkConfig,
  account: AccountKeypair,
  web3Provider: providers.JsonRpcProvider
) => {
  //remove call data cost from gas estimate as tx will be called from contract
  transaction.gas = estimateGasWithoutCallData(
    transaction,
    config.gsn.gtxDataNonZero,
    config.gsn.gtxDataZero
  );

  const secondsNow = Math.round(Date.now() / 1000);
  const validUntilTime = (
    secondsNow + config.gsn.requestValidSeconds
  ).toString();

  const senderNonce = await getSenderNonce(
    account.address,
    config.gsn.forwarderAddress,
    web3Provider
  );

  const relayRequest: RelayRequest = {
    request: {
      from: transaction.from,
      to: transaction.to,
      value: transaction.value || '0',
      gas: parseInt(transaction.gas, 16).toString(),
      nonce: senderNonce,
      data: transaction.data,
      validUntilTime,
    },
    relayData: {
      maxFeePerGas: transaction.maxFeePerGas,
      maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
      transactionCalldataGasUsed: '',
      relayWorker: config.gsn.relayWorkerAddress,
      paymaster: config.gsn.paymasterAddress,
      forwarder: config.gsn.forwarderAddress,
      paymasterData: transaction.paymasterData?.toString() || '0x',
      clientId: '1',
    },
  };

  const transactionCalldataGasUsed = await estimateCalldataCostForRequest(
    relayRequest,
    config.gsn
  );

  relayRequest.relayData.transactionCalldataGasUsed = parseInt(
    transactionCalldataGasUsed,
    16
  ).toString();

  return relayRequest;
};

const buildRelayHttpRequest = async (
  relayRequest: RelayRequest,
  config: NetworkConfig,
  account: AccountKeypair,
  web3Provider: providers.JsonRpcProvider
) => {
  const signature = await signRequest(
    relayRequest,
    config.gsn.domainSeparatorName,
    config.gsn.chainId,
    account
  );

  const approvalData = '0x';

  const wallet = new ethers.VoidSigner(
    relayRequest.relayData.relayWorker,
    web3Provider
  );
  const relayLastKnownNonce = await wallet.getTransactionCount();
  const relayMaxNonce = relayLastKnownNonce + config.gsn.maxRelayNonceGap;

  const metadata = {
    maxAcceptanceBudget: config.gsn.maxAcceptanceBudget,
    relayHubAddress: config.gsn.relayHubAddress,
    signature,
    approvalData,
    relayMaxNonce,
    relayLastKnownNonce,
    domainSeparatorName: config.gsn.domainSeparatorName,
    relayRequestId: '',
  };
  const httpRequest = {
    relayRequest,
    metadata,
  };

  return httpRequest;
};

export const relayTransaction = async (
  account: AccountKeypair,
  config: NetworkConfig,
  transaction: GsnTransactionDetails
) => {
  const web3Provider = new ethers.providers.JsonRpcProvider(config.gsn.rpcUrl);
  const updatedConfig = await updateConfig(config, transaction);
  const relayRequest = await buildRelayRequest(
    updatedConfig.transaction,
    updatedConfig.config,
    account,
    web3Provider
  );
  const httpRequest = await buildRelayHttpRequest(
    relayRequest,
    updatedConfig.config,
    account,
    web3Provider
  );

  const relayRequestId = getRelayRequestID(
    httpRequest.relayRequest,
    httpRequest.metadata.signature
  );

  //update request metadata with relayrequestid

  httpRequest.metadata.relayRequestId = relayRequestId;

  const res = await axios.post(`${config.gsn.relayUrl}/relay`, httpRequest, {
    headers: authHeader(config),
  });
  return handleGsnResponse(res, web3Provider);
};
```


# Guides

###


# Add ERC2771Recipient

## Overview

Contracts that are [ERC2771](https://eips.ethereum.org/EIPS/eip-2771) compatible are natively supported by the RallyMobile SDK to perform gasless transactions. Contracts that are not ERC2771 compatible, but are whitelisted by us, can also perform gasless transactions

## If you are able to update the contract:

1. [Add `ERC2771Recipient` to your token contract](https://docs.opengsn.org/contracts/#install-opengsn-contracts) to make it compatible with our SDK.&#x20;
2. Set the trusted forwarder:
   1. Amoy: `0x0ae8FC9867CB4a124d7114B8bd15C4c78C4D40E5`  &#x20;
   2. Mainnet: `0xB2b5841DBeF766d4b521221732F9B618fCf34A87`
3. [View code example](https://gist.github.com/DonGambas/230389f11dc5f10137a510527175e01e)

{% hint style="info" %}
For ERC721 contracts, follow [Opensea's guide](https://docs.opensea.io/docs/polygon-basic-integration) to enable meta transactions for your NFTs.
{% endhint %}

## If you are unable to update the contract:

Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and [gasless transaction method](/unity/gasless-transactions/guides/determining-the-gasless-transaction-method) (`permit` or `executeMetaTransaction`) to have it reviewed and whitelisted by our team. &#x20;


# Determining the gasless transaction method

## **Overview**

Contracts that have the `executeMetaTransaction` or the `Permit` method for gasless transactions are natively supported by our SDK to perform gasless transactions.&#x20;

{% hint style="info" %}
Does your contract use a different gasless transaction method? Contact us on [Discord](https://discord.gg/rlynetwork) with the contract address and method to find a solution.
{% endhint %}

## executeMetaTransaction

Here is how to check if your contract supports the executeMetaTransaction method for gasless transactions:&#x20;

### Method 1

Verifying the Contract Name

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Code tab
4. If the Contract Name displays `UChildERC20Proxy`, it supports the `executeMetaTransaction` method.&#x20;

Here is an example of the RLY Token on [Polygonscan](https://polygonscan.com/token/0x76b8d57e5ac6afac5d415a054453d1dd2c3c0094#code)

<div align="left" data-full-width="false"><figure><img src="/files/l9JXLRln3BKTG1Lk7Uas" alt=""><figcaption></figcaption></figure></div>

{% hint style="info" %}
UChildERC20Proxy contracts inherit from the UChildERC20 contract that natively supports the executeMetaTransaction method.&#x20;
{% endhint %}

### Method 2

Verify the implementation contract&#x20;

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Read Contract
4. Navigate to the contract address under the implementation dropdown
5. Click on the Contract tab&#x20;
6. Click on the Write Contract&#x20;
7. Check that the contract has the executeMetaTransaction method

Here is an example of the RLY Token's implementation contract on [Polygonscan](https://polygonscan.com/address/0x93d4a27C64b9Dba29ed328c5801120a68B331C30#writeContract)

<div align="left"><figure><img src="/files/UXUD2yEdD9p1FCqIg1IQ" alt="" width="375"><figcaption></figcaption></figure></div>

## Permit

1. View your token contract on polygonscan
2. Click on the Contract tab
3. Click on the Write Contract
4. Check that the contract has the permit method

Here is an example of the TAKI Token's implementation contract on [Polygonscan](https://polygonscan.com/token/0xe78aee6ccb05471a69677fb74da80f5d251c042b#writeContract)

<div align="left"><figure><img src="/files/dNxdXs92iw25wpMaIZpM" alt="" width="375"><figcaption></figcaption></figure></div>


