# Authentication
Source: https://docs.rhino.fi/api-integration/authentication
Most API endpoints require you to authenticate your requests using a Json Web Token (JWT). You can obtain a JWT by making a `POST` request to [https://api.rhino.fi/authentication/auth/apiKey](https://api.rhino.fi/authentication/auth/apiKey) and including your API key in the payload.
```bash cURL theme={null}
curl https://api.rhino.fi/authentication/auth/apiKey \
-d '{"apiKey": "YOUR_API_KEY"}'\
-H 'content-type: application/json'
```
```javascript JavaScript theme={null}
const response = await fetch('https://api.rhino.fi/authentication/auth/apiKey', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
apiKey: 'YOUR_API_KEY',
}),
})
const { jwt } = await response.json()
```
Information on API keys can be found [here](https://docs.rhino.fi/get-started/api-keys).
## Using the JWT
Once you have a JWT, you need to include it in your API requests using the `Authorization` header. Example:
```bash cURL theme={null}
curl https://api.rhino.fi -H 'Authorization: YOUR_JWT'
```
```javascript JavaScript theme={null}
fetch('https://api.rhino.fi', {
headers: {
'Authorization': 'YOUR_JWT',
},
})
```
**A JWT obtained from an API key is only valid for one hour.** Once expired, you can just use your API key again in the same way to obtain a fresh JWT.
We recommend using the SDK if you use JavaScript/TypeScript for your application as it will take care of handling the JWT flow for you.
# Making a bridge
Source: https://docs.rhino.fi/api-integration/bridge
This guide walks you through integrating our bridge API into your application. By following these steps, you’ll be able to transfer assets between chains using our API and smart contract interactions.
## Quickstart
The whole process consists of:
Get a JWT to authenticate your requests.
Retrieve supported chains and tokens.
Obtain transaction details, including fees.
Confirm the transaction.
Interact with the smart contract to complete the transfer.
***
### 1. Authentication
All non-public API endpoints are authenticated using a JSON Web Token. To authenticate your requests, include the token in the `Authorization` header. [Learn about API authentication](/api-integration/authentication).
### 2. Fetch Bridge Configs
Retrieve available chains and supported tokens to ensure your transaction uses the correct parameters.
```javascript getBridgeConfigs.js theme={null}
export const getBridgeConfigs = async () => {
const request = await fetch('https://api.rhino.fi/bridge/configs')
return request.json()
}
```
For the exact response format and more details, see the [API Reference](/api-reference/bridge/configs/obtain-token-and-chain-details-for-swapping-andor-bridging-activity).
### 3. Get a Bridge Quote
Before executing a bridge transaction, you must generate a quote that provides transaction details, including fees and amounts.
```javascript getBridgeUserQuote.js theme={null}
export const getBridgeUserQuote = async (payload, jwt) => {
const request = await fetch(`https://api.rhino.fi/bridge/quote/bridge-swap/user`, {
headers: {
"content-type": "application/json",
"authorization": jwt
},
method: "POST",
body: JSON.stringify(payload)
})
return request.json()
}
```
For the exact request / response format and more details, see the [API Reference](/api-reference/bridge/quote/generate-a-bridgebridge+swap-quote).
### 4. Commit the Quote
Once you have a quote, you must commit it to confirm the transaction before execution. The deadline to commit a quote is provided within the `expiresAt` param within the quote response.
```javascript commitBridgeUserQuote.js theme={null}
export const commitBridgeUserQuote = async (quoteId, jwt) => {
const request = await fetch(`https://api.rhino.fi/bridge/quote/commit/${quoteId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'authorization': jwt
}
})
return request.json()
}
```
For the exact request / response format and more details, see the [API Reference](/api-reference/bridge/quote/commit-an-already-generated-quote).
### 5. Execute the Bridge Transaction
Now, you can execute the bridge transaction by interacting with the smart contract.
See [examples](/contracts/contract-examples) for interacting with the bridge smart contracts across different blockchain environments.
### 6. Full Example
Here’s the complete implementation combining all the steps above:
```javascript index.js theme={null}
import { getBridgeUserQuote } from "./getBridgeUserQuote";
import { getBridgeConfigs } from "./getBridgeConfigs";
import { commitBridgeUserQuote } from "./commitBridgeUserQuote";
const JWT = 'YOUR_JWT'; // Replace with your JWT generated from our API
const amount = '3';
const chainIn = 'BASE';
const chainOut = 'SOLANA';
const token = 'USDT';
const depositorAddress = '0x0000000000000000000000000000000000000000'; // Replace with your depositor address
const recipientAddress = '0x0000000000000000000000000000000000000000'; // Replace with your recipient address
const bridge = async () => {
// Get bridge configs to determine supported chains and tokens or later use for the contract call
const configs = await getBridgeConfigs();
// Get a bridge quote for the transaction
const quote = await getBridgeUserQuote({
amount,
chainIn,
chainOut,
tokenIn: token,
tokenOut: token,
mode: 'receive',
depositor: depositorAddress,
recipient: recipientAddress,
amountNative: '0'
}, JWT);
if (!quote?.quoteId) throw new Error('Failed to generate user quote.');
// Commit the quote to confirm the transaction when you are ready to send on-chain
const commitResult = await commitBridgeUserQuote(quote.quoteId, JWT);
if (!commitResult?.quoteId) throw new Error('Failed to commit user quote.');
const chainConfig = configs[chainIn];
// Execute the bridge transaction by interacting with the smart contract -- see contract examples section on exact implementation
await callBridgeContract({
chainConfig,
amount: quote.payAmount,
token,
commitmentId: commitResult.quoteId,
callback: (hash) => console.info('Transaction hash:', hash)
});
};
bridge();
```
We recommend using the [SDK](https://docs.rhino.fi/sdk/bridge-functions/bridge) for a simpler bridging experience.
For a detailed breakdown of API endpoints and parameters, check the [API Reference](/api-reference).
Further examples can be found within the [bridge-examples README](https://github.com/rhinofi/bridge-examples).
## Next steps
For the smart contract integration see - [contract examples](/contracts/contract-examples)
To track your bridge transaction status, see [Bridge Status & History](/api-integration/status-history).
# API Integration
Source: https://docs.rhino.fi/api-integration/introduction
Integrate the Rhino Stablecoin Activation Stack directly via REST API.
## API Integration
The Rhino API is a REST API that gives you direct access to the full Rhino Stablecoin Activation Stack. Use it to:
* Generate and manage Smart Deposit Addresses
* Fetch bridge configs, generate quotes, commit quotes, and execute bridges
* Query transaction status and history
* Configure Automated Onchain Actions (Extension)
All non-public endpoints are authenticated with a JSON Web Token. See [Authentication](/api-integration/authentication) for details.
If you use JavaScript or TypeScript, the [SDK](/sdk/quickstart) wraps the API and reduces integration time significantly. For all other languages, use the API directly — it is straightforward REST with JSON payloads.
## Useful resources
* [API Reference](/api-reference/introduction) — full endpoint documentation with request/response schemas
* Swagger schemas — for generating client code in your preferred language:
* [Authentication](https://api.rhino.fi/authentication/swagger.json)
* [Smart Deposit Addresses](https://api.rhino.fi/sda/swagger.json)
* [Bridge](https://api.rhino.fi/bridge/swagger.json)
# Smart Deposit Addresses
Source: https://docs.rhino.fi/api-integration/smart-deposits
Use the Smart Deposit Addresses API to generate addresses that accept stablecoin deposits from any supported chain and automatically settle to a specified destination. Includes support for cross-stable swaps, Automated Onchain Actions, and configurable fee handling.
**Default limits apply to Smart Deposit Addresses for initial testing purposes:**
* 500 total SDAs
* 50 SDAs generated per hour
Please contact your Rhino.fi account manager and the customer success team to lift your limits.
## Introduction
Smart Deposit Addresses are the core deposit primitive in the Rhino.fi Stablecoin Activation Stack. Generate one address per user; Rhino.fi handles chain detection, routing, settlement, and compliance automatically. This allows customers to easily send any supported asset to the Smart Deposit Address, without worrying about needing a specific chain-token pairing, but allows clients to receive the funds in their required chain-token pairing, and take additional activation steps to ensure that funds land in a usable balance.
## How it works
Get a JWT to authenticate your requests.
Retrieve supported [chains](https://docs.rhino.fi/api-reference/bridge/configs/obtain-token-and-chain-details-for-swapping-andor-bridging-activity) and [tokens](https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-the-supported-tokens-for-a-smart-deposit-address).
Call our API to [generate a Smart Deposit Address](https://docs.rhino.fi/api-reference/sda/depositaddresses/generate-a-new-smart-deposit-address) which will allow funds to be received across multiple chains. We strongly recommend generating an SDA on all available EVM chains to ensure that funds accidentally sent by customers to the wrong chain and automatically detected. More best practices can be found [here.](https://learn.rhino.fi/articles/9715155695-what-are-best-practices-when-operating-sdas)
Any [supported assets](https://docs.rhino.fi/get-started/supported-chains) can then be sent to the Smart Deposit Address.
Rhino.fi monitors the Smart Deposit Addresses to detect when funds are received and will then take the necessary bridging and swapping actions to ensure funds land in an activated state on the specified destination.
Smart Deposit Addresses are reusable and can be used for multiple bridges indefinitely (see caveats below).
## Fees
Your Rhino.fi PAYG/Subscription covers use of both the Rhino.fi Bridge as well as Smart Deposit Addresses. Therefore your Flow Credit allocation is across both. To understand how many Flow Credits a transaction into a Smart Deposit Address will consume, the `POST/bridge/quote/bridge-swap/user` endpoint should be called with the `isSda` parameter set as `true`. The response will also provide a fee breakdown showing what additional fees are charged to the client (such as onchain [gas fee charges](https://learn.rhino.fi/articles/7749894324-tron-and-solana-onchain-gas-costs)) or are being passed onto the end customer. Where a Client Surcharge is being applied, this will be shown in the fee amount and settled up to the client within end of month invoicing.
Learn more about Rhino.fi's fee structure [here.](https://learn.rhino.fi/articles/4043100177-fees-commercial-structure)
N.B for clients on the [legacy pricing structure](https://learn.rhino.fi/articles/4088519114-how-fees-are-applied), any fees will be deducted from the amount settled on the destination chain.
**Advanced Fee and Limit Management**
Rhino.fi has the Advanced Fee and Limit Management extension which is optimal for any clients who are looking to set up more complex or variable fee structures - beyond passing on a simple Client Surcharge to their end customers. See [**Advanced Fee & Limit Management**](https://docs.rhino.fi/get-started/extensions/advanced-fee-management) for details and please reach out to your Rhino.fi Account Manager or the partnership team to discuss your optimal fee set up.
## Smart Deposit Address Expiration
Smart Deposit Addresses are re-usable, meaning they can function as a receive for customer funds repeatedly and Rhino.fi will continue to actively monitor for deposits and process bridging and swapping activity to the destination of choice. Rhino.fi will monitor a Smart Deposit Address for **7 years**, and thereafter it switches into an unmonitored state to conserve resources.
## Supported chains and tokens
Smart Deposit Addresses are enabled across a [wide variety of tokens and chains](https://docs.rhino.fi/get-started/supported-chains). This is a growing list and based on client demand so please do reach out with any specific token or chain requests.
The general bridge config endpoint provides a boolean `enabledDepositAddress` flag that can be used to find chains that Smart Deposit Addresses are enabled for. Example:
```javascript theme={null}
const response = await fetch('https://api.rhino.fi/bridge/configs')
```
The response will then specify which chains have Smart Deposit Addresses enabled:
```json highlight={5,10} theme={null}
{
"ETHEREUM": {
"name": "Ethereum",
...,
"enabledDepositAddress": true,
},
"TON": {
"name": "Ton",
...,
"enabledDepositAddress": false,
},
}
```
To get the list of tokens supported between a deposit chain and destination chain you can execute the following code:
```javascript theme={null}
const depositChain = 'ETHEREUM'
const destinationChain = 'BASE'
const tokenOut = 'USDT' // optional
const response = await fetch(`https://api.rhino.fi/sda/deposit-addresses/${depositChain}/${destinationChain}/supported-tokens?tokenOut=${tokenOut}`, {
method: 'GET',
headers: {
'authorization': 'YOUR_JWT',
},
})
```
It will return the list of supported tokens between the 2 specified chains along with their associated deposit limit.
For the exact response format and more details, see the [**API Reference**](https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-the-supported-tokens-for-a-smart-deposit-address).
## API interactions
The following examples showcase the use of our API to manage Smart Deposit Addresses. All those calls require authentication, see details on this [here](/api-integration/authentication).
### Generating a Smart Deposit Address
You can generate a new Smart Deposit Address with the following API call:
```javascript theme={null}
const response = await fetch('https://api.rhino.fi/sda/deposit-addresses', {
method: 'POST',
body: JSON.stringify({
depositChains: ['ETHEREUM'],
destinationChain: 'BASE',
destinationAddress: '0x123...',
}),
headers: {
'Content-Type': 'application/json',
'authorization': 'YOUR_JWT',
},
})
```
The response will then look like this:
```json highlight={3,6} theme={null}
[{
"depositChain": "ETHEREUM",
"depositAddress": "0x456...",
"destinationChain": "BASE",
"destinationAddress": "0x123...",
"supportedTokens": [
{
"symbol": "USDT",
"address": "0x789...",
"maxDepositLimitUsd": 100000,
"minDepositLimitUsd": 10,
}
],
"isActive": true,
}]
```
For the exact response format and more details, see the [**API Reference**](https://docs.rhino.fi/api-reference/sda/depositaddresses/generate-a-new-smart-deposit-address).
The first highlighted line contains the generated Smart Deposit Address. Funds can be sent there to be bridged.
However, only tokens from the supportedTokens list will be processed. Transfers of tokens that are not in this list will not be processed. Transfers that are smaller than the `minDepositLimitUsd` or larger than the `maxDepositLimitUsd` will not be processed (there is a "grace" window to account for different price sources and price fluctuations). If this happens, funds can be returned through our Account Management team.
#### Multiple deposit chains
You can provide multiple deposit chains in the request. The API will then generate one Smart Deposit Address that can be used on all the provided chains. This is why the response is also a list - one element for each chain provided. The Smart Deposit Addresses in the individual elements will be identical in this case.
**Please note that providing multiple deposit chains only works for EVM chains currently.** Only then can the same address be used for all of them.
#### Swaps
When creating a Smart Deposit Address you can also specify a `tokenOut` field. This will cause all withdrawals on the destination chain to be in that token, swapping if needed. This is currently only supported for stablecoins (USDC and USDT). The given `tokenOut` needs to be a bridgable token on the destination chain, otherwise creation of the SDA will fail.
**1:1 Stablecoin Swaps (Extension)**
If your account has the 1:1 Stablecoin Swaps extension enabled, cross-stable conversions (e.g. USDT deposited → USDC settled) are processed at a guaranteed 1:1 rate. No slippage and no exposure to market price. This applies automatically to any SDA configured with a cross-stable `tokenOut`. Contact your account representative to enable this extension.
#### Post Bridge Data
Rhino.fi provides the option to execute contract calls as part of the withdrawal process on the destination chain so that funds can land in a fully activated state. For security reasons, only predefined actions are available. Please [contact us](mailto:partnerships@rhino.fi) with details on your use case if interested.
#### Refund address
Rhino.fi provides the option to pass a refund address when creating a Smart Deposit Address so that, in the case of an unsuccessful transaction, funds can be reverted back to the provided address. However we recommend that any refunds are dealt with on a case by case basis directly with our Account Management team instead, to ensure that customer funds sent from pooled locations; such as a CEX, are able to be handled successfully. We therefore suggest that this field is not used unless you are highly confident that the customer will be able to access the sending address again.
#### Custom data and reusing of addresses
When creating a Smart Deposit Address you can provide a `addressNote` field to be stored in our database. This field will be returned from all status endpoints as well.
Additionally, a `reusePolicy` can be specified when creating a Smart Deposit Address. It can have the following values:
* `reuse-existing`: Reuse an existing Smart Deposit Address if it exists. Only create a new one if no existing address matches the given parameters.
* `create-new`: Always create a new Smart Deposit Address.
When no `reusePolicy` is specified it defaults to `reuse-existing`.
To determine if a Smart Deposit Address already exists, the following combination of fields is used:
* `depositChain`
* `destinationChain`
* `destinationAddress`
* `userId` (only Smart Deposit Addresses previously created by you will be considered)
* `addressNote` if provided
* `tokenOut` if provided
* `postBridgeData` if provided
* `webhookUrl` if provided
* `refundAddress` if provided
Example use cases for this feature:
* Your users have unique destination addresses and you want to show users their previously used Smart Deposit Address.
* You consolidate multiple Smart Deposit Addresses into the same destination address but store a user ID in the `addressNote` field to make sure to only have one address per user.
### Checking Smart Deposit Address status
You can also check the current status of a Smart Deposit Address with the following call:
```javascript theme={null}
const depositAddress = '0x123...'
const depositChain = 'ETHEREUM'
const response = await fetch(`https://api.rhino.fi/sda/deposit-addresses/${depositAddress}/${depositChain}`, {
headers: {
'authorization': 'YOUR_JWT',
},
})
```
The response format will be the same as the one returned when generating a new Smart Deposit Address with the exception of the destinationAddress. That field will only be included if the request was made with a secret API key.
For the exact response format and more details, see the [**API Reference**](https://docs.rhino.fi/api-reference/sda/depositaddresses/get-deposit-address-status).
### Checking Smart Deposit Address history
You can check the history of a Smart Deposit Address with the following call:
```javascript theme={null}
const depositAddress = '0x123...'
const depositChain = 'ETHEREUM'
const response = await fetch(`https://api.rhino.fi/sda/deposit-addresses/${depositAddress}/${depositChain}/history`, {
headers: {
'authorization': 'YOUR_JWT',
},
})
```
The response will contain a list of transfers that have been processed through the Smart Deposit Address. By default it gives you the transfers from the last 7 days. To see older transfers you can provide a `from` and `to` query parameter with a timestamp in milliseconds. Only a `SECRET_` key is allowed to access history.
For the exact response format and more details, see the [**API Reference**](https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-a-list-of-previous-activity-for-a-smart-deposit-address).
### Checking Smart Deposit Address bridge status with a PUBLIC API key
Generally the above history endpoint can be used to track the bridges through a Smart Deposit Address. However it is not possible to use this in a frontend integration as a SECRET API key would be exposed in an unsafe browser environment.
To also track bridges in this case, a public status endpoint exists. However, this endpoint additionally takes a `destinationAddress` as query parameter that has to match the requested Smart Deposit Address (this value should still be available in the local state after creating a Smart Deposit Address). If it does not match, the request will be rejected.
Example:
```javascript theme={null}
const depositAddress = '0x123...'
const depositChain = 'ETHEREUM'
const destinationAddress = '0x456...'
const response = await fetch(`https://api.rhino.fi/sda/deposit-addresses/${depositAddress}/${depositChain}/public-status?destinationAddress=${destinationAddress}`, {
headers: {
'authorization': 'YOUR_JWT_FROM_SECRET_KEY',
},
})
```
The response will contain the last bridge (accepted or rejected) through the Smart Deposit Address in the `lastBridges` field. If this array is empty it means that no transfer has been picked up yet.
Please note that this array will currently only contain the last event. In the future this endpoint will be expanded with the option to return all bridges after a certain timestamp.
### Search your addresses
Rhino.fi provides an endpoint that allows you to search for your Smart Deposit Addresses with some optional filters.
This endpoint is authenticated and will only return the addresses associated with your user.
The result is paginated and you can set the `pageToken` field to get to the next page. If not provided, it will return the first page of results and a `nextTokenPage` value that can be used to query for the next page.
The following filters are available (all are optional and can be combined):
* `addressNote`
* `depositChain`
* `destinationChain`
* `destinationAddress`: requires `destinationChain` to be set as well
```javascript theme={null}
// all filters are optional and can be combined
const addressNote = 'my_note'
const depositChain = 'ETHEREUM'
const destinationChain = 'BASE'
const destinationAddress = '0x....'
const pageSize = 20 // optional, default to 20, max 50
const pageToken = "eyJkZXBvc2l0Q2hhaW4iOiJCQVNFIiwiZGVwb3NpdEFkZHJlc3MiOiIweDlmYTE1MDdiNTY0YzRlNWM1YmJjN2E5Yzc1ZWQ1MmFhMTc5ODIxNjkifQ==" // optional, returns the first page of results if not provided
const response = await fetch(
`https://api.rhino.fi/sda/deposit-addresses/search?addressNote=${addressNote}&depositChain=${depositChain}&destinationChain=${destinationChain}&destinationAddress=${destinationAddress}&page=${page}&limit=${limit}`,
{
method: 'GET',
headers: {
'authorization': 'YOUR_JWT',
},
},
)
```
For the exact response format and more details, see the [**API Reference**](https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-a-specific-smart-deposit-addresses-based-on-various-criteria).
# Bridge Status & History
Source: https://docs.rhino.fi/api-integration/status-history
This section explains how to retrieve the status and history of your bridge transactions. You can use these endpoints to monitor transaction progress and confirm successful transfers.
## **1. Get Bridge Transaction Status**
To check the status of a specific bridge, use the following API request:
```javascript getBridgeStatus.js theme={null}
export const getBridgeStatus = async (bridgeId, jwt) => {
const request = await fetch(`https://api.rhino.fi/bridge/history/bridge/${bridgeId}`, {
headers: {
"content-type": "application/json",
"authorization": jwt
},
method: "GET"
})
return request.json()
}
```
When you commit a bridge transaction, you receive a `quoteId` that you can use to track the transaction status.
```javascript theme={null}
import { getBridgeStatus } from './getBridgeStatus';
const JWT = 'YOUR_JWT'; // Replace with your JWT generated from our API
const quoteId = '123456'; // Replace with your quote ID
const status = await getBridgeStatus(quoteId, JWT);
const depositTxHash = status.state === 'ACCEPTED' || status.state === 'EXECUTED' ? status.depositTxHash : ''
const withdrawTxHash = status.state === 'EXECUTED' ? status.withdrawTxHash : ''
console.log('Bridge status:', status.state);
console.log('Deposit transaction hash:', depositTxHash);
console.log('Withdraw transaction hash:', withdrawTxHash);
```
#### **Possible state values:**
**Bridge (non‑swap) flows**
* `PENDING` – Quote committed; we’re waiting for a deposit on the source chain.
* `PENDING_CONFIRMATION` – *Fast chains only (WebSocket monitored)*. We’ve detected the deposit and are waiting for network confirmations. Not every bridge moves through this state.
* `ACCEPTED` – We’ve detected the deposit and are executing on the destination chain.
* `EXECUTED` – Bridge is complete; funds are credited on the destination chain.
* `CANCELLED` – *Cosmetic state*. Can occur only if the user cancels a **pending** bridge via API. If the deposit later arrives, the bridge will move out of this state and be executed. Useful for hiding pending records you no longer intend to fund.
* `FAILED` – *Rare, manual state*. Used only when there’s a specific issue and no way to complete the bridge. The user should contact Customer Support for a refund if one hasn’t already been issued.
**Additional states for swap flows**
* `DEPOSIT_ACCEPTED` – We’ve accepted the deposit and are performing a pre‑swap on the source chain (occurs **before** `ACCEPTED`).
* `SWAP_FAILED` – The swap failed; we’ll look into issuing a refund.
* `SWAP_FAILED_REFUNDED` – The swap failed and a refund was successfully issued.
For the full response format and more details, see the [API Reference](https://docs.rhino.fi/api-reference/bridge/history/obtain-the-status-of-a-specific-bridge-transaction).
***
## **2. Get Bridge History**
To retrieve a list of past transactions:
rhino.fi prioritizes transaction privacy, so the history endpoint is disabled for public API keys. Only secret API keys can access the history endpoint. Those API keys should only be used in code that is not run client side.
```javascript getBridgeUserHistory.js theme={null}
export const getBridgeUserHistory = async (jwt, pageToken) => {
const queryParams = new URLSearchParams({
pageSize: '20',
sortBy: 'createdAt',
sortDirection: 'desc',
pageToken,
})
const request = await fetch(`https://api.rhino.fi/history-cursor/user?${queryParams.toString()}`, {
headers: {
"content-type": "application/json",
"authorization": jwt
},
method: "GET"
})
return await request.json()
}
```
```javascript theme={null}
import { getBridgeUserHistory } from './getBridgeUserHistory';
const JWT = 'YOUR_JWT'; // Replace with your JWT generated from our API
const history = await getBridgeUserHistory(JWT);
console.log('Bridge history page 1:', history.items);
# Fetch next page by using the nextPageToken
const nextPage = await getBridgeUserHistory(JWT, history.nextPageToken);
console.log('Bridge history page 2:', nextPage.items);
```
The response will contain a `nextPageToken` field if there are more items matching the query. That field can then be passed into a followup requests with the same parameters to fetch the next page.
The page + limit based endpoint previously described on this page is deprecated and will be removed soon. Please use the cursor based endpoint described above which has better performance on larger history sizes.
***
## **Next Steps**
* If you haven’t integrated the bridge yet, start with the [Quickstart Guide](/api-integration/bridge#quickstart).
* For contract-level interactions, see [Contract Examples](/contracts/contract-examples).
# Making a bridge & swap
Source: https://docs.rhino.fi/api-integration/swap
This guide walks you through integrating our bridge API into your application. By following these steps, you’ll be able to transfer and swap assets between chains using our API and smart contract interactions.
## Quickstart
The whole process consists of:
Get a JWT to authenticate your requests.
Retrieve supported chains and tokens.
Obtain transaction details, including fees.
Confirm the transaction.
Interact with the smart contract to complete the transfer.
***
### 1. Authentication
All non-public API endpoints are authenticated using a JSON Web Token. To authenticate your requests, include the token in the `Authorization` header. [Learn about API authentication](/api-integration/authentication).
### 2. Fetch Bridge Configs
Retrieve available chains to ensure your transaction uses the correct parameters.
```javascript getBridgeConfigs.js theme={null}
export const getBridgeConfigs = async () => {
const request = await fetch('https://api.rhino.fi/bridge/configs')
return request.json()
}
```
For the exact response format and more details, see the [API Reference](https://docs.rhino.fi/api-reference/bridge/configs/obtain-token-and-chain-details-for-swapping-andor-bridging-activity).
### 3. Fetch Swap token config
Retrieve available tokens for swap transactions.
```javascript theme={null}
export const getSwapTokenConfig = async () => {
const request = await fetch('https://api.rhino.fi/bridge/bridge-swap-token-configs')
return request.json()
}
```
For the exact response format and more details, see the [API Reference](https://docs.rhino.fi/api-reference/bridge/configs/obtain-details-for-which-swap-tokens-are-supported-by-rhinofi-on-which-chains).
### 4. Get a Bridge & Swap Quote
Before executing a bridge transaction, you must generate a quote that provides transaction details, including fees and amounts.
```javascript getBridgeSwapUserQuote.js theme={null}
export const getBridgeSwapUserQuote = async (payload, jwt) => {
const request = await fetch(`https://api.rhino.fi/bridge/quote/bridge-swap/user`, {
headers: {
"content-type": "application/json",
"authorization": jwt
},
method: "POST",
body: JSON.stringify(payload)
})
return request.json()
}
```
For the exact request / response format and more details, see the [API Reference](https://docs.rhino.fi/api-reference/bridge/quote/generate-a-bridgebridge+swap-quote).
### 5. Commit the Quote
Once you have a quote, you must commit it to confirm the transaction before execution. The deadline to commit a quote is provided within the `expiresAt`param within the quote response.
```javascript commitBridgeSwapUserQuote.js theme={null}
export const commitBridgeSwapUserQuote = async (quoteId, jwt) => {
const request = await fetch(`https://api.rhino.fi/bridge/quote/commit/${quoteId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'authorization': jwt
}
})
return request.json()
}
```
For the exact request / response format and more details, see the [API Reference](https://docs.rhino.fi/api-reference/bridge/quote/commit-an-already-generated-quote).
### 6. Execute the Bridge & Swap Transaction
Now, you can execute the bridge transaction by interacting with the smart contract.
See [examples](/contracts/contract-examples) for interacting with the bridge smart contracts across different blockchain environments.
### 7. Full Example
Here’s the complete implementation combining all the steps above:
```javascript index.js theme={null}
import { getBridgeSwapUserQuote } from "./getBridgeSwapUserQuote";
import { getBridgeConfigs } from "./getBridgeConfigs";
import { getSwapTokenConfig } from "./getSwapTokenConfig";
import { commitBridgeSwapUserQuote } from "./commitBridgeSwapUserQuote";
const JWT = 'YOUR_JWT'; // Replace with your JWT generated from our API
const amount = '1';
const chainIn = 'BASE';
const chainOut = 'ARBITRUM';
const tokenIn = 'USDT';
const tokenOut = 'USDC';
const depositorAddress = '0x0000000000000000000000000000000000000000'; // Replace with your depositor address
const recipientAddress = '0x0000000000000000000000000000000000000000'; // Replace with your recipient address
const bridgeAndSwap = async () => {
// Get bridge configs to determine supported chains and tokens or later use for the contract call
const configs = await getBridgeConfigs();
const swapTokenConfig = await getSwapTokenConfig();
// Get a bridge quote for the transaction
const quote = await getBridgeSwapUserQuote({
amount,
chainIn,
chainOut,
tokenIn,
tokenOut,
mode: 'pay',
depositor: depositorAddress,
recipient: recipientAddress,
amountNative: '0'
}, JWT);
if (!quote?.quoteId) throw new Error('Failed to generate user quote.');
// Commit the quote to confirm the transaction when you are ready to send on-chain
const commitResult = await commitBridgeSwapUserQuote(quote.quoteId, JWT);
if (!commitResult?.quoteId) throw new Error('Failed to commit user quote.');
const chainConfig = configs[chainIn];
const swapConfig = swapTokenConfig[chainIn][tokenIn];
// Execute the bridge transaction by interacting with the smart contract -- see contract examples section on exact implementation
await callBridgeContract({
chainConfig,
swapConfig,
amount: quote.payAmount,
tokenIn,
commitmentId: commitResult.quoteId,
callback: (hash) => console.info('Transaction hash:', hash)
});
};
bridgeAndSwap();
```
For a detailed breakdown of API endpoints and parameters, check the [API Reference](/api-reference/introduction).
## Same-chain swaps
When `chainIn === chainOut`, the quote response is flagged as an **atomic same-chain swap** (`isAtomicSwap: true`). These go through a separate contract and a different transaction format than a regular bridge deposit — broadcasting a `depositWithId` / `depositNativeWithId` for an atomic same-chain swap commitment will be rejected by the deposit watcher.
Same-chain swaps are **EVM-only**.
The flow is identical up to step 5 (commit), then diverges:
### Detect the case
After fetching the quote in step 4, check the response:
```javascript theme={null}
const isSameChainSwap =
quote.chainIn === quote.chainOut && quote.isAtomicSwap === true
```
### Use a different approval target
For ERC-20 same-chain swaps, the approval target is the chain's `sameChainSwapsAddress` (returned in `GET /bridge/configs` under the chain entry) — **not** the regular `contractAddress`. Native-token swaps don't require an approval.
### Fetch the swap calldata instead of building a deposit tx
Instead of calling the deposit contract, fetch the prepared calldata. The `commitmentId` is the `quoteId` you committed.
```javascript getSwapCalldata.js theme={null}
export const getSwapCalldata = async (commitmentId, jwt) => {
const request = await fetch(`https://api.rhino.fi/bridge/swap/calldata/${commitmentId}`, {
headers: { 'authorization': jwt }
})
return request.json()
}
```
The response contains a JSON-stringified transaction object:
```json theme={null}
{
"calldata": "{\"to\":\"0x...\",\"data\":\"0x...\",\"value\":\"...\"}"
}
```
Parse it and submit it as a raw EVM transaction from the depositor wallet:
```javascript ethers theme={null}
import { ethers } from 'ethers'
const { calldata } = await getSwapCalldata(commitResult.quoteId, JWT)
const { to, data, value } = JSON.parse(calldata)
const tx = await wallet.sendTransaction({ to, data, value: BigInt(value ?? 0) })
await tx.wait()
```
```javascript viem theme={null}
const { calldata } = await getSwapCalldata(commitResult.quoteId, JWT)
const { to, data, value } = JSON.parse(calldata)
const hash = await walletClient.sendTransaction({ to, data, value: BigInt(value ?? 0) })
await publicClient.waitForTransactionReceipt({ hash })
```
Do **not** call `depositWithId` / `depositNativeWithId` on the regular deposit contract for same-chain swaps.
## Next steps
For the smart contract integration see - [contract examples](/contracts/contract-examples)
To track your bridge transaction status, see [Bridge Status & History](/api-integration/status-history).
# Obtain details for which swap tokens are supported by Rhino.fi on which chains
Source: https://docs.rhino.fi/api-reference/bridge/configs/obtain-details-for-which-swap-tokens-are-supported-by-rhinofi-on-which-chains
https://api.rhino.fi/bridge/swagger.json get /bridge-swap-token-configs
This endpoint provides a list of which swap tokens are supported by Rhino.fi on which chains and key details for each chain and token. These are the tokens available for swapping activity.
# Obtain token and chain details for swapping and/or bridging activity
Source: https://docs.rhino.fi/api-reference/bridge/configs/obtain-token-and-chain-details-for-swapping-andor-bridging-activity
https://api.rhino.fi/bridge/swagger.json get /configs
This endpoint provides key information about the chains and tokens that Rhino.fi supports swapping and/or bridging across as well as field names that should be used in other endpoints.
# Obtain token and chain details for swapping and/or bridging activity for a specific token on a specific chain
Source: https://docs.rhino.fi/api-reference/bridge/configs/obtain-token-and-chain-details-for-swapping-andor-bridging-activity-for-a-specific-token-on-a-specific-chain
https://api.rhino.fi/bridge/swagger.json get /config/{chain}/{token}
This endpoint provides key information a chain or token that Rhino.fi supports swapping and/or bridging across as well as field names that should be used in other endpoints.
# Single Config (token address/chain)
Source: https://docs.rhino.fi/api-reference/bridge/configs/single-config-token-addresschain
https://api.rhino.fi/bridge/swagger.json get /config/tokens
Fetch config for a token by address
# Obtain a list of previously generated swapping and/or bridging quotes
Source: https://docs.rhino.fi/api-reference/bridge/history/obtain-a-list-of-previously-generated-swapping-andor-bridging-quotes
https://api.rhino.fi/bridge/swagger.json get /history-cursor/user
This endpoint provides a full history of a user's bridging and swapping activity. This includes commits for quotes generated through the `POST /quote/bridge-swap/user` endpoint and submitted through `POST /quote/commit/{quoteId}`, as well as bridges initiated via a Smart Deposit Address.
# Obtain the status of a specific bridge transaction
Source: https://docs.rhino.fi/api-reference/bridge/history/obtain-the-status-of-a-specific-bridge-transaction
https://api.rhino.fi/bridge/swagger.json get /history/bridge/{bridgeId}
This endpoint provides the status of a bridge transaction as created through the `POST /quote/bridge-swap/user` endpoint. For status information for Smart Deposit Address transactions, the `GET /sda/deposit-addresses/{depositAddress}/{depositChain}` endpoint should be used.
# Obtain the status of a specific bridge transaction by deposit hash
Source: https://docs.rhino.fi/api-reference/bridge/history/obtain-the-status-of-a-specific-bridge-transaction-by-deposit-hash
https://api.rhino.fi/bridge/swagger.json get /history/bridge/by-deposit-hash/{depositTxHash}
This endpoint provides the status of a bridge transaction as created through the `POST /quote/bridge-swap/user` endpoint based on a given deposit transaction hash. For status information for Smart Deposit Address transactions, the `GET /sda/deposit-addresses/{depositAddress}/{depositChain}` endpoint should be used.
# Obtain the status of a specific bridge transaction by withdrawal hash
Source: https://docs.rhino.fi/api-reference/bridge/history/obtain-the-status-of-a-specific-bridge-transaction-by-withdrawal-hash
https://api.rhino.fi/bridge/swagger.json get /history/bridge/by-withdraw-hash/{withdrawTxHash}
This endpoint provides the status of a bridge transaction as created through the `POST /quote/bridge-swap/user` endpoint based on a given withdrawal transaction hash. For status information for Smart Deposit Address transactions, the `GET /sda/deposit-addresses/{depositAddress}/{depositChain}` endpoint should be used.
# Patch Bridge Status
Source: https://docs.rhino.fi/api-reference/bridge/history/patch-bridge-status
https://api.rhino.fi/bridge/swagger.json patch /history/bridge/{bridgeId}
Patch the status of a bridge.
# Commit an already generated quote
Source: https://docs.rhino.fi/api-reference/bridge/quote/commit-an-already-generated-quote
https://api.rhino.fi/bridge/swagger.json post /quote/commit/{quoteId}
This endpoint allows you to commit a generated quote from the `POST /quote/bridge-swap/user` endpoint so that it is picked up and tracked by the Rhino.fi system.
# Generate a bridge/bridge+swap quote
Source: https://docs.rhino.fi/api-reference/bridge/quote/generate-a-bridgebridge+swap-quote
https://api.rhino.fi/bridge/swagger.json post /quote/bridge-swap/user
This endpoint allows you to obtain a quote for a bridge or bridge+swap transaction that can be submitted through the `POST /quote/commit/{quoteId}` endpoint.
# Public Quote for Bridge & Swap
Source: https://docs.rhino.fi/api-reference/bridge/quote/public-quote-for-bridge-&-swap
https://api.rhino.fi/bridge/swagger.json get /quote/bridge-swap/public
Fetch public bridge and swap quote.
# Calldata to execute a single swap
Source: https://docs.rhino.fi/api-reference/bridge/swap/calldata-to-execute-a-single-swap
https://api.rhino.fi/bridge/swagger.json get /swap/calldata/{commitmentId}
Get the calldata needed to execute a single swap.
# Rhino API Reference
Source: https://docs.rhino.fi/api-reference/introduction
Full endpoint reference for the Rhino Stablecoin Activation Stack — deposit address generation, bridge quotes and execution, transaction history, and Extensions.
If you're looking for a quick start guide, check out the [Quick Start](/api-integration/bridge#quickstart) guide.
## Rhino API Reference
The Rhino API is the integration surface for the Rhino Stablecoin Activation Stack. It covers deposit address generation, bridge quotes and execution, transaction history, and — for accounts with Extensions enabled — Automated Onchain Actions and advanced fee configuration.
Each endpoint includes example requests and responses to help you implement features quickly and efficiently.
**What's Covered**
* Authentication – How to securely connect to our API
* Endpoints – Detailed descriptions of available API calls
* Error Handling – Understanding API responses and troubleshooting issues
* Rate Limits – Guidelines on request limits
## Authentication
All non-public API endpoints are authenticated using a JSON Web Token. To authenticate your requests, include the token in the `Authorization` header.
To obtain a JWT for your app, please follow the [Authentication](/api-integration/authentication) guide.
# Generate a new smart deposit address
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/generate-a-new-smart-deposit-address
https://api.rhino.fi/sda/swagger.json post /deposit-addresses
This endpoint allows you to generate a new smart deposit address for a given set of chains and set the swapping and/or bridging activity that should be done for any funds sent to it.
# Get completed SDA refunds
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/get-completed-sda-refunds
https://api.rhino.fi/sda/swagger.json get /deposit-addresses/{depositAddress}/{depositChain}/completed-refunds
Returns the list of completed refunds for the given deposit address on a given chain.
# Get deposit address status
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/get-deposit-address-status
https://api.rhino.fi/sda/swagger.json get /deposit-addresses/{depositAddress}/{depositChain}
Returns the status of a deposit address on a given chain
# Get public deposit address status
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/get-public-deposit-address-status
https://api.rhino.fi/sda/swagger.json get /deposit-addresses/{depositAddress}/{depositChain}/public-status
Returns the status of a deposit address on a given chain
# Obtain a list of previous activity for a Smart Deposit Address
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-a-list-of-previous-activity-for-a-smart-deposit-address
https://api.rhino.fi/sda/swagger.json get /deposit-addresses/{depositAddress}/{depositChain}/history
This endpoint provides the history of activity (both successful and unsuccessful) for a given Smart Deposit Address within a given time window. By default the previous 7 days of activity are returned, with a maximum window of 31 days (configurable via the `from` and `to` query params).
# Obtain a specific Smart Deposit Address/es based on various criteria
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-a-specific-smart-deposit-addresses-based-on-various-criteria
https://api.rhino.fi/sda/swagger.json get /deposit-addresses/search
This endpoint allows you to find a specific Smart Deposit Address or Addresses based on provided criteria.
# Obtain the current rate limit usage for Smart Deposit Address creation
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-the-current-rate-limit-usage-for-smart-deposit-address-creation
https://api.rhino.fi/sda/swagger.json get /deposit-addresses/rate-limit-status
This endpoint allows you to see what your total number of generated Smart Deposit Addresses is, as well as what any limits across any frequencies are.
# Obtain the supported tokens for a Smart Deposit Address
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/obtain-the-supported-tokens-for-a-smart-deposit-address
https://api.rhino.fi/sda/swagger.json get /deposit-addresses/{depositChain}/{destinationChain}/supported-tokens
This endpoint provides key information about the chains and tokens that Rhino.fi supports swapping and/or bridging on for Smart Deposit Addresses.
# Pause, unpause, or update deposit addresses
Source: https://docs.rhino.fi/api-reference/sda/depositaddresses/pause-unpause-or-update-deposit-addresses
https://api.rhino.fi/sda/swagger.json patch /deposit-addresses/{depositAddress}
Pauses or unpauses deposit address(es) matching the given address, or updates the destination address using signature authorization. For Pause and Unpause, depositChain limits the action to one chain when provided; otherwise all matching deposit addresses are affected. Signature-authorized destination updates require custom per-project setup and are not generally available.
# Rhino.fi Webhook Public Key
Source: https://docs.rhino.fi/api-reference/webhook/signature/rhinofi-webhook-public-key
https://api.rhino.fi/webhook/swagger.json get /public-key
Rhino.fi public key used to verify webhook events signatures.
# Obtain a list of previously generated webhook events
Source: https://docs.rhino.fi/api-reference/webhook/user-events/obtain-a-list-of-previously-generated-webhook-events
https://api.rhino.fi/webhook/swagger.json get /user-events-cursor
This endpoint provides the webhook events generated for your project since a given point in time, using cursor-based pagination. It can be used to reconcile or replay events, for example where your webhook endpoint was unavailable at the time an event was sent.
# Overview
Source: https://docs.rhino.fi/contracts/contract-examples
This section provides integration examples for interacting with the rhino.fi bridge smart contracts across different blockchain environments. Whether you're working with **EVM-based chains** like Ethereum and Scroll or **non-EVM chains** such as Solana, Tron, TON, and Starknet, you'll find practical code samples to help you execute bridge transactions.
Select your target blockchain to explore contract interaction examples:
* [EVM Chains](./evm) – Ethereum, Scroll, and compatible chains
* [Solana](./solana)
* [Tron](./tron)
* [TON](./ton)
* [Starknet](./starknet)
If you prefer reading code locally or want to run these examples in your local environment, you can use the [Github examples repo](https://github.com/rhinofi/bridge-examples)
We recommend using the [SDK](/sdk/quickstart) to make bridges as it has all the blockchain interactions already built in.
# EVM (Ethereum / L2s)
Source: https://docs.rhino.fi/contracts/evm
This guide explains how to interact with the [**Bridge Contract**](https://github.com/rhinofi/contracts_public/blob/master/bridge-deposit/DVFDepositContract.sol) on EVM-compatible blockchains.
## **Contract Overview**
The `BridgeContract` exposes the following key function for bridging:
```solidity theme={null}
/**
* Deposit ERC20 tokens to the contract address with a commitment ID
*/
function depositWithId(
address token,
uint256 amount,
uint256 commitmentId
) public;
/**
* Deposit native chain currency into contract address with a commitment ID
*/
function depositNativeWithId(
uint256 commitmentId
) external payable;
```
## ABIs
```json bridgeAbi.json theme={null}
[
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "commitmentId",
"type": "uint256"
}
],
"name": "depositWithId",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "commitmentId",
"type": "uint256"
}
],
"name": "depositNativeWithId",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]
```
```json erc20Abi.json theme={null}
[
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
```
### Setup
For this example we'll use `ethers.js` to interact with the contract, but you can replace it with any EVM library of your choice.
Make sure to install it first:
```bash yarn theme={null}
yarn add ethers
```
```bash npm theme={null}
npm install ethers
```
## Interacting with the Contract
To execute a bridge transaction, call the `depositWithId` (ERC20) or `depositNativeWithId` (native) function as follows:
The `commitmentId` required by the contract corresponds to the `quoteId` returned by the API.
To obtain the `commitmentId`, first request a quote from the API, then commit it. The response will include the `quoteId`, which should be used as the `commitmentId` in the contract call.
See [the API bridge example](/api-integration/bridge#6-full-example) for details.
```javascript ethers theme={null}
import ethers from 'ethers'
import getBridgeConfigs from './getBridgeConfigs'
import erc20Abi from './erc20Abi'
import bridgeAbi from './bridgeAbi'
export const callEvmBridgeContract = async ({
address,
amount,
chain,
token,
commitmentId,
}) => {
// See API Integration -> Fetch Bridge Configs for the full implementation
const configs = await getBridgeConfigs()
const chainConfig = configs[chain]
// Replace with your wallet implementation
const wallet = new ethers.Wallet(EVM_PRIVATE_KEY, new ethers.JsonRpcProvider(chainConfig.rpc))
const tokenConfig = chainConfig.tokens[token]
const tokenAmount = +amount * 10 ** tokenConfig.decimals
const tokenAddress = tokenConfig.address
const bridgeContractAddress = chainConfig.contractAddress
const isNativeToken = chainConfig.nativeTokenName === token
const bridgeContract = new ethers.Contract(bridgeContractAddress, bridgeAbi, wallet)
// For ERC20 tokens we call 'depositWithId' and for native tokens we call 'depositNativeWithId'
if (!isNativeToken) {
// make sure to check token allowance first for erc20 tokens
const erc20Contract = new ethers.Contract(tokenAddress, erc20Abi, wallet)
const contractAllowance = await erc20Contract.allowance(address, bridgeContractAddress)
const allowance = parseFloat(contractAllowance.toString()) / 10 ** tokenConfig.decimals
if (allowance === 0 || allowance < parseFloat(amount)) {
const tx = await erc20Contract.approve(bridgeContractAddress, tokenAmount)
await tx.wait()
}
// Call the contract
const tx = await bridgeContract.depositWithId(tokenAddress, tokenAmount, BigInt(`0x${commitmentId}`))
return tx.wait()
}
const tx = await bridgeContract.depositNativeWithId(BigInt(`0x${commitmentId}`), {
value: parseUnits(amount, 'ether'),
})
return tx.wait()
}
```
# Solana Contracts
Source: https://docs.rhino.fi/contracts/solana
This guide explains how to interact with the rhino.fi Bridge program on Solana.
## **Contract Overview**
The Bridge program exposes the following function for bridging:
```solidity theme={null}
pub fn deposit_with_id(
ctx: Context,
amount: u64,
commitment_id: u128,
)
```
## IDL
```json idl theme={null}
{
version: '0.1.0',
name: 'bridge',
constants: [
{
name: 'AUTHORITY_SEED',
type: 'string',
value: '"authority"',
},
{
name: 'BRIDGE_SEED',
type: 'string',
value: '"rhino_bridge"',
},
],
instructions: [
{
name: 'createBridge',
accounts: [
{
name: 'bridge',
isMut: true,
isSigner: false,
},
{
name: 'admin',
isMut: false,
isSigner: false,
docs: ['The admin of the Bridge'],
},
{
name: 'payer',
isMut: true,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'systemProgram',
isMut: false,
isSigner: false,
docs: ['Solana ecosystem accounts'],
},
],
args: [],
},
{
name: 'deposit',
accounts: [
{
name: 'bridge',
isMut: false,
isSigner: false,
},
{
name: 'pool',
isMut: false,
isSigner: false,
},
{
name: 'poolAuthority',
isMut: false,
isSigner: false,
},
{
name: 'depositor',
isMut: false,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'mint',
isMut: false,
isSigner: false,
},
{
name: 'poolAccount',
isMut: true,
isSigner: false,
},
{
name: 'depositorAccount',
isMut: true,
isSigner: false,
},
{
name: 'payer',
isMut: true,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'tokenProgram',
isMut: false,
isSigner: false,
docs: ['Solana ecosystem accounts'],
},
{
name: 'associatedTokenProgram',
isMut: false,
isSigner: false,
},
{
name: 'systemProgram',
isMut: false,
isSigner: false,
},
{
name: 'eventAuthority',
isMut: false,
isSigner: false,
},
{
name: 'program',
isMut: false,
isSigner: false,
},
],
args: [
{
name: 'amount',
type: 'u64',
},
{
name: 'ethAddressUpper',
type: 'u32',
},
{
name: 'ethAddressLower',
type: 'u128',
},
],
},
{
name: 'depositWithId',
accounts: [
{
name: 'bridge',
isMut: false,
isSigner: false,
},
{
name: 'pool',
isMut: false,
isSigner: false,
},
{
name: 'poolAuthority',
isMut: false,
isSigner: false,
},
{
name: 'depositor',
isMut: false,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'mint',
isMut: false,
isSigner: false,
},
{
name: 'poolAccount',
isMut: true,
isSigner: false,
},
{
name: 'depositorAccount',
isMut: true,
isSigner: false,
},
{
name: 'payer',
isMut: true,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'tokenProgram',
isMut: false,
isSigner: false,
docs: ['Solana ecosystem accounts'],
},
{
name: 'associatedTokenProgram',
isMut: false,
isSigner: false,
},
{
name: 'systemProgram',
isMut: false,
isSigner: false,
},
{
name: 'eventAuthority',
isMut: false,
isSigner: false,
},
{
name: 'program',
isMut: false,
isSigner: false,
},
],
args: [
{
name: 'amount',
type: 'u64',
},
{
name: 'commitmentId',
type: 'u128',
},
],
},
{
name: 'createPool',
accounts: [
{
name: 'bridge',
isMut: false,
isSigner: false,
},
{
name: 'pool',
isMut: true,
isSigner: false,
},
{
name: 'poolAuthority',
isMut: false,
isSigner: false,
},
{
name: 'mint',
isMut: false,
isSigner: false,
},
{
name: 'poolAccount',
isMut: true,
isSigner: false,
},
{
name: 'admin',
isMut: false,
isSigner: true,
docs: ['The admin of the Bridge'],
},
{
name: 'payer',
isMut: true,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'tokenProgram',
isMut: false,
isSigner: false,
docs: ['Solana ecosystem accounts'],
},
{
name: 'associatedTokenProgram',
isMut: false,
isSigner: false,
},
{
name: 'systemProgram',
isMut: false,
isSigner: false,
},
],
args: [],
},
{
name: 'allowOperator',
accounts: [
{
name: 'bridge',
isMut: false,
isSigner: false,
},
{
name: 'operatorStorage',
isMut: true,
isSigner: false,
},
{
name: 'operator',
isMut: false,
isSigner: false,
},
{
name: 'admin',
isMut: false,
isSigner: true,
docs: ['The admin of the Bridge'],
},
{
name: 'payer',
isMut: true,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'systemProgram',
isMut: false,
isSigner: false,
docs: ['Solana ecosystem accounts'],
},
],
args: [
{
name: 'isAllowed',
type: 'bool',
},
],
},
{
name: 'withdraw',
accounts: [
{
name: 'bridge',
isMut: false,
isSigner: false,
},
{
name: 'pool',
isMut: false,
isSigner: false,
},
{
name: 'poolAuthority',
isMut: false,
isSigner: false,
},
{
name: 'operatorStorage',
isMut: false,
isSigner: false,
},
{
name: 'recipient',
isMut: false,
isSigner: false,
},
{
name: 'operator',
isMut: false,
isSigner: true,
},
{
name: 'mint',
isMut: false,
isSigner: false,
},
{
name: 'poolAccount',
isMut: true,
isSigner: false,
},
{
name: 'recipientAccount',
isMut: true,
isSigner: false,
},
{
name: 'payer',
isMut: true,
isSigner: true,
docs: ['The account paying for all rents'],
},
{
name: 'tokenProgram',
isMut: false,
isSigner: false,
docs: ['Solana ecosystem accounts'],
},
{
name: 'associatedTokenProgram',
isMut: false,
isSigner: false,
},
{
name: 'systemProgram',
isMut: false,
isSigner: false,
},
],
args: [
{
name: 'amount',
type: 'u64',
},
],
},
{
name: 'transferAdmin',
accounts: [
{
name: 'bridge',
isMut: true,
isSigner: false,
},
{
name: 'newAdmin',
isMut: false,
isSigner: false,
docs: ['The admin of the Bridge'],
},
{
name: 'admin',
isMut: false,
isSigner: true,
docs: ['The admin of the Bridge'],
},
{
name: 'systemProgram',
isMut: false,
isSigner: false,
docs: ['Solana ecosystem accounts'],
},
],
args: [],
},
],
accounts: [
{
name: 'bridge',
type: {
kind: 'struct',
fields: [
{
name: 'admin',
docs: ['Account that has admin authority over the Bridge'],
type: 'publicKey',
},
],
},
},
{
name: 'pool',
type: {
kind: 'struct',
fields: [
{
name: 'bridge',
docs: ['Primary key of the Bridge'],
type: 'publicKey',
},
{
name: 'mint',
docs: ['Mint of token A'],
type: 'publicKey',
},
],
},
},
{
name: 'operator',
type: {
kind: 'struct',
fields: [
{
name: 'operator',
docs: ['Public key of the operator'],
type: 'publicKey',
},
{
name: 'isAllowed',
docs: ['Whether the operator has access'],
type: 'bool',
},
],
},
},
],
events: [
{
name: 'BridgedDeposit',
fields: [
{
name: 'from',
type: 'publicKey',
index: false,
},
{
name: 'to',
type: 'publicKey',
index: false,
},
{
name: 'token',
type: 'publicKey',
index: false,
},
{
name: 'amount',
type: 'u64',
index: false,
},
{
name: 'ethAddressUpper',
type: 'u32',
index: false,
},
{
name: 'ethAddressLower',
type: 'u128',
index: false,
},
],
},
{
name: 'BridgedDepositWithId',
fields: [
{
name: 'from',
type: 'publicKey',
index: false,
},
{
name: 'to',
type: 'publicKey',
index: false,
},
{
name: 'token',
type: 'publicKey',
index: false,
},
{
name: 'amount',
type: 'u64',
index: false,
},
{
name: 'commitmentId',
type: 'u128',
index: false,
},
],
},
{
name: 'BridgeWithdraw',
fields: [
{
name: 'from',
type: 'publicKey',
index: false,
},
{
name: 'to',
type: 'publicKey',
index: false,
},
{
name: 'amount',
type: 'u64',
index: false,
},
],
},
],
errors: [
{
code: 6000,
name: 'DepositNotAllowed',
msg: 'DEPOSIT_NOT_ALLOWED',
},
{
code: 6001,
name: 'Unauthorized',
msg: 'UNAUTHORIZED',
},
{
code: 6002,
name: 'NotEnoughLiquidity',
msg: 'NOT_ENOUGH_LIQUIDITY',
},
{
code: 6003,
name: 'NotEnoughBalance',
msg: 'NOT_ENOUGH_BALANCE',
},
{
code: 6004,
name: 'InvalidAmount',
msg: 'INVALID_AMOUNT',
},
],
}
```
### Setup
For this example we'll use `@solana/web3.js`, `@solana/spl-token` and `@coral-xyz/anchor` to interact with the contract, but you can replace it with any Solana library of your choice.
Make sure to install it first:
```bash yarn theme={null}
yarn add @solana/web3.js @solana/spl-token @coral-xyz/anchor
```
```bash npm theme={null}
npm install @solana/web3.js @solana/spl-token @coral-xyz/anchor
```
## Set-up helpers for Solana accounts
```javascript solanaBridgeHelpers.js theme={null}
import { PublicKey } from '@solana/web3.js'
import { getAssociatedTokenAddressSync } from '@solana/spl-token'
import { Buffer } from 'buffer'
export const getSolanaBridgeKey = async (bridgeContractAddress) => {
const programId = new PublicKey(bridgeContractAddress)
return PublicKey.findProgramAddressSync([Buffer.from('rhino_bridge')], programId)[0]
}
export const getSolanaPoolKey = async (bridgeContractAddress, mint) => {
const programId = new PublicKey(bridgeContractAddress)
const bridgeKey = await getSolanaBridgeKey(bridgeContractAddress)
const mintId = new PublicKey(mint)
return PublicKey.findProgramAddressSync([bridgeKey.toBuffer(), mintId.toBuffer()], programId)[0]
}
export const getSolanaPoolAuthority = async (bridgeContractAddress, mint) => {
const programId = new PublicKey(bridgeContractAddress)
const bridgeKey = await getSolanaBridgeKey(bridgeContractAddress)
const mintId = new PublicKey(mint)
return PublicKey.findProgramAddressSync(
[bridgeKey.toBuffer(), mintId.toBuffer(), Buffer.from('authority')],
programId,
)[0]
}
export const getSolanaPoolTokenAccount = async (bridgeContractAddress, mint) => {
const poolAuthority = await getSolanaPoolAuthority(bridgeContractAddress, mint)
const mintId = new PublicKey(mint)
return getAssociatedTokenAddressSync(mintId, poolAuthority, true)
}
export const getSolanaDepositorAccount = async (secondaryWalletAddress, mint) => {
const mintId = new PublicKey(mint)
const depositor = new PublicKey(secondaryWalletAddress)
return getAssociatedTokenAddressSync(mintId, depositor, true)
}
```
## Interacting with the Contract
To execute a bridge transaction, call the `depositWithId` (ERC20) or `depositNativeWithId` (native) function as follows:
The `commitmentId` required by the contract corresponds to the `quoteId` returned by the API.
To obtain the `commitmentId`, first request a quote from the API, then commit it. The response will include the `quoteId`, which should be used as the `commitmentId` in the contract call.
See [the API bridge example](/api-integration/bridge#6-full-example) for details.
```javascript anchor theme={null}
import {Connection, PublicKey} from '@solana/web3.js'
import {AnchorProvider, Program, BN} from '@coral-xyz/anchor'
import {
getSolanaBridgeKey,
getSolanaDepositorAccount,
getSolanaPoolAuthority,
getSolanaPoolKey,
getSolanaPoolTokenAccount,
} from './solanaBridgeHelpers'
import {IDL} from "./idl";
import getBridgeConfigs from './getBridgeConfigs'
// Import a solana wallet / or use your wallet library
const secretKey = base58.decode(SOLANA_PRIVATE_KEY)
const keypair = Keypair.fromSecretKey(new Uint8Array(secretKey))
const wallet = new Wallet(keypair)
export const callSolanaBridgeContract = async (
{
token,
chain, // SOLANA
commitmentId,
amount,
}) => {
// See API Integration -> Fetch Bridge Configs for the full implementation
const configs = await getBridgeConfigs()
const chainConfig = configs[chain]
const solanaWalletAddress = wallet.publicKey.toString()
const tokenConfig = chainConfig.tokens[token]
const tokenAmount = +amount * 10 ** tokenConfig.decimals
const tokenAddress = tokenConfig.address
const bridgeContractAddress = chainConfig.contractAddress
const connection = new Connection(chainConfig.rpc)
const options = AnchorProvider.defaultOptions()
const anchorProvider = new AnchorProvider(connection, wallet, options)
const depositor = new PublicKey(solanaWalletAddress)
const bridgeKey = await getSolanaBridgeKey(bridgeContractAddress)
const poolKey = await getSolanaPoolKey(bridgeContractAddress, tokenAddress)
const poolAuthority = await getSolanaPoolAuthority(bridgeContractAddress, tokenAddress)
const poolAccount = await getSolanaPoolTokenAccount(bridgeContractAddress, tokenAddress)
const depositorAccount = await getSolanaDepositorAccount(solanaWalletAddress, tokenAddress)
const program = new Program(IDL, bridgeContractAddress, anchorProvider)
const accounts = {
bridge: bridgeKey,
pool: poolKey,
poolAuthority,
poolAccount: poolAccount,
depositor,
mint: new PublicKey(tokenAddress),
depositorAccount: depositorAccount,
}
const depositTxHash = await program.methods['depositWithId'](new BN(tokenAmount), new BN(commitmentId, 'hex'))
.accounts(accounts)
.signers([])
.rpc()
await connection.getSignatureStatus(depositTxHash)
return depositTxHash
}
```
# Starknet Contracts
Source: https://docs.rhino.fi/contracts/starknet
This guide explains how to interact with the rhino.fi Bridge contract on Starknet.
### Setup
For this example we'll use `starknet.js` to interact with the contract, but you can replace it with any Starknet library of your choice.
Make sure to install it first:
```bash yarn theme={null}
yarn add starknet
```
```bash npm theme={null}
npm install starknet
```
## Interacting with the Contract
To execute a bridge transaction, call the `deposit_with_id` function as follows:
The `commitmentId` required by the contract corresponds to the `quoteId` returned by the API.
To obtain the `commitmentId`, first request a quote from the API, then commit it. The response will include the `quoteId`, which should be used as the `commitmentId` in the contract call.
See [the API bridge example](/api-integration/bridge#6-full-example) for details.
```javascript starknet.js theme={null}
import {CallData, cairo} from 'starknet'
export const callStarknetBridgeContract = async (
{
token,
commitmentId,
amount,
token,
}) => {
// See API Integration -> Fetch Bridge Configs for the full implementation
const configs = await getBridgeConfigs()
const chainConfig = configs[chain]
// Replace with your wallet implementation
const provider = new RpcProvider({ nodeUrl: chainConfig.rpc });
const account = new Account(provider, STARKNET_WALLET_ADDRESS, STARKNET_PRIVATE_KEY);
const tokenConfig = chainConfig.tokens[token]
const amountWithDecimals = +amount * 10 ** tokenConfig.decimals
const tokenAddress = tokenConfig.address
const bridgeContractAddress = chainConfig.contractAddress
const depositAmount = cairo.uint256(amountWithDecimals)
const id = `0x${commitmentId}`
const multiCall = await account.execute([
{
contractAddress: tokenAddress,
entrypoint: 'approve',
calldata: CallData.compile({
spender: bridgeContractAddress,
amount: depositAmount,
}),
},
{
contractAddress: bridgeContractAddress,
entrypoint: 'deposit_with_id',
calldata: CallData.compile({
token: tokenAddress,
amount: depositAmount,
commitment_id: id,
}),
},
])
const result = await provider.waitForTransaction(multiCall.transaction_hash)
if ('revert_reason' in result && 'execution_status' in result) {
if (result.execution_status === 'REVERTED') {
throw new Error(result.revert_reason)
}
}
return {transactionHash: multiCall.transaction_hash}
}
```
# TON Contracts
Source: https://docs.rhino.fi/contracts/ton
This guide explains how to interact with the rhino.fi Bridge contract on TON.
### Setup
For this example we'll use `@ton/ton` and `@ton/crypto` to interact with the contract, but you can replace it with any TON library of your choice.
Make sure to install it first:
```bash yarn theme={null}
yarn add @ton/ton @ton/crypto
```
```bash npm theme={null}
npm install @ton/ton @ton/crypto
```
## Interacting with the Contract
To execute a bridge transaction, send a transfer with the following payload:
The `commitmentId` required by the contract corresponds to the `quoteId` returned by the API.
To obtain the `commitmentId`, first request a quote from the API, then commit it. The response will include the `quoteId`, which should be used as the `commitmentId` in the contract call.
See [the API bridge example](/api-integration/bridge#6-full-example) for details.
```javascript @ton/ton theme={null}
import {beginCell, toNano, Address, TonClient, internal, WalletContractV4} from '@ton/ton'
import {mnemonicToPrivateKey} from '@ton/crypto';
import getBridgeConfigs from './getBridgeConfigs'
const getJettonWalletAddress = async (tonWalletAddress, jettonMasterAddress, publicProvider) => {
const jettonMaster = publicProvider.open(JettonMaster.create(jettonMasterAddress))
return jettonMaster.getWalletAddress(tonWalletAddress)
}
export const callTonBridgeContract = async (
{
commitmentId,
amount,
token,
tonWalletAddress,
}) => {
// See API Integration -> Fetch Bridge Configs for the full implementation
const configs = await getBridgeConfigs()
const chainConfig = configs[chain]
// Replace with your wallet implementation
const tonClient = new TonClient({
endpoint: chainConfig.rpc,
})
const key = await mnemonicToPrivateKey(TON_MNEMONIC);
const workchain = 0;
const wallet = WalletContractV4.create({
workchain,
publicKey: keyPair.publicKey,
})
const walletProvider = tonClient.open(wallet)
const tokenConfig = chainConfig.tokens[token]
const amountWithDecimals = +amount * 10 ** tokenConfig.decimals
const tokenAddress = tokenConfig.address
const bridgeContractAddress = chainConfig.contractAddress
const jettonWalletContract = await getJettonWalletAddress(
Address.parse(tonWalletAddress),
Address.parse(tokenAddress),
tonClient
)
const sourceAddress = Address.parse(tonWalletAddress)
const destinationAddress = Address.parse(bridgeContractAddress)
const forwardPayload = beginCell()
.storeUint(BigInt(`0x${commitmentId}`), 96)
.endCell()
const forwardAmount = 0.02
const body = beginCell()
.storeUint(0xf8a7ea5, 32) // opcode for jetton transfer
.storeUint(0, 64) // query id
.storeCoins(BigInt(amountWithDecimals)) // jetton amount, amount * 10^9
.storeAddress(destinationAddress) // TON wallet destination address
.storeAddress(sourceAddress) // response excess destination
.storeBit(0) // no custom payload
.storeCoins(toNano(forwardAmount)) // forward amount (if >0, will send notification message)
.storeBit(1) // we store forwardPayload as a reference
.storeRef(forwardPayload)
.endCell()
const seqno: number = await walletProvider.getSeqno();
return await walletProvider.sendTransfer({
seqno,
secretKey: key.secretKey,
timeout: Math.floor(Date.now() / 1000) + 360,
messages: [
internal({
to: jettonWalletContract.toString(),
value: toNano(0.1),
body: body,
}),
],
})
}
```
# TRON Contracts
Source: https://docs.rhino.fi/contracts/tron
This guide explains how to interact with the rhino.fi Bridge contract on Tron.
## ABI
```json abi.js theme={null}
[
{
inputs: [
{
internalType: "address",
name: "token",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
{
internalType: "uint256",
name: "commitmentId",
type: "uint256",
},
],
name: "depositWithId",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "depositNative",
outputs: [],
stateMutability: "payable",
type: "function",
},
];
```
### Setup
For this example we'll use `tronweb` to interact with the contract, but you can replace it with any Tron library of your choice.
Make sure to install it first:
```bash yarn theme={null}
yarn add tronweb
```
```bash npm theme={null}
npm install tronweb
```
## Interacting with the Contract
To execute a bridge transaction, call the `depositWithId` (TRC20) function as follows:
The `commitmentId` required by the contract corresponds to the `quoteId` returned by the API.
To obtain the `commitmentId`, first request a quote from the API, then commit it. The response will include the `quoteId`, which should be used as the `commitmentId` in the contract call.
See [the API bridge example](/api-integration/bridge#6-full-example) for details.
```javascript tronweb theme={null}
import { TronWeb, providers } from 'tronweb'
import getBridgeConfigs from './getBridgeConfigs'
// You can find the bridgeABI above in the ABI section
import bridgeAbi from './bridgeAbi'
export const callTronBridgeContract = async (
{
token,
amount,
commitmentId,
tronWalletAddress
}) => {
// See API Integration -> Fetch Bridge Configs for the full implementation
const configs = await getBridgeConfigs()
const chainConfig = configs[chain]
// Replace with your wallet implementation
const tronWeb = new TronWeb({
fullHost: new providers.HttpProvider('https://api.trongrid.io'),
// headers: { "TRON-PRO-API-KEY": 'your api key' }, -- if using a custom RPC
privateKey: TRON_PRIVATE_KEY
})
const tokenConfig = chainConfig.tokens[token]
const amountWithDecimals = +amount * 10 ** tokenConfig.decimals
const tokenAddress = tokenConfig.address
const bridgeContractAddress = chainConfig.contractAddress
const {
abi
} = await tronWeb.trx.getContract(tokenAddress);
const trc20Contract = tronWeb.contract(abi.entrys, tokenAddress);
const allowance = await trc20Contract.allowance(tronWalletAddress, bridgeContractAddress).call();
const formattedAllowance = parseInt(allowance, 10) / 10 ** tokenConfig.decimals;
if (formattedAllowance < parseFloat(amount)) {
await trc20Contract.approve(bridgeContractAddress, amountWithDecimals).send()
}
const contract = tronWeb.contract(bridgeAbi, bridgeContractAddress)
const tx = await contract.depositWithId(tokenAddress, amountWithDecimals, `0x${commitmentId}`).send({shouldPollResponse: true})
return tx
}
```
# Contract addresses
Source: https://docs.rhino.fi/general/contract-addresses
List of rhino.fi smart contract addresses deployed across our supported chains
| Chain | Contract address |
| --------------- | ------------------------------------------------------------------ |
| Arbitrum One | 0x10417734001162Ea139e8b044DFe28DbB8B28ad0 |
| Avalanche | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Base | 0x2f59e9086ec8130e21bd052065a9e6b2497bb102 |
| BNB Smart Chain | 0xb80a582fa430645a043bb4f6135321ee01005fef |
| Celo | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Ethereum | 0xbca3039a18c0d2f2f84ba8a028c67290bc045afa |
| Gnosis | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| HyperEVM | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Ink | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Kaia | 0x92650ddc467ed628868bdba23cf81eafaab60175 |
| Katana | 0x04317f0e4795b1e1bab333234153fa10aaac79e9 |
| Linea | 0xcf68a2721394dcf5dcf66f6265c1819720f24528 |
| Mantle | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Polygon | 0xBA4EEE20F434bC3908A0B18DA496348657133A7E |
| opBNB | 0x2b4553122d960ca98075028d68735cc6b15deeb5 |
| Optimism | 0x0bca65bf4b4c8803d2f0b49353ed57caaf3d66dc |
| Plasma | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Solana | FCW1uBM3pZ7fQWvEL9sxTe4fNiH41bu9DWX4ErTZ6aMq |
| Stable | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Starknet | 0x0259fec57cd26d27385cd8948d3693bbf26bed68ad54d7bdd1fdb901774ff0e8 |
| Stellar | GBKLV7CN24FVACNKNXW2XBG6AI5DZVY7EUTJA5VFBV2RYTIRXEVVAWXL |
| Tempo | 0x5e023c31e1d3dcd08a1b3e8c96f6ef8aa8fcacd1 |
| Ton | EQAj3SoOk4MPzjn816Crw1b4RxW79fB\_Z549tyCd9HIQV6b7 |
| Tron | TT3kgJohTQJNKDUWwTxtRDMHNNWNvNG3i4 |
# Status page
Source: https://docs.rhino.fi/general/status-page
To see up-to-date information about the health of the chains that we support and key Rhino.fi product status information please visit the status page:
[https://status.rhino.fi](https://status.rhino.fi/posts/dashboard)
# API Keys
Source: https://docs.rhino.fi/get-started/api-keys
To use the API or SDK, you need to obtain an API key. API keys can either be secret or public.
### Public API keys
Public API keys can be safely exposed in a browser environment as they don't grant access to any sensitive data. So for example, if your application runs client-side in a browser, you should use a public API key.
Public API keys can't be used to access the bridge history of your app as that would leak the entire history to the public.
### Secret API keys
Secret API keys work just the same as public ones with the difference that they can be used to access the entire bridge history of your app. You should only use secret API keys in code that is not run client side.
You can create a project and manage API keys there.
# Extensions
Source: https://docs.rhino.fi/get-started/extensions
Extensions are premium features that expand the capabilities of the Rhino Stablecoin Activation Stack.
## Extensions
Extensions are premium features that expand the capabilities of the Rhino Stablecoin Activation Stack. They are activated at the account level — your account representative enables them when you sign up or upgrades your account later.
Extensions are designed for teams with specific requirements: custom compliance workflows, programmable settlement outcomes, custom chain coverage, or advanced fee management.
***
## Available extensions
| Extension | Tier | Description |
| ------------------------------------------------------------------------------------ | -------------- | ----------------------------------------------------------------------------------------------------------- |
| [Enhanced Compliance & Risk Management](/get-started/extensions/enhanced-compliance) | Free + Premium | Custom AML screening rules and real-time AML event notifications |
| [Automated Onchain Actions](/get-started/extensions/automated-onchain-actions) | Premium | Execute a contract call automatically when settled funds arrive on the destination chain |
| [1:1 Stablecoin ](/get-started/extensions/stablecoin-swaps)Settlement | Premium | USDT↔USDC conversion at a guaranteed 1:1 rate, no slippage |
| [Advanced Fee & Limit Management](/get-started/extensions/advanced-fee-management) | Free + Premium | Sponsor fees for end users, add markup, end-of-month invoicing |
| [Custom Chain & Token Support](/get-started/extensions/custom-chain-token-support) | Premium | Expand supported chains or tokens beyond the standard set |
| [Tron, Solana and Bitcoin Access](/get-started/extensions/tron-access) | Premium | Enable transactions involving Tron, Solana and Bitcoin which have higher onchain costs associated with them |
***
## Enabling extensions
Extensions are not self-serve. To enable any extension, contact your account representative.
All extensions are governed by your master service agreement. Some extensions have additional contractual requirements (e.g. Enhanced Compliance has a data processing addendum).
***
## Related
* [What is Rhino?](/get-started/what-is-rhino) — overview of the full product
* [Fees & Limits](/get-started/fees-and-limits) — how fees work, including fee extensions
# Advanced Fee & Limit Management
Source: https://docs.rhino.fi/get-started/extensions/advanced-fee-management
View fee and limit configurations across the Stablecoin Activation Stack with the ability to change fee structures to customer and route levels, as well as levying a client surcharge.
## Advanced Fee & Limit Management
**Extension tier:** Free + Premium
The Advanced Fee & Limit Management extension gives you control over how fees are presented and collected in your integration. It is designed for clients that want to either remove fee friction from their end users entirely, or use the bridge integration as a revenue source.
It is accessible within the Rhino.fi Console to provide quick and easy access to view limit information and set fee policies.
***
### Client Surcharge
Subscription clients may choose to add on an additional charge that is passed directly through to their customers for their Rhino.fi activity. This is called a 'client surcharge' and is able to be a fixed \$ amount and/or a fixed % amount. This amount is rebated to clients as part of the end of month invoicing.
For example:
* ClientA may add a client surcharge of 1%
* ClientB may add a client surcharge of \$0.50
* ClientC may add a client surcharge of 0.25% + \$0.30
For clients without the Advanced Fee and Limit Management extension, this surcharge is applied across all customers and all routes.
However with the Advanced Fee and Limit Management extension, clients are able to apply a 'Default Policy' to one or more projects which applied to all customers and all routes, but then also set 'Fee Policies' which override the default policy when specified conditions are met.
These fee policies can be set at the customer lever or route level.
* **Customer Fee Policies:** Clients are able to specify one or more customers (via a customerID or Rhino.fi Smart Deposit Addresses) that the client surcharge should be applied to. This enables fee structures where subsets of customers are charged different rates e.g 0.1% for VIP customers vs 0.4% for all other customers.
* **Route Fee Policies:** Clients are able to specify one or more chains that fees should be applied to. It is also possible to specify whether this is as the source and/or destination chain. This enables fee structures where certain chains are charged at different rates e.g for Solana to Base there is a \$1 fee, and for transaction involving Ethereum (whether the source or destination chain) the fee should be \$2. It is also possible to specify one or more tokens - as either/or the source or destination token, and set different rates for them. Route Fee Policies can also be a combination of chains and tokens.
This comprehensive configurability allows for clients to set bespoke fee policies which meet their individual needs.
We will continue to build to build out additional fee policy structures and configurations so please reach out if you have additional requirements not facilitated by the above.
### Limit Information
Within Advanced Fee and Limit Management there is information about Smart Deposit Address limits as well as, where the extension is enabled, 1:1 Stablecoin Swap Limits. This ensures that clients have access to this information directly.
Smart Deposit Address (SDA) Limits shows the number of generated Smart Deposit Addresses, in total and over various time periods, and how the tracks towards the client's total SDA limit. Information on the minimum and maximum deposit per SDA, per project is also visible.
1:1 Limits shows a comprehensive view of which projects have 1:1 enabled on, what the 30 day volume vs the limit is including the daily volume, rolling 30d total and projected volume to help clients best manage their activity. The max USD per recipient limit is also included.
For more information on Rhino.fi limits generally [https://docs.rhino.fi/get-started/](https://docs.rhino.fi/get-started/introduction)limits
***
## Related
* [Fees & Limits](/get-started/fees-and-limits) — general fee documentation
* [Smart Deposit Addresses — fee handling](/api-integration/smart-deposits)
* [Extensions overview](/get-started/extensions)
# Automated Onchain Actions
Source: https://docs.rhino.fi/get-started/extensions/automated-onchain-actions
Automated Onchain Actions let you specify a contract call that executes automatically when settled funds arrive on the destination chain. Configure once; Rhino handles approval, encoding, execution, and error handling.
Automated Onchain Actions let you specify a smart contract call that executes automatically the moment settled funds arrive on the destination chain. You define the target contract, the function to call, and any custom parameters — Rhino handles everything else.
This is a premium Extension. Your account must have Automated Onchain Actions enabled before the `postBridgeData` field will be accepted on quote or SDA generation requests. Contact your account representative to enable it.
#### The result: a single deposit produces a fully activated on-chain outcome — no additional steps from your user or your system.
***
## How It Works
Enabling an Automated Onchain Action requires no additional API calls or integration steps. You simply include a `postBridgeData` field in your bridge quote or Smart Deposit Address generation request. We take care of the contract approval, encoding, execution, and error handling on the destination chain.
The `postBridgeData` field accepts:
* **Tag** under `_tag` that specifies the Automated Onchain Action you want to execute
* **Optional custom parameters** to specify a custom target like vault, user ID or invoice ID
We configure everything else: tokens and amounts, contract specific inputs, token approval, amount injection, gas estimation, and execution sequencing.
***
## Use Cases
Automated Onchain Actions apply wherever you need a defined on-chain outcome rather than just a token credit.
Common patterns:
**Yield and protocol integrations**
Deposit bridged funds directly into a vault, lending pool, or staking contract. The user's recipient address receives the protocol's output token (e.g. vault shares, staked positions).
**Custodial and off-chain crediting**
For custodial platforms and exchanges, Automated Onchain Actions can trigger a contract that signals an off-chain system like crediting a user's account, updating a balance, or notifying a clearing layer, without requiring the user to interact with your chain/system directly.
**Payments and holding accounts**
Trigger a contract that routes funds to a payment processor, holding account, or escrow. Useful for on-chain invoicing, subscription flows, or multi-party settlement.
In all cases, the integration surface is the same: one additional field on a request you're already making.
***
## Integration
### With a Bridge Quote
Add `postBridgeData` to your `POST /bridge/quote/bridge-swap/user` request body:
```json theme={null}
{
"token": "USDC",
"chainIn": "ARBITRUM",
"chainOut": "BASE",
"amount": "100.5",
"mode": "pay",
"depositor": "0x0",
"recipient": "0x0",
"postBridgeData": {
"_tag": "postBridgeActionTag",
"userId": "1"
}
}
```
The destination token and amount is injected automatically into the matching function argument. You only need to specify the unique tag and any custom parameters like userId in the example above.
### With a Smart Deposit Address
Add `postBridgeData` to your `POST /sda/deposit-addresses` request. Every deposit to that address will trigger the same action automatically — no per-deposit configuration needed.
```json theme={null}
{
"depositChains": ["ARBITRUM", "BASE", "ETHEREUM"],
"destinationChain": "BASE",
"destinationAddress": "0x0",
"postBridgeData": {
"_tag": "postBridgeActionTag",
"userId": "1"
}
}
```
This is the recommended pattern for onboarding flows. Generate an SDA per user with their recipient address baked in, and every inbound deposit routes directly to the target contract outcome.
***
## The `postBridgeData` Field
| Field | Type | Required | Description |
| ---------------------- | -------------------- | -------- | ---------------------------------------------------------- |
| `_tag` | `string` | ✓ | Specifies the Automated Onchain Action configuration. |
| optional custom params | `string` / `integer` | - | Specifies a custom target like a user, merchant or payment |
***
## Example — Bridge into a Morpho Vault
A user holds USDT on Arbitrum and wants to enter a USDC yield vault on Base.
**Without Automated Onchain Actions**, they would need to: swap USDT → USDC, bridge to Base, approve the vault, and call `deposit()` themselves — four to five steps across two chains.
**With Automated Onchain Actions + SDA**, you generate one deposit address per user. They send USDT on Arbitrum. We swap, bridge, and execute the vault deposit. Their wallet receives vault shares on Base.
```bash theme={null}
curl -X POST https://api.rhino.fi/sda/deposit-addresses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"depositChains": ["ARBITRUM", "ETHEREUM", "BASE"],
"destinationChain": "BASE",
"destinationAddress": "0x0",
"postBridgeData": {
"_tag": "Morpho",
},
"tokenOut": "USDC"
}
}'
```
Just hand the user the returned `depositAddress` for any of the supported source chains. They send funds. The vault deposit happens automatically.
In the example above, the user can send USDC or USDT on Arbitrum, Ethereum and Base (yes, same chain works as well). We process any supported token and your user ends up with Morpho USDC yield token on Base with on the specified destination address.
***
## What Happens If the Action Fails
If the contract call reverts on the destination chain, funds are not lost. Depending on your account configuration, we will either retry (for transient failures), credit the destination token directly to the `destinationAddress`, or notify you via webhook and await instructions.
Test your contract integration before going live. Contact your account representative to enable testnet access.
***
## Limitations
* **ERC-20 type tokens only** — native gas tokens (ETH, SOL, etc.) cannot be the input to an Automated Onchain Action yet
* **One action per bridge** — chaining multiple contract calls in sequence is not currently supported
* **EVM destination chains only** — non-EVM destinations (i.e. Solana) are on the roadmap
* **Automated Onchain Actions must be enabled on your account** — the `postBridgeData` field is accepted only on accounts with this Extension active. Contact your account representative to enable it.
***
## Next Steps
* [Smart Deposit Addresses](/api-integration/smart-deposits) — Full SDA documentation
* [API Reference — User Quote](/api-reference/bridge/quote/generate-a-bridgebridge+swap-quote#body-post-bridge-data) — `postBridgeData` field schema
* [API Reference — Create Deposit Address](/api-reference/sda/depositaddresses/generate-a-new-smart-deposit-address#body-post-bridge-data) — `postBridgeData` field schema
# Custom Chain & Token Support
Source: https://docs.rhino.fi/get-started/extensions/custom-chain-token-support
Expand supported chains or tokens beyond the standard set.
## Custom Chain & Token Support
**Extension tier:** Premium
Rhino supports 20+ chains and a standard set of stablecoins out of the box. The Custom Chain & Token Support extension is for clients whose users hold assets on chains or in tokens not currently in the standard set.
***
## What can be added
* **New EVM chains** — any EVM-compatible chain where Rhino can deploy liquidity and operate a solver
* **New non-EVM chains** — evaluated on a case-by-case basis depending on chain tooling and liquidity availability
* **Additional tokens** — stablecoins or other fungible tokens not currently listed in the bridge config
***
## How to request
Custom chain or token additions are not self-serve. Rhino's engineering team evaluates each request based on:
* Liquidity depth on the target chain
* Security and smart contract auditability
* Operational overhead and monitoring requirements
* Client volume commitment
To request a new chain or token, contact your account representative with details of the chain/token and expected volume.
***
## Timelines
Standard evaluation and onboarding for a new EVM chain takes 1–2 days from agreement. Non-EVM chains vary depending on tooling and liquidity requirements.
Token additions for established stablecoins on already-supported chains are typically faster.
***
## Related
* [Supported Chains](/get-started/supported-chains)
* [Extensions overview](/get-started/extensions)
# Enhanced Compliance & Risk Management
Source: https://docs.rhino.fi/get-started/extensions/enhanced-compliance
Custom AML screening rules and real-time AML event notifications for clients with stricter compliance requirements.
## Enhanced Compliance & Risk Management
**Extension tier:** Free + Premium
All Rhino accounts include baseline KYT/AML screening: every deposit is screened before crediting, and any deposit that fails compliance checks is blocked. Clients never receive funds that fail Rhino's standard compliance checks.
The Enhanced Compliance & Risk Management extension provides additional controls for clients with stricter compliance requirements.
***
## What's included
### Custom AML check rules
Integrate your own compliance provider on top of Rhino's existing screening. You provide rules via API and can use any third-party service you prefer — for example, [Chainalysis](https://www.chainalysis.com/), [Elliptic](https://www.elliptic.co/), or your own in-house solution.
This allows you to apply custom risk score thresholds, jurisdiction-based blocking rules, or additional data lookups against third-party sanctions lists tailored to your regulatory environment or risk appetite.
Contact your account representative to discuss configuration options.
### Real-time AML notifications
When a deposit triggers a compliance flag, a webhook event is dispatched in real time. Your system can react immediately — holding the transaction, alerting your compliance team, or triggering a review workflow — without polling the status endpoint.
AML notification events appear in the standard [Webhooks](/webhook/events-list) event stream. The specific event types are documented on the [Events List](/webhook/events-list) page.
### Slack notifications (optional add-on)
Rhino can enable Slack notifications for your team when any of your end user deposits fail KYT screening. This provides immediate visibility into compliance events without requiring webhook integration. Contact your account representative to set this up.
***
## Baseline compliance (all accounts)
Every account includes:
* 100% KYT screening on all inbound deposits via Smart Deposit Addresses
* Automatic blocking of deposits from flagged addresses
* Compliance event logging accessible via the Rhino Console and history API
***
## Related
* [Webhooks — Events List](/webhook/events-list)
* [Smart Deposit Addresses](/get-started/sda)
* [Extensions overview](/get-started/extensions)
# 1:1 Stablecoin Settlement
Source: https://docs.rhino.fi/get-started/extensions/stablecoin-swaps
USDT↔USDC conversion at a guaranteed 1:1 rate, no slippage, no market exposure.
## 1:1 Stablecoin Settlement
**Extension tier:** Premium
When the 1:1 Stablecoin Settlement extension is enabled, cross-stable conversions between USDT and USDC are executed at a guaranteed 1:1 rate. There is no slippage, no market exposure, and no variance between the deposited amount and the settled amount (net of the standard bridge fee).
***
## The problem this solves
Without this extension, a cross-stable swap (e.g. a user deposits USDT and the destination expects USDC) is routed through a market. The settled amount depends on the USDT/USDC market price at the time of execution. In thin conditions, this can deviate from 1:1 by a meaningful amount.
For applications that display a quoted settlement amount to end users — onboarding flows, payment screens, balance estimations — any variance creates a UX problem and potential customer support burden.
***
## How it works
Rhino maintains its own USDT and USDC reserves on supported chains. When a cross-stable deposit arrives, Rhino credits the destination from its reserves at exactly 1:1, then rebalances its own positions off the critical path.
The user's experience is: deposited USDT amount equals settled USDC amount (minus fee).
This applies to:
* USDT → USDC
* USDC → USDT
Both directions, across all supported chains where USDT and USDC are both available.
Rhino.fi provides 1:1 support when the rate between USDT and USDC is up to 3bps (the Rhino.fi guardrail), it is then possible for client's who have the Advanced Fee and Limit Management extension to set an additional Client guardrail and continue providing a 1:1 experience for customers between the Rhino.fi and Client guardrail. Thereafter the activity will return to market rate.
***
## Integration
No API change is required. If your account has this extension enabled, any SDA or bridge request that specifies a cross-stable `tokenOut` will automatically use 1:1 pricing.
To confirm this extension is active on your account, check the [Rhino Console](/get-started/rhino-console) under Configuration, or contact your account representative.
***
## Limits
Deposit limits for 1:1 swap routes follow the same limits as standard SDA deposits.
For very large individual transactions (above \$1M USD equivalent), contact your account representative to confirm available liquidity.
Volume based limits are applied to protect your app from abuse. These limits are customizable on a case by case basis.
* Account level limit - the total 1:1 swap volume limit over all transactions within a specified time frame (expressed in days)
* Recipient level limit - the total 1:1 swap volume limit per specific recipient
Once limits are hit, the service falls back to the market rate. If a recipient hits their limit, only their transactions will fall back to the market rate.
***
## Related
* [Smart Deposit Addresses](/get-started/sda)
* [Fees & Limits](/get-started/fees-and-limits)
* [Extensions overview](/get-started/extensions)
# Tron, Solana and Bitcoin Access
Source: https://docs.rhino.fi/get-started/extensions/tron-access
Enable transactions on Tron, Solana and Bitcoin where there are higher onchain costs
**Extension tier:** Premium
Tron, Solana and Bitcoin all have higher onchain transaction costs and as such access to these chains must be enabled through this extension.
***
## What's enabled
* **Deposits to a Tron Smart Deposit Address** — Smart Deposit Addresses can be generated on Tron. Users can send [supported tokens](https://docs.rhino.fi/get-started/supported-chains) to a Rhino.fi SDA on Tron, and funds are settled to the configured destination chain. For all deposits to a Tron Smart Deposit Address there is a per-transaction \$1.40 cost.
* **Tron as a destination** — Bridges can settle to a Tron address. There is a per-transaction \$1.40 cost for this.
* **Solana as a destination** — Bridges can settle to a Solana address. There is a per-transaction \$0.50 cost for this.
* **Deposits to a Bitcoin Smart Deposit Address** — Smart Deposit Addresses can be generated on Bitcoin. Users can send [supported tokens](https://docs.rhino.fi/get-started/supported-chains) to a Rhino.fi SDA on Bitcoin, and funds are settled to the configured destination chain. For all deposits to a Bitcoin Smart Deposit Address there is a per-transaction \$1 fee which can be sponsored or paid by the customer.
***
## Why this is a separate extension
These chains requires specific infrastructure, compliance controls and operational processes that differ from EVM chains and have a higher per-transaction cost.
To enable this extension, please speak with your Rhino.fi Account Manager.
***
## Integration
Once Tron, Solana and Bitcoin Access is enabled on your account, Tron and Solana will appear in the bridge config (`bridgeConfigs`) as an available chain with `enabledDepositAddress: true`.
No code changes are required beyond confirming that these chains are listed.
For SDA generation on Tron and Bitcoin, use `"TRON"` or `"BITCOIN"` in the `depositChains` array.
Generating a Tron or Solana or Bitcoin SDA requires a separate call. I.e. you can specify many EVMs to generate SDAs across all EVMs. Then generate a Tron (or Solana or Bitcoin) SDA with a separate call.
For Tron as a destination, use `"TRON"` as the `destinationChain`. For Solana, use `"SOLANA"`.
***
## SDK support
Chain adapter documentation is available at [Chain Adapters](https://docs.rhino.fi/sdk/chain-adapters/overview)
***
## Related
* [Smart Deposit Addresses](/get-started/sda)
* [Supported Chains](/get-started/supported-chains)
* [Extensions overview](/get-started/extensions)
# Fees
Source: https://docs.rhino.fi/get-started/fees-and-limits
Learn how Rhino.fi charges fees and how the Advanced Fee & Limit Management extension gives you control and comprehensive configurability.
## How Rhino.fi fees work
Rhino.fi offers a pricing structure which has the flexibility to suit clients throughout their growth journey and provides full access to the Rhino.fi Stablecoin Activation Stack with a number of optional extensions to supercharge your Rhino.fi use.
For more information please visit: [https://rhino.fi/pricing](https://rhino.fi/pricing)
### Subscription
The Subscription tiers are ideal for growing with companies as they scale up.
* The 'Starter' tier is ideal for early teams who are within beta testing and low volumes.
* The 'Growth' tier allows for predictable spend at scale for companies growing their offering.
* The 'Scale' tier is a match for scaling teams with stablecoins as core to their offering.
* The 'Enterprise' tier is for companies where stablecoins are a critical flow and has tailored economics and coverage for the highest volumes and values.
Each tier has a fixed monthly cost with the ability to continue into overages for an uninterrupted service.
Clients have access to the Rhino.fi Stablecoin Activation Stack including; Smart Deposit Addresses, Console access, enterprise-grade transaction volume and value support, embedded KTY and AML, multi-chain support across 30+ chains and more!
It is also possible to enable a wide variety of extensions, including;
* The Tron, Solana and Bitcoin extension provides access to these chains across bridging and Rhino.fi Smart Deposit Addresses.
* The Advanced Fee and Limit Management extension which provides comprehensive control over fees and limit visibility.
* 1:1 Stablecoin Swaps which enables zero slippage USDC/USDT conversions
* Enhanced Compliance functionality with alerting and custom configuration on top of Rhino.fi robust set up.
and more.
The onchain cost for activity (moving funds from Smart Deposit Addresses and settlement through the Rhino.fi Bridge) are borne by Rhino.fi and clients are charged their fixed monthly fee. This provides cost predictability to clients and removes the volatility of onchain costs. The underlying fee-related information is still transparently provided within generated quotes for bridge+swap within the `POST/quote/bridge-swap/user`endpoint, and when using Rhino.fi Smart Deposit Addresses, the fee information can be obtained prior to the transaction by providing `isSda = true`within the request body. Rhino.fi will honor the quoted fee until the quote expires — there are no surprises at settlement (the timestamp of the expiry is provides within the `expiresAt`param within the response).
Further information about specific onchain costs for Tron, Solana and Bitcoin can be found here: [https://learn.rhino.fi/articles/7749894324-tron-and-solana-onchain-gas-costs](https://learn.rhino.fi/articles/7749894324-tron-and-solana-onchain-gas-costs)
It is possible for clients to add on an additional charge that is passed directly through to their customers for their Rhino.fi activity. This is called a 'client surcharge' and is able to be a fixed \$ amount and/or a fixed % amount. This amount is deducted from the end of month invoice amount.
*For example: ClientB may wish to levy a \$1 charge to their customers for any Rhino.fi activity. At the end of the month the total Client Surcharge collected by Rhino.fi on behalf of clientB was \$1000. The end of month invoice for clientB is therefore their fixed subscription cost minus this \$1000 amount.*
To configure the client surcharge per specific customers or specific routes, or to make changes to this once set, clients must enable the Advanced Fee and Limit Management extension: [https://docs.rhino.fi/get-started/extensions/advanced-fee-management](https://docs.rhino.fi/get-started/extensions/advanced-fee-management)
To discuss which approach is the most suitable for your company, please reach out.
***
## Related
* [Advanced Fee & Limit Management](/get-started/extensions/advanced-fee-management) — Extension detail page
* [Bridge quote endpoint](/api-reference/bridge/quote/generate-a-bridgebridge+swap-quote) — API reference for fee quotes
* [Smart Deposit Addresses](/get-started/sda) — How fees apply to SDA deposits
# Get Started
Source: https://docs.rhino.fi/get-started/introduction
Rhino.fi is stablecoin deposits infrastructure built for teams that need reliable, enterprise-grade settlement at scale. This documentation covers everything you need to integrate the Rhino Stablecoin Activation Stack — from generating your first deposit address to configuring advanced fee management and automated onchain actions.
## What are you looking to do?
Generate a single deposit address that accepts stablecoins from any supported chain. Funds are settled to your destination chain automatically — no bridge UI, no user complexity.
Accept stablecoin deposits and settle them on 20+ chains with deterministic outcomes, MEV protection, and configurable speed tiers.
Get running in minutes using the JavaScript/TypeScript SDK.
Integrate directly via REST API in any language.
Full endpoint reference with request/response schemas.
# Limits
Source: https://docs.rhino.fi/get-started/limits
Learn how Rhino.fi charges fees and how the Advanced Fee & Limit Management extension gives you control and comprehensive configurability.
## How Rhino.fi limits work
Rhino.fi supports enteprise-grade volumes and value and can work with you to ensure that the limits on your account meet your expected transaction activity. With the [**Advanced Fee & Limit Management**](https://docs.rhino.fi/get-started/extensions/advanced-fee-management) extension, these limits are viewable, and we are always happy to discuss extending limits to ensure uninterrupted service to meet your transaction volume and value needs.
There are a number of different limits which can be applicable to Rhino.fi activity:
### Smart Deposit Address Limits
* **Generated SDAs:** Clients will have maximum number of Smart Deposit Addresses (SDAs) which can be created as per their PAYG or Subscription tier. The number of generated SDAs vs the maximum allowance is available within the response of the `GET/deposit-addresses/rate-limit-status` endpoint and will be available in the Usage page in the Console.
* **Generation Windows:** There may also be addition limits which specify the number of SDAs that can be generated over a specific time period e.g over a hour or day. Where this is applicable, these details can be found within the response of the `GET/deposit-addresses/rate-limit-status` endpoint.
* **Min and Max Deposit Size:** This is the minimum \$ amount and maximum \$ amount which can be received into an SDA and is set at a project level. This information can be found within the SDA limits section of Advanced Fee and Limit Management in the Console or by speaking with your Rhino.fi account manager.
### Bridge+Swap Limits
To avoid situations where a transaction value may fall below the onchain costs, Rhino.fi has a minimum deposit amount per token, per chain. For the majority of our enterprise clients and their end customers, this will be significantly below the value they wish to transact. If you have transaction volume which may fall close to or below this please reach out as we may be able to support per-client custom minimum limits.
Please note that activity below the set minimum amounts will not be processed.
Rhino.fi also has a maximum \$ amount per token, per chain which can be bridged / bridge+swapped. This is to ensure an enterprise-grade experience for clients with optimal settlement times. The limits for a specific token on a specific chain can be found within the response of the `GET/config/{chain}/{token}` endpoint however if you have specific large transaction value activity above this then please do reach out as we can also set per-client transaction limits exceeding this.
Please note that activity above the set maximum amounts will not be processed.
The response within the `POST/quote/bridge-swap/user` endpoint will inform if the transaction would fail due to limits being above/below the set values.
### 1:1 Limits
Deposit limits for 1:1 swap routes follow the same limits as standard SDA deposits.For very large individual transactions (above \$1M USD equivalent), contact your Rhino.fi account manager to confirm available liquidity. Volume based limits are applied to protect your app from abuse. These limits are customizable on a case by case basis.
* Account level limit - the total 1:1 swap volume limit over all transactions within a specified time frame (expressed in days)
* Recipient level limit - the total 1:1 swap volume limit per specific recipient
Once limits are hit, the service falls back to the market rate. If a recipient hits their limit, only their transactions will fall back to the market rate.
For more information of the 1:1 extension: [https://docs.rhino.fi/get-started/extensions/stablecoin-swaps](https://docs.rhino.fi/get-started/extensions/stablecoin-swaps)
For prospects testing the Rhino.fi Stablecoin Activation Stack and for clients wishing to temporarily have lower limits during integration testing, these limits can be lowered. Please reach out to your Rhino.fi account manager to discuss your limit needs.
# Rhino Console
Source: https://docs.rhino.fi/get-started/rhino-console
The Rhino Console is the web interface for managing your Rhino integration — transaction history, analytics, team settings, and configuration.
## Rhino Console
The Rhino Console is the web interface for managing your Rhino integration. It is available to all clients at [console.rhino.fi](https://console.rhino.fi).
***
## What the Console covers
| Section | Description |
| --------------------------------- | --------------------------------------------------------------------------------- |
| **Transaction history** | Full history of all bridges, SDA deposits, and settlement events for your account |
| **Analytics** | Volume, success rate, fee totals, and per-chain breakdown |
| **Team settings** | Manage team members and access levels |
| **API key management** | Create, rotate, and revoke API keys |
| **Configuration** (coming soon) | View account-level settings, enabled Extensions, and fee configuration |
| **Status & alerts** (coming soon) | Link to the Rhino status page; incident history |
***
## API keys
API keys are created and managed in the Console. Each key is scoped to your account. Two key types are available:
| Key type | Use |
| ---------------- | -------------------------------------------------------------------------------- |
| `SECRET_` prefix | Full access — query history, manage addresses, required for sensitive operations |
| Standard key | Quote generation, bridge execution, public reads |
Store secret keys securely. They cannot be retrieved after creation — only rotated.
***
## Access
The Console requires you to log in using a Google workspace account. You can invite teammates to contribute and manage your project on the Rhino Console.
***
## Related
* [API Keys](/get-started/api-keys) — how to generate and use keys programmatically
* [Authentication](/api-integration/authentication) — how to authenticate API requests with your keys
# Smart Deposit Addresses
Source: https://docs.rhino.fi/get-started/sda
Smart Deposit Addresses are the core deposit primitive in the Rhino.fi Stablecoin Activation Stack. Generate one address per user; Rhino.fi handles chain detection, routing, settlement, and compliance automatically.
See some live use cases here: [5 ways to use SDAs](https://rhino.fi/blog/5-ways-to-use-smart-deposit-addresses)
**Premium Extension: 1:1 Stablecoin Swaps**
When the 1:1 Stablecoin Swaps extension is enabled on your account, cross-stable conversions (USDT→USDC and USDC→USDT) are executed at a guaranteed 1:1 rate with no slippage and no market exposure. This removes the guesswork from mixed-stablecoin deposit flows.
[Learn more →](/get-started/extensions/stablecoin-swaps)
## The Flow
At a high level, the process works as follows:
1. **Generate a Smart Deposit Address (SDA)**: Call the API to create a new deposit address, linked to a destination chain and address or a target action.
We strongly recommend generating an SDA on all available EVM chains immediately to ensure that funds accidentally sent by customers to the wrong chain are automatically detected.
2. **User deposits**: Any wallet, CEX, or payment service can send supported tokens to the Smart Deposit Address. The user only performs a normal token transfer.
3. **Monitoring & orchestration**: The system continuously watches for incoming transfers. If a valid deposit arrives (correct token, within limits), it triggers the bridge and swap process.
4. **Cross-chain execution**: Funds are credited on the destination chain, or the requested Automated Onchain Action (e.g. swap, pool deposit) is performed.
5. **Reconciliation & sweeping**: After crediting is complete, addresses are swept periodically to consolidate balances and keep monitoring efficient.
From the client/user perspective: user sends → destination credited → job done.
## The Features
Key properties that make SDAs flexible:
* **Many-to-one**: Multiple SDAs can funnel to the same destination address.
* **Multi-chain source support**: SDAs can currently be generated on the top EVM chains like Ethereum, Arbitrum, Base, BSC, Polygon, Optimism, Kaia, Tron, and Solana.
Full list available under [Supported Chains → Smart Deposit Address](https://docs.rhino.fi/get-started/supported-chains#smart-deposit-addresses).
Generating a Tron (and Solana) SDA requires a separate call. I.e. you can specify all EVMs to generate SDAs across all EVMs. Then generate a Tron and Solana SDA with separate calls.
* **Webhooks**: Real-time push updates on deposit results.
* **Any-chain destination**: Funds can settle on any of the 20+ Rhino-supported chains, including non-EVMs (Starknet, Ton, Solana, etc.).
* **Stablecoin primitives**:
* Same-to-same (e.g. USDC → USDC)
* Cross-stable swap (e.g. USDC → USDT) — with the [1:1 Stablecoin Swaps](/get-started/extensions/stablecoin-swaps) extension, cross-stable conversions execute at a guaranteed 1:1 rate with no slippage
* **Native asset support:** Users can send ETH (and soon additional native assets) to the Rhino.fi Smart Deposit Addresses and either bridge as ETH to supported chains or have embedded swap and bridge functionality to receive it on the destination chain as a stablecoin.
* **Programmable actions**: Trigger Automated Onchain Actions like swaps, pool deposits, or contract interactions.
* **Metadata support**: Attach arbitrary data (e.g. UUID) to track users, payments, or invoices.
* **Flexible fees**: It is possible for fees to be paid by you or passed directly onto the end customer. More information on fees can be found [here](https://learn.rhino.fi/collections/8556189802-fees).
* **Validity:** Unlimited. Smart Deposit Addresses are monitored for 7 years ensuring that no later transactions are missed.
* **Deposit size**: Typically 5-digit USD+ supported, 7-digit on request.
* **100% KYT screening**: Every deposit is screened for KYT/AML before crediting. Clients never receive funds that fail compliance checks. Enhanced screening options and real-time AML notifications are available via the [Enhanced Compliance & Risk Management](/get-started/extensions/enhanced-compliance) extension.
* **Privacy benefit**: SDAs are naturally decoupled from destination addresses.
* **Refund address**: Users/clients can specify fallback if a transaction fails (\< 1% of cases) or can use our concierge approach to processing refunds e.g in the case of unsupported tokens being received into the SDA.
## The Constraints
There are some limitations to be aware of:
* Deposits below \$5 USD equivalent or above set limits will not process and require a manual refund.
* If liquidity is insufficient, deposits will retry until successful or refunded.
* Fees can be fetched in real time through the API or fixed by agreement.
* Internal native token transfers (like ETH) are only supported on Base, Ethereum and Optimism currently. If such a transfer is sent on an unsupported chain, funds can be returned through our customer service.
### Core Technologies
Several technologies come together to make Smart Deposit Addresses (SDAs) work in practice:
* **API / SDK (SDA generation)**: Developers generate deposit addresses via Rhino's API or SDK with zero blockchain complexity. No private key management or contract deployment is required.
* **Wallet & App Transfers (funding SDA)**: Any wallet, CEX, or app that supports token transfers can send funds to an SDA. This keeps the user interaction primitive and universal.
* **Rhino.fi Solver & Liquidity Layer (orchestration & crediting)**: Our solver system monitors deposits in real time and uses Rhino's liquidity to credit the specified destination chain/address.
* **Reconciliation (sweeping)**: On EVM chains we use **EIP-7702**; for non-EVMs we maintain custom implementations. This ensures SDA balances are reconciled safely after crediting.
## Before You Start
**Default limits apply to Smart Deposit Addresses for initial testing purposes:**
* 500 total SDAs
* 50 SDAs generated per hour
Please contact your Rhino.fi account manager and the customer success team to lift your limits.
* You'll need an account in the [Rhino.fi Console](https://console.rhino.fi/).
* The [API docs](/api-integration/smart-deposits) provide an in depth guide to how to create your first Smart Deposit Addresses
# Supported Chains and Tokens
Source: https://docs.rhino.fi/get-started/supported-chains
List of chains and tokens that Rhino.fi support for Smart Deposit Addresses (SDAs) and the Rhino.fi Bridge.
Rhino.fi supports two route types:
* **Bridge:** moves the same token between chains
* **Bridge + swap:** moves between chains and convert into a different supported token on the destination
For bridge-only activity, always check that the source chain and destination chain support the same token
> **Bridge- only Example:**
>
> * *USDT on Tron → USDT on Polygon works because USDT is supported on both Tron and Polygon.*
> * *USDT on Sonic → USDT on Paradex will not work as USDT is not supported on Paradex*
For bridge and swap activity, check that the token you wish to send is supported on the source chain and the token you wish to receive is supported on the destination chain
> **Bridge and Swap Example:**
>
> *USDC on Optimism → USDT on Celo works because USDC is supported on Optimism and USDT is supported on Celo.*
1: ETH can only be the destination asset when the transaction is bridging ETH from one chain to another, no swapping into ETH is supported.
2: BNB can only be the destination asset when the transaction is bridging BNB from one chain to another, no swapping into BNB is supported.
3: Only sending from an Externally Owned Account (EOA) on Base is supported, sending from a smart contract on Base is not supported.
**The following table details what tokens can be sent into and out from the [Rhino.fi](http://Rhino.fi) bridge**
| | Sent TO the bridge | Received FROM the bridge |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Arbitrum One | **Stablecoins**
USDC, USDT, USDe, PYUSD
**Other Assets**
ETH, USDS, WBTC | **Stablecoins**
USDC, USDT
**Other Assets**
ETH1, |
| Avalanche | **Stablecoins**
USDC, USDT, USDe
**Other Assets**
WBTC | **Stablecoins**
USDC, USDT, USDe |
| Base | **Stablecoins**
EURC, USDC, USDT, USDe
**Other Assets**
ETH, USDS, WBTC | **Stablecoins**
EURC, USDC, USDT,
**Other Assets**
ETH1, 3, |
| Bitcoin | **Other Assets**
BTC | |
| BNB Smart Chain | **Stablecoins**
USDC, USDT, USDe
**Other Assets**
BNB, ETH, USD1, WBTC | **Stablecoins**
USDC, USDT
**Other Assets**
BNB2, ETH 1 |
| Celo | **Stablecoins**
USDC, USDT | |
| Ethereum | **Stablecoins**
EURC, USDC, USDT, EURCV, EURE, PYUSD, USAT, USD1, USDS
**Other Assets**
ETH, SENT, WBTC | **Stablecoins**
EURC, USDC, USDT
**Other Assets**
ETH1 |
| Gnosis | **Stablecoins**
USDC, USDT, EURE | **Stablecoins**
USDC, USDT |
| HyperEVM | **Stablecoins**
USDC, USDT, USDe | **Stablecoins**
USDC, USDT |
| Ink | **Stablecoins**
USDC, USDT
**Other Assets**
ETH | **Stablecoins**
USDC, USDT
**Other Assets**
ETH 1 |
| Kaia | **Stablecoins**
USDT | **Stablecoins**
USDT |
| Katana | **Stablecoins**
USDC, USDT
**Other Assets**
ETH | **Stablecoins**
USDC, USDT
**Other Assets**
ETH1 |
| Linea | **Stablecoins**
USDC, USDT | **Stablecoins**
USDC, USDT |
| Mantle | **Stablecoins**
USDC, USDT, USDe
**Other Assets**
ETH | **Stablecoins**
USDC, USDT
**Other Assets**
ETH1 |
| opBNB | **Stablecoins**
USDT
**Other Assets**
BNB | **Stablecoins**
USDT
**Other Assets**
BNB2 |
| Optimism | **Stablecoins**
USDC, USDT, USDe, EURC
**Other Assets**
ETH | **Stablecoins**
USDC, USDT
**Other Assets**
ETH1 |
| Plasma | **Stablecoins**
USDT, USDe, USDC | **Stablecoins**
USDT, USDC |
| Polygon | **Stablecoins**
USDC, USDT, PYUSD
**Other Assets**
ETH | **Stablecoins**
USDC, USDT
**Other Assets**
ETH1 |
| Solana | **Stablecoins**
USDC, USDT, USDe, USD1, USDS, EURC | **Stablecoins**
USDC, USDT |
| Stable | **Stablecoins**
USDT | **Stablecoins**
USDT |
| Starknet | **Stablecoins**
EURC, USDC, USDT,
**Other Assets**
ETH, WBTC | **Stablecoins**
EURC, USDC, USDT,
**Other Assets**
ETH1 |
| Stellar | **Stablecoins**
USDC | **Stablecoins**
USDC |
| Tempo | **Stablecoins**
USDC, USDT | **Stablecoins**
USDC, USDT |
| Ton | **Stablecoins**
USDT
| **Stablecoins**
USDT |
| Tron | **Stablecoins**
USDT | **Stablecoins**
USDT |
| | |
| - | - |
| | |
**Smart Deposit Addresses (SDAs)** allow you to receive supported tokens on a supported chain and route them onwards. They have inbuilt bridging and swapping functionality, therefore once assets are received into the SDA, then they can be swapped and bridged to a varierty of supported chains and tokens.
For SDA activity, always check:
* that the token being **sent into the SDA** is supported on that SDA chain
* that the token being **received on the destination chain** is supported on the destination chain
> **SDA Examples:**
>
> * *USDT on Tron → USDT on Polygon works because USDT is supported on both chains.*
> * *USDC on Polygon → USDT on Celo works because USDC is supported on Polygon and USDT is supported on Celo.*
> * *USDT on Kaia → USDC on Tron will not work because Tron does not support USDC.*
**The following table details what tokens can be sent to and from a Rhino.fi Smart Deposit Address:**
| **Chain** | **Sent TO a Smart Deposit Address (SDA)** | **Received FROM a Smart Deposit Address (SDA)** |
| :-------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Arbitrum One | **Stablecoins**
USDC, USDT, USDe, USDS, PYUSD
**Other Assets**
WBTC, ETH | **Stablecoins**
USDC, USDT
|
| Base | **Stablecoins**
EURC, USDC, USDT, USDe, USDS
**Other Assets**
WBTC, ETH | **Stablecoins**
EURC, USDC, USDT
|
| Bitcoin | **Other Assets**
BTC | |
| BNB Smart Chain | **Stablecoins**
USDC, USDT, USDe, ETH, USD1
**Other Assets**
WBTC, BNB | **Stablecoins**
USDC, USDT
|
| Celo | **Stablecoins**
USDC, USDT | **Stablecoins**
USDC, USDT |
| Ethereum | **Stablecoins**
EURC, USDC, USDT, EURCV, EURE, PYUSD, USAT, USD1, USDS
**Other Assets**
ETH, WBTC, SENT | **Stablecoins**
EURC, USDC, USDT |
| Gnosis | **Stablecoins**
USDC, USDT, EURE | **Stablecoins**
USDC, USDT, |
| Kaia | **Stablecoins**
USDT | **Stablecoins**
USDT |
| Katana | **Stablecoins**
USDC, USDT | **Stablecoins**
USDC, USDT |
| Mantle | **Stablecoins**
USDC, USDT, USDe | **Stablecoins**
USDC, USDT |
| Optimism | **Stablecoins**
USDC, USDT, USDe, EURC
**Other Assets**
ETH | **Stablecoins**
USDC, USDT |
| Plasma | **Stablecoins**
USDT, USDe, USDC | **Stablecoins**
USDT, USDC |
| Polygon | Stablecoins
USDC, USDT, PYUSD | Stablecoins
USDC, USDT
|
| Solana | **Stablecoins**
USDC, USDT, USDe, USD1, USDS, EURC | **Stablecoins**
USDC, USDT |
| Stellar | **Stablecoins**
USDC | **Stablecoins**
USDC |
| Tempo | **Stablecoins**
USDC, USDT | **Stablecoins**
USDC, USDT |
| Tron | **Stablecoins**
USDT | **Stablecoins**
USDT |
**Assets activated on request.** The assets below are supported, but acceptance is enabled with settlement on demand rather than automatic, so they are activated on request. If a route you need involves one of them, [talk to the team](https://rhino.fi/contact) to have it enabled.
EURCV, EURE, PYUSD, USAT, USD1, USDe, USDS
**Assets supported on a single chain.** The assets below are supported on one chain only, so they cannot be bridged. They can still be swapped to an asset on another chain via Bridge + swap.
BTC, EURCV, PYUSD, SENT, USAT
# What is Rhino.fi?
Source: https://docs.rhino.fi/get-started/what-is-rhino
Learn about the Rhino.fi Stablecoin Activation Stack and how it turns stablecoin deposits into usable balances for enterprise teams.
## The Rhino.fi Stablecoin Activation Stack
Rhino.fi turns stablecoin deposits into usable balances. Enterprise teams — exchanges, neobanks, payment processors, DeFi protocols — use Rhino.fi to give their users a seamless stablecoin deposit experience across any chain, without building or operating the underlying infrastructure themselves.
## The value chain: deposit → settlement → activation
Every transaction through Rhino.fi follows three steps:
1. **Deposit** — A user sends stablecoins to a deposit address from any supported chain, CEX, or wallet. No bridge UI, no chain selection, no manual routing.
2. **Settlement** — Rhino.fi detects the deposit, routes funds through its liquidity layer, and credits the destination chain with a deterministic outcome.
3. **Activation** — Optionally, Rhino.fi executes a configurable onchain action at the destination — vault deposit, contract call, protocol interaction — so funds land in a ready-to-use state.
## Three pillars
### Universal stablecoin deposit experience
One deposit address accepts stablecoins from any supported chain. Users never need to select a network or interact with a bridge. Fewer UX steps means fewer failures and higher completion rates.
### Fast, predictable, ready-to-use outcomes
Settlement is deterministic: Rhino.fi honors quoted amounts and fees, handles gas estimation, protects against MEV and gas spikes, and achieves a 99.99% route success rate. Funds land in the exact state you specify.
### Specialist, enterprise-grade execution
Rhino.fi owns the full stack: liquidity, routing, compliance, failure handling, refunds, and monitoring. 100% of deposits are screened for KYT/AML before crediting. Rhino.fi handles rebalancing, retries, and incident response — so your team does not have to.
## What's included
| Component | Description |
| --------------------------- | -------------------------------------------------------------- |
| **Smart Deposit Addresses** | One address per user, any source chain, KYT/AML built in |
| **Bridge & Swap** | 20+ chains, deterministic settlement, configurable speed tiers |
| **Webhooks** | Real-time transaction status events |
| **Rhino.fi Console** | Transaction history, analytics, team settings, configuration |
| **Checkout** | *(Coming soon)* |
## Extensions (premium)
Select extensions are available as premium add-ons:
| Extension | Description |
| ----------------------------------------- | ----------------------------------------------------------------- |
| **Enhanced Compliance & Risk Management** | Custom AML screening and real-time AML notifications |
| **Automated Onchain Actions** | Post-settlement contract calls that activate funds immediately |
| **1:1 Stablecoin Swaps** | USDT↔USDC at a guaranteed 1:1 rate, no slippage |
| **Advanced Fee & Limit Management** | Sponsor fees for end users, or add markup; end-of-month invoicing |
| **Custom Chain & Token Support** | Expand supported chains or tokens beyond the standard set |
| **Tron Access** | Enable TRON network deposits and settlement |
## Who uses Rhino.fi?
Rhino.fi is an API-first, full-service infrastructure product. It is built for engineering teams at:
* Exchanges and custodial platforms accepting stablecoin deposits from end users
* DeFi protocols onboarding liquidity from multiple chains
* Neobanks and payment processors that need predictable, compliant settlement
* Any product where users arrive with stablecoins on the wrong chain
If you are building a consumer bridge UI or a personal cross-chain wallet, Rhino.fi may not be the right fit. If you are running infrastructure that processes stablecoin deposits at volume, read on.
# Interacting with Bitcoin
Source: https://docs.rhino.fi/interacting-with-bitcoin
This page covers accepting native Bitcoin deposits via Smart Deposit Addresses. Bitcoin is currently **deposit-only** - bridging *to* Bitcoin (i.e. delivering native BTC as a destination asset) is not yet supported.
## Creating a Bitcoin deposit address
Use `POST /deposit-addresses` with `"BITCOIN"` in the `depositChains` array. A Bitcoin SDA is created the same way as an SDA on any other chain, but must be requested separately - Bitcoin, Tron and Solana each require their own call to generate an SDA (you can generate SDAs on all EVM chains with a single call).
**Sample request** - accepting BTC, settling to USDC on Base:
```json theme={null}
{
"depositChains": ["BITCOIN"],
"destinationChain": "BASE",
"destinationAddress": "0x7c9cfcf64a02200f0b3fa4ffd1773758fc082749",
"tokenOut": "USDC"
}
```
**Sample response:**
```json theme={null}
[
{
"depositChain": "BITCOIN",
"depositAddress": "bc1pwvza3f5hz85h9xdsmar8vjznsta9un82r5m5rvdsyys56u4xg75qytn3t4",
"destinationChain": "BASE",
"destinationAddress": "0x7c9cfcf64a02200f0b3fa4ffd1773758fc082749",
"supportedTokens": [
{
"symbol": "BTC",
"minDepositLimitUsd": 10,
"maxDepositLimitUsd": 50000
}
],
"isActive": true
}
]
```
`supportedTokens` on a Bitcoin SDA will always only contain `BTC`.
## Choosing a settlement asset
* If `tokenOut` is **not** specified, the deposit settles into WBTC by default, provided WBTC is available on the destination chain.
* If `tokenOut` **is** specified, the deposit settles into that asset instead. If the requested route isn't supported, address generation returns an error.
The API will return an error if the specified route is not supported for any reason. Contact your account manager to enable it.
### Settling into a wrapped BTC position via a Post Bridge Action
A Bitcoin deposit address can be configured with a Automated Onchain Action to deliver into a destination contract, such as a vault. Specify `tokenOut` for the asset the destination actually expects, as a safety measure - even when a PBA is configured. For example, to accept BTC but deliver into a vault as USDT, set `tokenOut: "USDT"`.
## Limits
* **Minimum deposit:** 5,000 sats is the absolute minimum. A \$10 minimum is surfaced in the API for consistency.
* **Maximum deposit:** \$50,000 per transaction at launch, expected to rise over time. Higher limits can be requested via your account manager.
## Speed
A Bitcoin deposit is processed after one network confirmation. Confirmation time depends on Bitcoin network conditions and typically falls between 5–20 minutes, though high network traffic can delay this further.
## Fees
A flat \$1 fee applies per Bitcoin deposit.
## Swap details in the history response
Because settling a Bitcoin deposit involves a currency conversion, the transaction history response for a Bitcoin SDA includes swap details in the `preSwap` / `postSwap` fields:
* **`preSwap`** - always present. Represents BTC converting into the settlement asset: WBTC or USDC, used as a middle step in our liquidity provision.
* **`postSwap`** - present only when the requested destination asset differs from the settlement asset (e.g. USDT, EURC). Represents the settlement asset converting into the requested `tokenOut`.
Quoting is based on the deposited BTC amount at the live market rate at time of deposit confirmation. Above the maximum deposit limit, the settled amount may incur slippage.
### Checking status and history
```text theme={null}
GET /deposit-addresses/{depositAddress}/{depositChain}/status
GET /deposit-addresses/{depositAddress}/{depositChain}/history
```
## Constraints
* Currently one-directional: native BTC deposits settle into other supported assets. Converting other assets back into native BTC is not yet supported.
* Available through the SDK, but only via the SDA route - not through the bridge & swap quote endpoint, so there are no committed quotes for Bitcoin.
* Native Bitcoin support is being rolled out client by client. Contact your account manager to enable it for your integration.
# Interacting with Stellar
Source: https://docs.rhino.fi/interacting-with-stellar
# Bridging TO Stellar
This page covers the case where **Stellar is the destination chain** — i.e. a user bridges or swaps from another chain and receives USDC on Stellar.
## Getting a quote
Use `POST /quote/bridge-swap/user` with `"chainOut": "Stellar"`.
### Recipient address format
When `chainOut` is `Stellar`, the `recipient` field accepts either:
* A **muxed address** (`M…`) — the preferred format, encodes both the destination G address and the memo in a single string.
* A **base account** (`G…`) — use only if the destination is a plain Stellar account with no memo required.
> ⚠️ **There is no separate memo field in the API.** If the destination requires a specific memo (e.g. your user's account on an exchange or a custodian), you must construct the muxed M address yourself from the G address + memo ID before passing it as `recipient`.
The API always returns `recipient` as specified in the quote — either an M address or a G address.
The request and response body shape is otherwise unchanged from other chains.
**Sample request** — bridging 100 USDT from Tron to USDC on Stellar:
```json theme={null}
{
"chainIn": "Tron",
"chainOut": "Stellar",
"amount": "100",
"tokenIn": "USDT",
"tokenOut": "USDC",
"depositor": "TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL",
"mode": "pay",
"recipient": "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAABUTGI4",
"isSda": "false"
}
```
The `recipient` here is the muxed address of the end user's Stellar account — not [rhino.fi](http://rhino.fi)'s pool address. This is the address the user will **receive** USDC at.
**Sample response:**
```json theme={null}
{
"chainIn": "Tron",
"chainOut": "Stellar",
"payAmount": "100",
"payAmountUsd": 100,
"receiveAmount": "",
"receiveAmountUsd": 100,
"fees": {
"fee": "0.33",
"feeUsd": 0.33,
"gasFee": "0.23",
"gasFeeUsd": 0.23,
"platformFee": "0.10",
"platformFeeUsd": 0.10
},
"depositor": "TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL",
"recipient": "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAABUTGI4",
"expiresAt": "12/06/2026 12:34:56",
"quoteId": "1hd86fhds87dfyfghfh",
"estimatedDuration": 1000
}
```
The `recipient` in the response echoes back the muxed address as provided.
### `depositor` when Stellar is the source
If `chainIn` is also Stellar (i.e. a Stellar-to-Stellar swap), the `depositor` field accepts either a muxed address or a base account.
## Trustlines
The destination address must have a trustline established for Stellar USDC. If the trustline is not established, the transaction will fail and will require a) manual refund or b) manual retry once the trustline is set.
***
# Bridging FROM Stellar
This page covers the case where **Stellar is the source chain** — i.e. a user sends USDC on Stellar into [rhino.fi](http://rhino.fi) to bridge or swap to another chain.
There are two distinct deposit modes on Stellar. Both land at the same [rhino.fi](http://rhino.fi) pool address but use different memo types to identify the deposit:
* **SDA (Smart Deposit Address)** — persistent, per-user address. Uses `MEMO_ID` (numeric). Suitable for all wallets and CEXes.
* **Regular bridge (commitment)** — one-time deposit tied to a specific quote. Uses `MEMO_TEXT` (hex string). For programmatic and wallet-initiated flows only.
## Option 1 - SDA deposit (persistent deposit address)
### Creating a Stellar deposit address
Use `POST /deposit-addresses` with `"STELLAR"` in the `depositChains` array.
**Sample request** — funds received from Stellar, sent onwards to Base:
```json theme={null}
{
"depositChains": ["STELLAR"],
"destinationChain": "BASE",
"destinationAddress": "0x7c9cfcf64a02200f0b3fa4ffd1773758fc082749",
"tokenOut": "USDC",
}
```
**Sample response:**
```json theme={null}
[
{
"depositChain": "STELLAR",
"depositAddress": "MAGPNUMMR6FQILPKTCP4TETJWC7DAYAGOIZAPHA63IOBTUHT2MMLYAAAAAAH5SLB66OM4",
"destinationChain": "BASE",
"destinationAddress": "0x7c9cfcf64a02200f0b3fa4ffd1773758fc082749",
"supportedTokens": [
{
"symbol": "USDC",
"address": "USDC-GDVK4AIWLLNCDTL7RZOKP4NHRIDOWRTL3CCLXKELCFPG4ISNRJODMXKW",
"maxDepositLimitUsd": 10000,
"minDepositLimitUsd": 0.5
}
],
"isActive": true,
"chainMetadata": {
"_tag": "STELLAR",
"baseAddress": "GAGPNUMMR6FQILPKTCP4TETJWC7DAYAGOIZAPHA63IOBTUHT2MMLYQD6",
"memoId": "2127127031"
}
}
]
```
### What's different about this response
* `depositAddress` is **always** a muxed address (`M…`). This is the canonical identifier for the SDA — it is what you must use across all SDA-related endpoints.
* `chainMetadata` is present whenever `depositChain` is `STELLAR`. The `baseAddress` is [rhino.fi](http://rhino.fi)'s hotwallet — it is identical across all SDAs and is **not** a per-user identifier. The `memoId` is the per-user identifier. Surface `baseAddress` + `memoId` only when a user's wallet cannot accept a muxed address directly.
* Currently only `USDC` is supported as a deposit token.
**SDA identification:** All SDA endpoints (`/status`, `/history`, `/search`) use the **M address** to identify a specific SDA. The `baseAddress` (`G…`) in `chainMetadata` is [rhino.fi](http://rhino.fi)'s hotwallet and is not a valid SDA identifier.
**Displaying to end users:** Show the muxed address by default. If a user's wallet requires a base address + memo, surface both `chainMetadata.baseAddress` and `chainMetadata.memoId` and make clear that **both values must be used together**. A transaction sent to the base address without the correct memo ID cannot be credited.
### Checking status and history
```text theme={null}
GET /deposit-addresses/{depositAddress}/{depositChain}/status
GET /deposit-addresses/{depositAddress}/{depositChain}/history
```
When `depositChain` is `Stellar`, the `depositAddress` path parameter must be the **muxed address** (`M…`). Base addresses are not accepted here.
The `destinationAddress` in the response will reflect whatever was provided when the SDA was created — either a muxed address or a base account.
### Searching deposit addresses
```text theme={null}
GET /deposit-addresses/search
```
When filtering by `destinationChain: Stellar`, the `destinationAddress` parameter accepts either a muxed address or a base account.
## Option 2 - Regular bridge deposit (quote commitment)
The regular bridge flow uses `POST /quote/bridge-swap/user` with `"chainIn": "STELLAR"`. This is a one-time deposit tied to a specific bridge quote. It works exactly the same as a [regular bridge](https://docs.rhino.fi/sdk/bridge-functions/bridge) on any other chain[.](https://docs.rhino.fi/sdk/bridge-functions/bridge)
### How it works
1. Call `POST /quote/bridge-swap/user` with `chainIn: "STELLAR"`
2. The the response includes quote details and a `quoteId`
3. Commit a quote by calling `POST /quote/commit/{quoteId}`
4. Create a transaction on Stellar network by passing the `quoteId` as `MEMO_TEXT` with the deposit to the Stellar [contract address](https://docs.rhino.fi/general/contract-addresses)
### Why MEMO\_TEXT and not a muxed address?
The bridge commitment ID is a 96-bit value. Muxed addresses and `MEMO_ID` are capped at 64 bits which is too small. `MEMO_TEXT` (28-byte capacity) fits the 24-character hex encoding and allows to differentiate between bridge and SDA deposits.
### Differences from the SDA flow
| | SDA (Mode B) | Regular bridge (Mode A) |
| :-------------- | :---------------------------------- | :----------------------------------------------------- |
| Memo type | `MEMO_ID` - numeric uint64 | `MEMO_TEXT` - 24-char hex string |
| Persistence | Permanent. Reusable across deposits | One-time. Tied to a single quote |
| Compatibility | Most CEXes and wallets | Most wallets and embedded flows |
| User experience | User saves once and reuses | Client commits and builds the transaction for the user |
## SDK note
The SDK requires `"depositChains": ["STELLAR"]` when generating a Stellar SDA. The response will provide a muxed address and the base address + memo ID combination. We recommend our clients to surface the muxed address in the UI as a default and allow toggling to display the base address and memo ID as a fallback for wallets/systems that don’t support muxed addresses.
Please note that you need to generate a Stellar SDA separately from SDAs on other chains. As a reminder, you can generate SDAs on all EVM chains with one call. Stellar, Tron and Solana each require a separate call to generate an SDA.
# From API to SDK
Source: https://docs.rhino.fi/migration-guides/from-api
This guide will help you migrate to the SDK if you have been using the API directly before. Depending on how sophisticated your use case is, you can choose between different levels of using the SDK.
## Use the bridge functions
For the vast majority of use cases you should be able to replace your manual API and blockchain calls with the [bridge](/sdk/bridge-functions/bridge) or [prepareBridge](/sdk/bridge-functions/prepare-bridge) function and the included [chain adapters](/sdk/chain/adapters/overview). Those will handle all the intricacies of the Rhino API and smart contracts correctly.
However, if you have a use case that is not covered by those, read further to learn how to use the underlying primitives of the SDK directly.
If the included chain adapters do not cover your use case, you can [implement a custom chain adapter](/sdk/chain-adapters/custom-chain-adapters) and use that with the `bridge` or `prepareBridge` function.
## Using the SDK API wrapper
If your use case requires you to be in control of all API calls, you can still replace your manual API calls with the wrappers from the SDK to reduce the amount of code significantly:
```typescript theme={null}
// Manual API call
const result = await fetch('https://api.rhino.fi/bridge/quote/bridge-swap/user', {
method: 'POST',
headers: {
"content-type": "application/json",
"authorization": jwt,
},
body: JSON.stringify(quoteArgs),
})
// SDK replacement
const result = rhinoSdk.api.bridge.getSwapUserQuote(quoteArgs)
```
Under `api.bridge` and `api.config` in the SDK you will find equivalent functions to all the manual API calls that you may have used before. The parameters are mostly the same, although some minor convenience tweaks have been made in certain wrappers.
## Using the SDK chain adapters
If you would like to be directly in control of the blockchain interactions, you can use the included chain adapters directly:
```typescript theme={null}
// Set up the chain adapter, can be any of the included ones
const evmChainAdapter = getEvmChainAdapterFromPrivateKey(
'YOUR_PRIVATE_KEY',
chainConfig
)
// Check if a token approval is needed. Could be skipped if you know that the blockchain does not have a concept of token approvals, like Solana.
const approvalAmount = await evmChainAdapter.getApprovalAmount(depositAmount, walletAddress, tokenConfig)
if(approvalAmount) {
// Set up the token approval if needed
await evmChainAdapter.handleTokenApproval(approvalAmount, tokenConfig)
}
// Send the funds to be bridged to the bridge contract. Once successful, the status
// of the bridge can be tracked through the Rhino API as usual.
const { depositTxHash } = await evmChainAdapter.handleDeposit({
tokenConfig,
depositAmount,
commitmentId,
})
```
# To SDK version 1.x
Source: https://docs.rhino.fi/migration-guides/to-1x
Learn how to upgrade your code to version 1.x if you have been using a 0.x version of the SDK before.
## Summary
Version 1 of the SDK introduces bridge and swap and marks the first major stable release. It also introduces a shortcut to fetch bridge and token swap configs.
## Breaking changes
This release contains some minor breaking changes. Migration is very straightforward.
### Bridge functions change
The first argument of the `bridge` and `prepareBridge` functions now require an additional `type` field with the possible values of `bridge` and `bridgeSwap`. This is required as a swap requires slighly different arguments.
To adjust your existing code, you only need to add the highlighted field to your existing `bridge` or `prepareBridge` calls:
```typescript {2} theme={null}
const bridgeResult = await rhinoSdk.bridge({
type: 'bridge',
amount: '100',
token: SupportedTokens.USDT,
chainIn: SupportedChains.ARBITRUM_ONE,
chainOut: SupportedChains.SOLANA,
depositor: 'DEPOSITOR_ADDRESS',
recipient: 'RECIPIENT_ADDRESS',
mode: 'receive',
}, {
getChainAdapter: chainConfig =>
getEvmChainAdapterFromPrivateKey(
'YOUR_PRIVATE_KEY',
chainConfig,
),
})
```
### Chain adapter interface changes
The `getApprovalAmount` function on chain adapters previously only returned the required approval amount (or undefined if no approval needed). Now this function returns an object with `requiredAllowance` and `availableAllowance` fields.\
This change was neccessary to include those two fields into the `approval-needed` variant of the `prepareBridge` function. It allows integrations to display the available and required allowance before users initiate the approval transaction.
There is no action needed regarding this change unless you have implemented your own chain adapter.
## Config shortcuts
Previously you might have fetched the bridge config like this:
```typescript theme={null}
const bridgeConfig = await rhinoSdk.api.bridge.getBridgeConfig()
```
To streamline this with the addition of the new swap tokens config, a new API is now available:
```typescript theme={null}
// New way to fetch bridge config
const bridgeConfig = await rhinoSdk.api.config.bridge()
// New token swap config
const swapTokens = await rhinoSdk.api.config.swapTokens()
// Fetch both configs in one call
const configs = await rhinoSdk.api.config.all()
```
The `getBridgeConfig` in the bridge API wrapper is still available and will keep working. However, we recommend to use the new function for more idiomatic code.
# Bridge and swap
Source: https://docs.rhino.fi/sdk/bridge-and-swap
The SDK offers convenient functions to make bridge and swaps that handle all the low level logic of fetching/committing quotes and making the correct blockchain transactions needed.
## Introduction
Cross chain swaps in general work the same way as a standard bridge:
1. Fetch bridge config and additionally also a new config for supported swap tokens.
2. Generate a swap quote.
3. Commit the quote.
4. Deposit funds into the bridge contract.
5. Rhino handles the swap and bridge logic under the hood and sends the funds to the recipient on the destination chain.
## Making cross chain swaps
To make cross chain swaps you can use the same `bridge` or `prepareBridge` functions. The only difference is:
* Instead of the `token` field, a swap requires `tokenIn` and `tokenOut` fields
* The `type: 'bridgeSwap'` to indicate that a swap should be performed
```typescript highlight={2,4-5} theme={null}
const bridgeResult = await rhinoSdk.bridge({
type: 'bridgeSwap',
amount: '100',
tokenIn: SupportedTokens.USDT,
tokenOut: 'agEUR',
chainIn: SupportedChains.ARBITRUM_ONE,
chainOut: SupportedChains.BASE,
depositor: 'DEPOSITOR_ADDRESS',
recipient: 'RECIPIENT_ADDRESS',
mode: 'pay',
}, {
getChainAdapter: chainConfig =>
getEvmChainAdapterFromPrivateKey(
'YOUR_PRIVATE_KEY',
chainConfig,
),
})
```
## Swap quotes
When using the `prepareBridge` function with swaps or using the `checkQuote` hook, you will receive a quote object that contains familiar properties to a bridge only transaction. However in the edge case that the swap is being facilitated by an external aggregator then there will be additional properties in the response such as:
* `bridgePayAmount`: The amount of `tokenIn` that will be paid
* `bridgePayAmountUsd`: The USD value of the pay amout
* `minReceiveAmount`: The minimum amount of `tokenOut` that will be received on the destination chain after considering slippage.
* `minReceiveAmountUsd`: The USD value of the minimum receive amount
* `usdPriceTokenIn`: The current USD price of `tokenIn`
* `usdPriceTokenOut`: The current USD price of `tokenOut`
## Failed swaps
The main reasons that a swap fails are:
**1. Unsupported Quote Mode (**`mode: "receive"`**)**
Returns HTTP 422 (`Receive mode is not supported for the selected tokens`)
This error is generated where receive mode is incompatible with the chain and token pairings provided. To solve this, change the chain and tokens, or select pay mode instead.
**2. Amount Below Minimum Threshold / Fees Exceed Output**
Returns HTTP 422 or 500
This error is generated when the amount being sent on the source chainis below Rhino's minimum threshold or when the amount that will be received on the destination chain is below the applicable fees. To solve this, send a larger amount.
**3. Unsupported Route or Unconfigured Token Pair**
Returns HTTP 422 (`No common aggregators found` or `Operation unavailable at this time`)
This error is generated when the requesting pairs not supported. This could be because the token does not exist on the chain e.g `tokenIn = USDT` and `chainIn = Base`, or where the token/chain is not supported through the [Rhino.fi](http://Rhino.fi) feature e.g `chainIn = BITCOIN` for a bridge transaction rather than an Smart Deposit Address related transaction. To solve this, check the supported token and chain pairings in the Bridge and Smart Deposit Address config.
Any failed swaps will either be returned to the given `refundAddress` or will be manually refunded via the [Rhino.fi](http://Rhino.fi) compliance team.
N.B where a swap fails then the bridge function will return an error with type `SwapFailed` that contains metadata about the refund (refund chain, token, amount and transaction hash), and the `onBridgeStatusChange` hook will also first report `swap-failed` and then follow up with `failed-swap-refunded` once the refund has been processed.
# Bridge API
Source: https://docs.rhino.fi/sdk/bridge-api
Learn how to use the SDK to interact with the bridge API directly.
## Introduction
You can use the bridge API directly if you need some additional data or if your application requires more control over the bridge flow. For that the SDK offers a typesafe wrapper around the Rhino API through a dedicated object. The following sections showcase some common use cases for this.
## Fetch bridge or token configs
```typescript theme={null}
const bridgeConfigResult = await rhinoSdk.api.bridge.getBridgeConfig()
const tokenConfigResult = await rhinoSdk.api.bridge.getTokenConfig({
chain: 'ETHEREUM',
token: 'USDT'
})
```
## Generate and commit bridge quotes
```typescript theme={null}
const userQuoteResult = await rhinoSdk.api.bridge.getUserQuote({
token: SupportedTokens.USDT,
chainIn: SupportedChains.ETHEREUM,
chainOut: SupportedChains.SOLANA,
amount: '100',
mode: 'pay',
depositor: '0x1234567890abcdef1234567890abcdef12345678',
recipient: '11111111111111111111111111111112',
})
if(userQuoteResult.error) {
throw new Error(`Error fetching user quote: ${userQuoteResult.error}`)
}
const quoteId = userQuoteResult.data.quoteId
const commitmentResult = await rhinoSdk.api.bridge.commitQuote(quoteId)
if(commitmentResult.error) {
throw new Error(`Error committing quote: ${commitmentResult.error}`)
}
```
The [bridge functions](/sdk/bridge-functions/bridge) in the SDK will already handle this logic for you. You should only do this manually if you have a very specific use case that is not covered by those.
## Fetch bridge status or history
If you want to check the status of a bridge or fetch the history of your app, you can use the following functions:
```typescript theme={null}
const statusResult = await rhinoSdk.api.bridge.getBridgeStatus(quoteId)
const historyResult = await rhinoSdk.api.bridge.getUserBridgeHistoryCursor({ pageSize: 10 })
```
Querying the bridge history is only possible with a secret API key. [Learn more about API keys](/get-started/api-keys)
# Bridge
Source: https://docs.rhino.fi/sdk/bridge-functions/bridge
The SDK offers convenient functions to make bridges that handle all the low level logic of fetching/committing quotes and making the correct blockchain transactions needed.
## Example
A call to the bridge function that uses all possible options can look like this:
```typescript theme={null}
import { SupportedChains, SupportedTokens } from '@rhino.fi/sdk'
import { getEvmChainAdapterFromPrivateKey } from '@rhino.fi/sdk/adapters/evm'
const bridgeResult = await rhinoSdk.bridge({
type: 'bridge',
amount: '100',
token: SupportedTokens.USDT,
chainIn: SupportedChains.ARBITRUM_ONE,
chainOut: SupportedChains.SOLANA,
depositor: 'DEPOSITOR_ADDRESS',
recipient: 'RECIPIENT_ADDRESS',
mode: 'receive',
gasBoost: {
amountNative: '4'
}
}, {
getChainAdapter: chainConfig =>
getEvmChainAdapterFromPrivateKey(
'YOUR_PRIVATE_KEY',
chainConfig,
),
hooks: {
checkQuote: quote => quote.fees.feeUsd < 5,
onBridgeStatusChange: status => console.log('Bridge status changed', status),
},
timeoutSeconds: 600,
bridgeConfig: bridgeConfig,
})
if (bridgeResult.data) {
console.log('Bridge successful', bridgeResult.data.withdrawTxHash)
} else {
console.log('Bridge error', bridgeResult.error)
}
```
## Bridge data
The first parameter of this function specifies the main parameters of the bridge.
* `amount` & `token` specify how much of which token should be bridged.
* `chainIn` & `chainOut` specify the origin (`chainIn`) and destination (`chainOut`) chain.
* `depositor` specifies the address that will send the funds on the origin chain.
* `recipient` specifies the address that will receive the funds on the destination chain.
* `mode` specifies if the fees should be added on top of the `amount` or subtracted from it. [Learn more about the bridge modes](/get-started/architecture#bridge-modes)
* `gasBoost.amountNative` specifies the amount of native tokens that should be sent to the recipient address on top of the bridged tokens.
## Options
The second parameter of this function is an options object that allows you to customize the bridge flow. Only `getChainAdapter` is mandatory.
### getChainAdapter
This function has to return a [chain adapter](/sdk/chain-adapters/overview) that matches the origin chain. It will be used to check if the depositors balance is sufficient, handle token approvals (if needed) and deposit the funds into the bridge contract.
Since all chain adapters included in this SDK will require the Rhino chain config to be initialized, this function will provide it as an argument to simply be passed through into the chain adapter function.
This modular design allows you to only import the chain adapters that you actually need which will ensure that only the code you actually need will be included in your final bundle.
### hooks.checkQuote (optional)
You can provide a predicate function that will receive the generated quote and has to return a boolean indicating if the quote is acceptable or not. If `false` is returned, the bridge will be aborted.
You can use this to make sure you only make bridges under a certain fee threshold for example.
### hooks.onBridgeStatusChange (optional)
Since the bridge only provides a result at the end, you can use this callback to be notified whenever the status of the bridge changes.
### timeoutSeconds (optional)
Once the deposit into the bridge contract is made, the `bridge` function will poll the Rhino API until the bridge has been confirmed as completed. By default the polling will be aborted after a default timeout of 10 minutes. With this property you can specify a different timeout.
### gasOptions (optional)
Gas options for EVM deposit and approval transactions. Use these to customize gas pricing and limits, for example to add buffer during fee spikes. These options are only used for EVM chains and ignored by other chain adapters.
```typescript theme={null}
const bridgeResult = await rhinoSdk.bridge(bridgeData, {
getChainAdapter: chainConfig =>
getEvmChainAdapterFromPrivateKey('YOUR_PRIVATE_KEY', chainConfig),
gasOptions: {
maxFeePerGas: 50000000000n, // 50 gwei
maxPriorityFeePerGas: 2000000000n, // 2 gwei
},
})
```
Available properties:
* `maxFeePerGas` - Maximum fee per gas unit in wei (EIP-1559). Use this to ensure your transaction gets included during fee spikes.
* `maxPriorityFeePerGas` - Maximum priority fee per gas unit in wei (EIP-1559). This is the tip paid to validators.
* `gasPrice` - Gas price in wei (legacy transactions). Use this for chains that don't support EIP-1559.
* `gasLimit` - Gas limit for the transaction. If not provided, it will be estimated automatically.
### bridgeConfig and swapTokensConfig (optional)
Internally the `bridge` function will fetch the current bridge and swap tokens (if needed) config from the Rhino API. If you already fetched those configs manually for some other purpose you can pass it into this property to avoid the extra network requests.
Please make sure that you pass in a somewhat recent bridge/swap tokens config. Using a too old config can lead to errors in the quote handling later.
## Handling the bridge result
The `bridge` function will return a result object with nullable `data` and `error` properties. If the bridge was successful, the `data` property will contain:
* `depositTxHash`: The hash of the transaction on the origin chain that sent the funds to the bridge contract.
* `withdrawTxHash`: The hash of the transaction on the destination chain that sent the funds to the recipient address.
If an error occurred during the bridge, the `error` property will contain the exact error that happened and potentially some additional properties to provide more context to the error. For example, if the given route is not supported you might receive a bridge result like this:
```typescript theme={null}
{
error: {
type: 'TokenNotSupported',
token: 'ETH',
chains: ['SOLANA']
}
}
```
This error would indicate that you tried to bridge ETH from or to Solana but Rhino does not support this combination.
# Prepare bridge
Source: https://docs.rhino.fi/sdk/bridge-functions/prepare-bridge
If you need more control over the bridge flow, you can use the `prepareBridge` function instead of the previously discussed `bridge` function.
## Use case
The `prepareBridge` function is particularly useful if you want to make bridges based on user input in a UI. The standard bridge function will execute the entire bridge flow in one go, meaning only one user input to start the entire process is possible. The `prepareBridge` function however will allow you to first present the user with the generated quote and then only start the token approval (if needed) and actual bridge when the user explicitly initiates them.
## Usage example
```typescript highlight={5,8,16} theme={null}
// Same parameters as the bridge function
const preparedBridge = await rhinoSdk.prepareBridge(bridgeData, options)
switch (preparedBridge.type) {
case 'no-approval-needed':
const bridgeResult = await preparedBridge.bridge()
break
case 'approval-needed':
const approvalResult = await preparedBridge.approve()
if (approvalResult.type === 'success') {
const bridgeResult = await approvalResult.bridge()
} else {
console.log('Approval error', approvalResult.error)
}
break
case 'error':
console.log('Bridge error', preparedBridge.error)
break
}
```
Unlike the `bridge` function, `prepareBridge` will not make any transactions immediately. The initial call will only
* Fetch the bridge config and validate the given parameters
* Set up the chain adapter
* Fetch a quote
* Check if a token approval is needed
Then it will return with a discriminated union type with three different cases, highlighted in the code example:
`no-approval-needed`\
This means that no token approval is needed and the bridge can be initiated directly. The object will contain the generated quote and a `bridge` function that will initiate the actual bridge transaction when called. It will return the same result as the [standard bridge function](/sdk/bridge-functions/bridge).
`approval-needed`\
This means that a token approval is needed before the bridge can be initiated. The object will contain:
* The generated quote (including the expiration date that the quote must be committed by - the `expiresAt`param.)
* Information about the currently available and required token allowance
* An `approve` function that can be called to initiate the token approval
The `approval` function will return on object containing a function to make the actual bridge or an error if the token approval failed. This bridge function will behave the same as in the previous case.
`error`\
This means an error occurred during the preparation. It will contain the same error type as the standard bridge function result.
# Custom chain adapters
Source: https://docs.rhino.fi/sdk/chain-adapters/custom-chain-adapters
If you need more control over the chain interactions than the included chain adapters provider, you can also implement your own or extend an existing one.
Implementing and using your own chain adapters can lead to a loss of funds if implemented incorrectly. Please make sure to test your adapter on a testnet thoroughly before using it with real funds.
## Implementing your own custom chain adapter
All you need to implement your own chain adapter is to implement the `ChainAdapter` type exported by the SDK. You can find a list of the required properties in the [chain adapters overview](/sdk/chain-adapters/overview). It would like something like this:
```typescript theme={null}
import type { ChainAdapter } from '@rhino.fi/sdk'
const customEvmChainAdapter: ChainAdapter = {
networkId: '',
getApprovalAmount: () => ...,
handleTokenApproval: () => ...,
handleDeposit: () => ...,
getTokenBalance: () => ...,
getAllTokenBalances: () => ...,
}
```
## Extending an existing chain adapter
If you only want to tweak a specific aspect of an existing chain adapter, you could simply take one as base and only re-implement some specific functions. Example:
```typescript From provider theme={null}
import { getEvmChainAdapterFromProvider } from '@rhino.fi/sdk/adapters/evm'
import type { ChainAdapter } from '@rhino.fi/sdk'
const evmChainAdapter = getEvmChainAdapterFromProvider(
provider,
chainConfig
)
const customEvmChainAdapter: ChainAdapter = {
...chainAdapter,
handleDeposit: (args) => {
// Your custom bridge deposit function
}
}
```
Please check [this section](/contracts/contract-examples) for guidance on how to interact with the Rhino bridge contracts if you plan on implementing a custom bridge deposit function.
# EVM Chain Adapter
Source: https://docs.rhino.fi/sdk/chain-adapters/evm-chains
Since all EVM chains can use the same tooling and libraries, you can use the same chain adapter function for all of them. You can create one either from a EIP-1193 compatible provider or as a shortcut from a private key.
### From EIP-1193 provider
Use this method to use connected wallets in webapps or if you want to set up a provider yourself.
```typescript theme={null}
import { getEvmChainAdapterFromProvider } from '@rhino.fi/sdk/adapters/evm'
const chainAdapter = getEvmChainAdapterFromProvider(
provider,
chainConfig
)
```
### From private key
Use this method as a shortcut to setting up a provider yourself.
```typescript theme={null}
import { getEvmChainAdapterFromPrivateKey } from '@rhino.fi/sdk/adapters/evm'
const chainAdapter = getEvmChainAdapterFromPrivateKey(
'YOUR_PRIVATE_KEY',
chainConfig
)
```
### Options
Both functions also accept a 3rd options parameter. Currently it only includes a `tokenAllowanceOverride` property that allows you to specify a higher token allowance to be used instead of the calculated one. This can be useful to optimize gas usage by setting bigger allowances that will last for multiple bridges.
## Fetching EVM balances
Normally you would use the `getAllBalances` function on a chain adapter to fetch all balances of an address on a chain. However as the same address is can hold assets on any EVM chain, it would be very cumbersome to create a chain adapter for each EVM chain and query it's balances.\
To make this easier, the SDK provides a shortcut function to fetch all balances on all supported EVM chains by only providing the [bridge config](/sdk/concepts#bridge-and-chain-configs) and a wallet address. You can use it like this:
```typescript theme={null}
import { getAllEvmBalances } from '@rhino.fi/sdk/adapters/evm'
const balances = await getAllEvmBalances(bridgeConfig, 'WALLET_ADDRESS')
console.log(balances)
```
The result will look like this:
```typescript theme={null}
{
ETHEREUM: {
USDT: {
tokenConfig: {...},
balance: 10550000n,
balanceWithDecimals; 10.55,
balanceFormatted: '10.55',
},
USDC: {...},
},
BASE: {...}
}
```
# Chain Adapters Overview
Source: https://docs.rhino.fi/sdk/chain-adapters/overview
The SDK ships with ready-to-use chain adapters for all chains that Rhino supports.
A chain adapter encapsulates all the logic required to interact with a specific blockchain type. Each chain adapter has the following properties:
* `networkId`: A unique identifier for the chain. This matches the `networkId` field that is used in the bridge config returned by the Rhino API.
* `getTokenBalance`: Returns the balance of a given address for a specific token.
* `getAllTokenBalances`: Returns the balances of all tokens that are supported by Rhino on a chain for a given address. Ideally some form of multicall is used to get all the data in one request. If that is not possible, multiple requests using `getTokenBalance` for each token individually could be made.
* `getApprovalAmount`: Returns the amount of tokens that need to be approved for the bridge contract call to work. If the existing allowance is enough or the chain has no concept of token approvals, this function returns `undefined`.
* `handleTokenApproval`: Makes the correct call to the token contract to approve the bridge contract as a spender for the needed amount.
* `handleDeposit`: Makes the correct call to the bridge contract to deposit the needed amount of tokens to initiate the bridge. Returns the transaction hash if successful.
* `estimateDepositGas` (optional): Given the same arguments as `handleDeposit`, return the estimated amount of gas tokens needed to pay the transaction fee for the bridge deposit.
The [bridge functions](/sdk/bridge-functions/bridge) included in the SDK will take care of using chain adapters correctly so generally you will not have a need to use most of those functions directly.
You might however want to use a chain adapter manually to fetch balances for example.
## General usage
Each chain adapter has a different way of constructing it, as each chain uses different libraries. However, one parameter all included chain adapters have in common is the config of the chain from the Rhino API. The included [bridge functions](/sdk/bridge-functions) will handle fetching the correct chain config for you. If you wan to construct a chain adapter ahead of time, you will need to [fetch the chain config](/sdk/concepts#bridge-and-chain-configs) manually before.
## Included chain adapters
* [EVM chains](/sdk/chain-adapters/evm-chains)
* [Solana](/sdk/chain-adapters/solana)
* [Starknet](/sdk/chain-adapters/starknet)
* [Ton](/sdk/chain-adapters/ton)
* [Tron](/sdk/chain-adapters/tron)
* [Paradex](/sdk/chain-adapters/paradex)
# Paradex Chain Adapter
Source: https://docs.rhino.fi/sdk/chain-adapters/paradex
### Prerequisites
You will need to install the [Paradex SDK](https://www.npmjs.com/package/@paradex/sdk) to instantiate a Paradex chain adapter. As Paradex can derive an account from either a Starknet or an EVM based signature, you have two options of initializing a Paradex account to be passed into the chain adapter function.
```typescript From Starknet account theme={null}
import { Account, Config, ParaclearProvider } from '@paradex/sdk'
const config = Config.fetchConfig('prod')
const paradexAccount = await Account.fromStarknetAccount(
{
provider: new ParaclearProvider.DefaultProvider(config),
config,
account // your starknet.js account
},
)
```
```typescript From EVM account theme={null}
import { Account, Config, ParaclearProvider, Signer } from '@paradex/sdk'
const config = await Config.fetchConfig('prod')
const account = await Account.fromEthSigner({
provider: new ParaclearProvider.DefaultProvider(config),
config,
signer: Signer.ethersSignerAdapter(ethersSigner) // Pass your ethers signer here
})
```
Once you have your Paradex account object set up, you can create the chain adapter from it:
```typescript theme={null}
import { getParadexChainAdapterFromAccount } from '@rhino.fi/sdk/adapters/paradex'
const chainAdapter = getParadexChainAdapterFromAccount(
paradexAccount,
chainConfig
)
```
# Solana Chain Adapter
Source: https://docs.rhino.fi/sdk/chain-adapters/solana
You can either create a Solana chain adapter from an [Anchor](https://www.npmjs.com/package/@coral-xyz/anchor) `Wallet` instance or as a shortcut from a Solana secret key:
### From wallet
Use this method to use connected wallets in webapps or if you want to set up a wallet yourself.
```typescript theme={null}
import { getSolanaChainAdapterFromWallet } from '@rhino.fi/sdk/adapters/solana'
// You can initialize the wallet yourself or depending on the specific browser
// wallet find it in th global window object
const chainAdapter = getSolanaChainAdapterFromWallet(
wallet,
chainConfig,
rpcUrl,
)
```
### From secret key
Use this method as a shortcut to setting up a wallet yourself.
```typescript theme={null}
import { getSolanaChainAdapterFromSecretKey } from '@rhino.fi/sdk/adapters/solana'
const chainAdapter = getSolanaChainAdapterFromSecretKey(
'YOUR_SECRET_KEY',
chainConfig
rpcUrl,
)
```
As there are no stable public Solana RPC endpoints, you will need to provide your own.
# Starknet Chain Adapter
Source: https://docs.rhino.fi/sdk/chain-adapters/starknet
You can create a Starknet chain adapter from either a [starknet.js](https://www.npmjs.com/package/starknet) account or as a shortcut from a private key.
### From account
Use this method to use connected wallets in webapps or if you want to set up an account yourself.
```typescript theme={null}
import { getStarknetChainAdapterFromAccount } from '@rhino.fi/sdk/adapters/starknet'
const chainAdapter = getStarknetChainAdapterFromAccount(
account,
chainConfig
)
```
### From private key
Use this method as a shortcut to setting up an account yourself.
```typescript theme={null}
import { getStarknetChainAdapterFromPrivateKey } from '@rhino.fi/sdk/adapters/starknet'
const chainAdapter = getStarknetChainAdapterFromPrivateKey({
privateKey: 'YOUR_PRIVATE_KEY',
address: 'YOUR_ADDRESS',
chainConfig,
})
```
### Options
Both functions also accept a 3rd options parameter. Currently it only includes a `tokenAllowanceOverride` property that allows you to specify a higher token allowance to be used instead of the calculated one. This can be useful to optimize gas usage by setting bigger allowances that will last for multiple bridges.
# Ton Chain Adapter
Source: https://docs.rhino.fi/sdk/chain-adapters/ton
You can either create a TON chain adapter from a [TonConnectUI](https://www.npmjs.com/package/@tonconnect/ui) instance or from a TON private key:
### From wallet
Use this method to use connected wallets.
```typescript theme={null}
import { getTonChainAdapter } from '@rhino.fi/sdk/adapters/ton'
const chainAdapter = getTonChainAdapter(
tonConnectUI,
chainConfig,
rpcUrl,
)
```
### From private key
Use this method to pass a private key directly.
```typescript theme={null}
import { getTonChainAdapterFromSecretKey } from '@rhino.fi/sdk/adapters/ton'
const chainAdapter = getTonChainAdapterFromSecretKey(
'YOUR_PRIVATE_KEY',
'YOUR_PUBLIC_KEY',
chainConfig,
rpcUrl,
)
```
As there are no stable public TON RPC endpoints, you will need to provide your own.
# Tron Chain Adapter
Source: https://docs.rhino.fi/sdk/chain-adapters/tron
To create a chain adapter for Tron you will need to pass a [TronWeb](https://www.npmjs.com/package/tronweb) instance.
```typescript theme={null}
import { getTronChainAdapter } from '@rhino.fi/sdk/adapters/tron'
const chainAdapter = getTronChainAdapter(
tronWeb,
chainConfig
)
```
# Concepts
Source: https://docs.rhino.fi/sdk/concepts
Learn about the core concepts of the SDK.
## Bridge & Chain configs
Rhino has one bridge configuration that lists all the chains and tokens that are available for bridging. Additionally there is a configuration that lists all tokens that are available for bridge and swap. You can get those configs through the following SDK calls:
```typescript theme={null}
// General bridge config
const bridgeConfigResult = await sdk.api.config.bridge()
// Tokens available for swapping by chain
const swapTokensResult = await sdk.api.config.swapTokens()
// Shortcut to fetch both configs in one call
const allConfigsResult = await sdk.api.config.all()
```
[Learn about the bridge API](/sdk/bridge-api).
Assuming no error occurred, the result of the bridge config call will look like this:
```typescript Bridge config [expandable] theme={null}
{
ETHEREUM: {
name: "Ethereum",
type: "EVM",
networkId: "1",
contractAddress: "0xbca3039a18c0d2f2f84ba8a028c67290bc045afa",
multicallContractAddress: "0x0dbBD1bB03Ed63AE2beA0Ce892567884dffb70a5",
confirmationBlocks: 5,
avgBlockTime: 12,
nativeTokenName: "ETH",
nativeTokenDecimals: 18,
nativeTokenSafeguard: 0.008,
blockExplorer: "https://etherscan.io",
rpc: "https://eth-mainnet.blastapi.io/7efcf8bf-0f7a-4364-8a59-c1917983dbdc",
site: "https://ethereum.org",
status: "enabled",
tokens: {
ETH: {
token: "ETH",
address: "0x0000000000000000000000000000000000000000",
decimals: 18
},
USDC: {
token: "USDC",
address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
decimals: 6
},
USDT: {
token: "USDT",
address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
decimals: 6
}
},
gasBoostEnabled: false
},
SOLANA: {
// same format as above
},
// Every supported chain will have an entry here
}
```
The whole object corrosponds to the `BridgeConfig` type that is exported by the SDK.
Each key will be a chain while the value will represent the chain config (`ChainConfig` type). Each value in the `tokens` field of a chain config represents a `TokenConfig`.
A lot of functions in the SDK will have the `BridgeConfig` or a specific `ChainConfig`/`TokenConfig` as parameters. So for most advanced use cases you will most likely want to fetch the bridge config first.
## Chain Adapters
The SDK follows a modular approach where all blockchain interactions are encapsulated in chain adapters. When using one of the provided bridge functions, you always need to explicitly pass in a chain adapter that matches the origin chain.\
Chain adapters handle the following:
* Fetching balances
* Checking and setting token allowances
* Making a deposit to a bridge contract
* Estimating the gas fees for a bridge transaction (optional)
### Resources
* [More about chain adapters](/sdk/chain-adapters/overview)
* [How to implement custom chain adapters](/sdk/chain-adapters/custom-chain-adapters)
## Supported chains and tokens
Since the bridge functions will often take chains or tokens as `string` parameters, the SDK provides a list of supported chains and tokens to avoid the usage of magic strings. You can use them like this:
```typescript theme={null}
import { SupportedChains, SupportedTokens } from '@rhino.fi/sdk'
const chainIn = SupportedChains.ETHEREUM
const chainOut = SupportedChains.SOLANA
const token = SupportedTokens.USDT
```
Those lists are generated at release time of the SDK and are not guaranteed to be always up to date. Therefore those lists might be out sync with the bridge config if Rhino adds a new supported chain/token or temporarily suspends one.\
**Always use a freshly fetched bridge config if you want to display a list of available chains or tokens to a user for example. Do not rely on hardcoded lists for such purposes.**
`SupportedTokens` does not include swap tokens as those are too dynamic to be included in a static list like this.
[See SDA supported chains](/get-started/supported-chains#smart-deposit-address)
## Error handling
A lot of functions in the SDK will return an object with nullable `data` and `error` fields instead of throwing. Checking one for being defined will automatically narrow down the type of the other to non-nullable in the opposite code path. Errors are always a discriminated union type with a `_tag` or `type` field as discriminator. Some error types will also contain some additional fields with specific information about the error.\
For example, an error that could occur when generating a user quote could be:
```typescript theme={null}
{
_tag: 'WithdrawLimitReached',
token: 'USDT',
chain: 'ETHEREUM',
receiveAmount: '5000',
maxWithdrawAmount: '1000'
}
```
# Quickstart
Source: https://docs.rhino.fi/sdk/quickstart
Learn how to use the bridge SDK to make bridges with only a few lines of code.
The Rhino SDK is a JavaScript/TypeScript client for the Rhino Stablecoin Activation Stack. It handles authentication, config fetching, quote generation, and transaction execution — letting you bridge assets with a few lines of code. For non-JS environments, use the [REST API](/api-integration/introduction) directly.
### Install the package
```bash npm theme={null}
npm install @rhino.fi/sdk
```
```bash yarn theme={null}
yarn add @rhino.fi/sdk
```
```bash pnpm theme={null}
pnpm install @rhino.fi/sdk
```
```bash bun theme={null}
bun install @rhino.fi/sdk
```
### Initialize the SDK
```typeScript rhino-sdk.ts theme={null}
import { RhinoSdk } from '@rhino.fi/sdk'
export const rhinoSdk = RhinoSdk({
apiKey: 'YOUR_API_KEY',
})
```
You can create a project and manage API keys there.
### Make a bridge
```typescript bridge.ts theme={null}
import { SupportedChains, SupportedTokens } from '@rhino.fi/sdk'
import { getEvmChainAdapterFromPrivateKey } from '@rhino.fi/sdk/adapters/evm'
import { rhinoSdk } from './rhino-sdk'
const bridgeResult = await rhinoSdk.bridge({
type: 'bridge',
amount: '100',
token: SupportedTokens.USDT,
chainIn: SupportedChains.ARBITRUM_ONE,
chainOut: SupportedChains.SOLANA,
depositor: 'DEPOSITOR_ADDRESS',
recipient: 'RECIPIENT_ADDRESS',
mode: 'receive',
gasBoost: {
amountNative: '4'
}
}, {
getChainAdapter: chainConfig =>
getEvmChainAdapterFromPrivateKey(
'YOUR_PRIVATE_KEY',
chainConfig,
),
hooks: {
checkQuote: quote => quote.fees.feeUsd < 5,
onBridgeStatusChange: status => console.log('Bridge status changed', status),
},
})
if (bridgeResult.data) {
console.log('Bridge successful', bridgeResult.data.withdrawTxHash)
} else {
console.log('Bridge error', bridgeResult.error)
}
```
This would bridge **100 USDT** from **Arbitrum to Solana** while also receiving **4 SOL** tokens at the recipient address. Through the `checkQuote` hook a bridge that would cost over \$5 in fees would be aborted.
### Create a Smart Deposit Address
Smart Deposit Addresses let users bridge by making a simple token transfer — no contract interaction needed.
```typescript sda.ts theme={null}
import { rhinoSdk } from './rhino-sdk'
const sda = await rhinoSdk.api.depositAddresses.create({
depositChains: ['ETHEREUM', 'ARBITRUM', 'BASE'],
destinationChain: 'BASE',
destinationAddress: '0x123...',
})
// Result:
// {
// depositAddress: '0x457...',
// depositChain: 'ETHEREUM',
// destinationChain: 'BASE',
// destinationAddress: '0x123...',
// supportedTokens: [{ symbol: 'USDT', address: '0x789...' }],
// isActive: true,
// }
```
Once created, anyone can send supported tokens to the deposit address and funds will be automatically bridged to the destination.
```typescript sda-status.ts theme={null}
import { rhinoSdk } from './rhino-sdk'
// Check status of an existing SDA
const status = await rhinoSdk.api.depositAddresses.getStatus({
depositAddress: '0x457...',
depositChain: 'ETHEREUM',
})
```
We recommend generating SDAs on all available EVM chains at once to ensure funds sent to the wrong chain are still detected automatically.
For more details, see the full [SDK SDA reference](/sdk/smart-deposits) or the [API integration guide](/api-integration/smart-deposits).
## Resources
Learn about the core concepts of the SDK
Learn about the overall architecture of the Rhino bridge
Learn more on how to use the bridge functions
Learn how to use chain adapters to connect to all supported chains
# Smart Deposit Addresses
Source: https://docs.rhino.fi/sdk/smart-deposits
Manage Smart Deposit Addresses conveniently with the SDK.
Please read about the [general concepts of Smart Deposit Addresses](/api-integration/smart-deposits) before continuing here.
**Default limits apply to Smart Deposit Addresses for initial testing purposes:**
* 500 total SDAs
* 50 SDAs generated per hour
Please contact your Rhino.fi account manager and the customer success team to lift your limits.
## Using the SDK wrappers
The SDK provides wrappers around the [API calls](/api-integration/smart-deposits#api-interactions) to manage Smart Deposit Addresses. Those wrappers are available under `sdk.api.depositAddresses`.
### Examples
```typescript Find supported chains theme={null}
const depositAddresses = await sdk.api.depositAddresses.getSupportedChains()
// Result:
['ETHEREUM', ...]
```
```typescript Set up a new Smart Deposit Address theme={null}
const depositAddresses = await sdk.api.depositAddresses.create({
depositChains: ['ETHEREUM'],
destinationChain: 'BASE',
destinationAddress: '0x123...',
})
// Result:
{
depositAddress: '0x457...',
depositChain: 'ETHEREUM',
destinationChain: 'BASE',
destinationAddress: '0x123...',
supportedTokens: [{symbol: 'USDT', address: '0x789...'}],
isActive: true,
}
```
```typescript Check the status of a Smart Deposit Address theme={null}
// Same result as the create function above
const status = await sdk.api.depositAddresses.getStatus({
depositAddress: '0x123...',
depositChain: 'ETHEREUM',
})
```
# Token Prices
Source: https://docs.rhino.fi/sdk/token-prices
Learn how to easily fetch token prices through the SDK.
## Introduction
If you are building any kind of UI application, you most likely want to show the user the USD amount of tokens they are about to bridge. For this the SDK includes a set of function that can provide this data to you.
## Usage
You can fetch a USD prices as follows:
```typescript Single token price theme={null}
// Fetch the price of a single token
const ethPrice = await sdk.api.usdPrices.getTokenPrice('ETH')
console.log(ethPrice)
// => { token: "ETH", price: 4500, timestamp: ''}
```
```typescript All token prices theme={null}
// Fetch the prices of all supported tokens
const allPrices = await sdk.api.usdPrices.getAllPrices()
console.log(allPrices)
// => { ETH: { token: "ETH", price: 4500, timestamp: '' }, ... }
```
In both cases, the timestamp of a token price will tell you how old this specific price is.
# Server setup
Source: https://docs.rhino.fi/webhook/endpoint-setup
Rhino webhooks deliver real-time transaction status events to your server — covering deposit detection, settlement confirmation, and action outcomes for Smart Deposit Addresses and bridges.
Setting up a webhook server involves creating an HTTP endpoint that can receive POST requests from Rhino.fi, process the webhook payloads, and respond appropriately. This guide covers the essential components and best practices for webhook server implementation.
## Basic Server Requirements
### **Endpoint**
Your endpoint must be a `POST` endpoint and called `/rhinofi-event`.
It will receive a signature header called: `X-Rhino-Signature` which can be then verified ([see verification details](/webhook/verify-webhook-signature)).
The received body structure will be the following:
```
{
event: WebhookEvent, // check the Webhook Events section for more details
eventId: string,
ts: number,
}
```
It should return a 204 status with no content.
### **HTTPS Only**
Your webhook endpoint must use HTTPS to ensure secure data transmission. Rhino.fi will not send webhooks to HTTP endpoints.
### **Public Accessibility**
The endpoint must be publicly accessible. Rhino.fi will provide a signature when calling your endpoint for you to be able to identify the sender.
Your endpoint might implement rate limiting to prevent anyone from abusing it.
### **Reliable Uptime**
Ensure your server has good uptime. Failed webhook deliveries will be retried a few times, but consistent failures may result in events being lost.
# Events List
Source: https://docs.rhino.fi/webhook/events-list
Different events can be received depending on whether the transaction is a bridge/bridge+swap (generated through the `POST/quote/bridge-swap/user`endpoint) or a Smart Deposit Address involving transaction (funds are sent to an address as generated through the `POST/deposit-addresses`endpoint).
## Bridge/bridge+swap events
The following bridge events will be sent to your webhook servers.
The bridge events data structure is the same as the [**Bridge Status endpoint**](https://docs.rhino.fi/api-reference/bridge/history/obtain-the-status-of-a-specific-bridge-transaction) excluding the `state` field. The `_tag` field denotes the event type.
* **BRIDGE\_PENDING** - sent when the bridge quote is committed
* **BRIDGE\_ACCEPTED** - sent when the deposit transaction was received and validated
* **BRIDGE\_EXECUTED** - sent when the bridge is completed and received on the destination chain
* **BRIDGE\_REFUNDED** - when a swap is involved, the bridge might get refunded if the token price moved too much since the quote was emitted
* **BRIDGE\_CANCELLED** - sent when a committed bridge is CANCELLED (note that that cancelled bridges can still be actioned if a matching deposit is detected on-chain, subsequent bridge events will be sent)
## Smart Deposit Addresses events
The following events will be sent to your webhook servers for any activity involving a [Rhino.fi](http://Rhino.fi) Smart Deposit Address.
* **DEPOSIT\_ADDRESS\_CREATED -** sent when a new Smart Deposit Address is generated Event structure:
```javascript theme={null}
{
_tag: "DEPOSIT_ADDRESS_CREATED",
depositChain: string
depositAddress: string
destinationChain: string
destinationAddress: string
tokenOut?: string
postBridgeData?: PostBridgeData
addressNote?: string
}
```
* **BRIDGE\_\* -** the same BRIDGE\_PENDING, BRIDGE\_ACCEPTED and BRIDGE\_EXECUTED events as described above will be sent for Smart Deposit Address related activity.
* **DEPOSIT\_ADDRESS\_BRIDGE\_REJECTED -** sent when a Smart Deposit Address bridge is rejected. Event structure:
```javascript theme={null}
{
_tag: "DEPOSIT_ADDRESS_BRIDGE_REJECTED",
_id: string, // bridge ID
tokenSymbol: string
tokenAddress: string
amount: string
amountUsd: string
amountWei: string
sender: string // depositor address
txHash: string // deposit transaction hash
reason: string // reason why it was rejected, e.g "UNDER_MIN"
createdAt: string
depositAddress: { // associated deposit address parameters
depositChain: string
depositAddress: string
destinationChain: string
destinationAddress: string
}
}
```
* **DEPOSIT\_ADDRESS\_BRIDGE\_REJECTED - UNSUPPORTED\_TOKEN** - sent when a Smart Deposit Address bridge is rejected as the token sent is not supported by Rhino.fi. The full list of supported chains and assets can be found here: [rhino.fi](http://rhino.fi)[ - Docs\*\*Supported Chains - \*\*](https://docs.rhino.fi/get-started/supported-chains)**[rhino.fi](http://rhino.fi)[ - Docs](https://docs.rhino.fi/get-started/supported-chains)**. N.B Rhino.fi monitors for a selection of unsupported tokens per chain however this does not cover all scenarios where an unsupported token may be sent to an Smart Deposit Address. The event structure is:
```javascript theme={null}
{
_tag: "DEPOSIT_ADDRESS_BRIDGE_REJECTED - UNSUPPORTED_TOKEN",
_id: string, // bridge ID
tokenSymbol: string
tokenAddress: string
amount: string
amountUsd: string
amountWei: string
sender: string // depositor address
txHash: string // deposit transaction hash
reason: string // reason why it was rejected, e.g "UNDER_MIN"
createdAt: string
depositAddress: { // associated deposit address parameters
depositChain: string
depositAddress: string
destinationChain: string
destinationAddress: string
}
}
```
* **DEPOSIT\_ADDRESS\_BRIDGE\_DELAYED -** sent when a bridged transaction is taking longer than expected. This may be due to onchain congestion. The bridge transaction will be re-tried, there is no action required of the user. The event structure is:
```javascript theme={null}
{
_tag: "DEPOSIT_ADDRESS_BRIDGE_DELAYED",
_id: string, // bridge ID
tokenSymbol: string
tokenAddress: string
amount: string
amountUsd: string
amountWei: string
sender: string // depositor address
txHash: string // deposit transaction hash
originalErrorTag: string // reason why the bridge is delayed
depositAddress: { // associated deposit address parameters
depositChain: string
depositAddress: string
destinationChain: string
destinationAddress: string
}
}
```
* **SDA\_REFUND\_COMPLETED** sent when a a refund is issued from an SDA.
```javascript theme={null}
{
_tag: "SDA_REFUND_COMPLETED"
refundId: string,
depositAddress: string,
depositChain: string,
recipient: string, // recipient of the refund
trigger: {
_tag: "REJECTED_BRIDGE" | "UNSUPPORTED_TOKEN",
rejectedDepositAddressBridgeId: string | undefined,
nativeToken: boolean, // If this is true, tokenAddress will be undefined as native tokens don't have an address
amountWei: string,
amount: string | undefined, // Human readable refund amount
amountUsd: string | undefined,
tokenAddress: string | undefined,
tokenSymbol: string | undefined,
},
"originalDepositTxHash": string | undefined, // hash of the deposit transaction that is refunded
txHash: string, // hash of the refund transaction
txTimestamp: string, // timestamp of the refund transaction
}
```
The `trigger` field provides information on the reason for the refund:
* `REJECTED_BRIDGE`: Reason is a deposit we detected but rejected. `originalDepositTxHash` will contain the hash of the original deposit transaction and the `amount` and `tokenSymbol` fields will have values.
* `UNSUPPORTED_TOKEN`: Reason is a deposit with an unsupported token. `originalDepositTxHash` will not be available for this as we did not detect the transaction in our system.
## Webhook Event Flows
Information about the transition between different webhook events can be found [here](https://learn.rhino.fi/articles/8943216896-webhook-event-state-transitions)
# Get missed events
Source: https://docs.rhino.fi/webhook/get-missed-events
If your webhook server goes down for a while and you need to fetch the events that occurred during the down time you can use the following endpoint:
```
GET https://api.rhino.fi/webhook/user-events-cursor
```
The endpoint is authenticated and it takes a few query parameters:
* `sinceTimestamp`: you'll receive all the events that happened after this timestamp
* `pageToken`: token you received from a previous request to fetch the next page
* `pageSize`: how many events you want to receive per page (max 50)
[Full API spec](/api-reference/webhook/user-events/obtain-a-list-of-previously-generated-webhook-events)
The page + limit based endpoint previously described on this page is deprecated and will be removed soon. Please use the cursor based endpoint described above.
# Subscribe to webhooks
Source: https://docs.rhino.fi/webhook/subscribe
You can either setup your webhook URL once for your project in the Rhino Console or pass it on invidual bridges.
## Using the Rhino.fi Console
Within the Rhino.fi Console, navigate to the ‘Integrate’ section within the navigation bar. Under the ‘Webhooks’ section you are able to set up a new webhook and view an existing webhook.
To add a new webhook, enter the webhook URL.
Please make sure NOT to include the suffix `/rhinofi-event`.
e.g you should provide: [https://my-server.com/prefix](https://my-server.com/prefix), and Rhino.fi will then call: [https://my-server.com/prefix/rhinofi-event](https://my-server.com/prefix/rhinofi-event)
The list of webhook event types are available here: [https://docs.rhino.fi/webhook/events-list](https://docs.rhino.fi/webhook/events-list)
## Including within API calls
The Rhino.fi Console routes enables you to receive all webhook evenst through to a single webhook URL. However it’s also possible to specify a different URL per bridge, bridge+swap or Smart Deposit Address.
This can be achieved by passing a URL within the `webhookUrl` param in the:
* `POST/quote/bridge-swap/user` endpoint: for bridge and bridge+swap related activity
* `POST/deposit-addresses` endpoint: for Smart Deposit Address set up and related activity
For the exact specification, [see the API Reference](https://docs.rhino.fi/api-reference/sda/depositaddresses/generate-a-new-smart-deposit-address)
# Verify signature
Source: https://docs.rhino.fi/webhook/verify-webhook-signature
As your endpoint is public, anyone can send it some data. For you to make sure the data is coming from Rhino.fi, you'll receive a signature in the request header, called `X-Rhino-Signature`.
To verify the signature you have 2 options:
* Using the SDK
* Manual verification
### SDK
To verify the signature received using the SDK, you can do the following:
```typescript theme={null}
const isValid = await sdk.api.webhook.verifySignature(receivedEventBody, signature)
```
Under the hood it will fetch the Rhino.fi webhook public key and verify the data provided was signed by Rhino.fi.
### Manual verification
Manually verifying the received signature is a two step process.
You first need to fetch Rhino.fi webhook public key and then use it to verify the signature.
This can be done with the following function:
```typescript theme={null}
import { createHash, createVerify } from 'crypto'
const isSignatureValid = async (receivedEventBody, signature) => {
// Fetch Rhino.fi webhook public key
const res = await fetch('https://api.rhino.fi/webhook/public-key')
const publicKeyBase64 = await res.json()
const publicKey = Buffer.from(publicKeyBase64, 'base64').toString()
// Hash the stringified event
const hash = createHash('sha256')
const hashedMessage = hash.update(
JSON.stringify(receivedEventBody),
)
// Verify the signature
const verify = createVerify('RSA-SHA256')
verify.update(hashedMessage.digest('hex'))
return verify.verify(publicKey, signature, 'hex')
}
```
# iframe SDA Widget
Source: https://docs.rhino.fi/widget/sda-widget
The SDA Widget lets you integrate a fully functional Smart Deposit Address bridge UI into your application with only a few lines of code
## Embedding the Rhino.fi SDA Widget (iframe)
You can easily embed the Rhino.fi SDA Widget into any site using a simple `