# Introduction

Welcome to the Unlockd SDK documentation.&#x20;

Unlockd is a permissionless protocol designed to facilitate secure and efficient borrowing and lending against tokenized Real-World Assets (RWAs) and financial assets. Our platform leverages advanced AI to provide the safest and most cost-effective way for users to obtain instant loans and earn yield.

## What is Unlockd?

Unlockd is a permissionless liquidity protocol that enables users to act as depositors or borrowers. Built on Ethereum and Polygon and expanding to multiple Layer 2 networks, Unlockd employs a Peer-to-Pool model, offering a more efficient and fair system compared to traditional Peer-to-Peer models.

#### Key Features

* **Permissionless Borrowing**: Users can borrow against their tokenized assets without needing permission, benefiting from instant loans and fair interest rates, using just a wallet.
* **Efficient Yield Generation**: Lenders can contribute liquidity to pools and receive auto-compounding yields in USDC.
* **AI-Driven Risk Engine**: Our advanced AI ensures accurate asset valuations and optimal, market-adjusting loan conditions tailored to individual needs.
* **Full Custody & Ownership**: Borrowers maintain full custody and ownership of their collateralized assets through their Unlockd Account.
* **Proven Security**: The protocol adheres to rigorous security standards and has been audited by multiple firms to ensure the highest level of safety.

## Why Use Unlockd?

Unlockd simplifies the process of leveraging tokenized assets for financial operations. By providing a seamless, secure, and efficient platform for borrowing and lending, Unlockd empowers users to unlock the full potential of their digital and real-world assets in a permissionless way.

## Who Should Use Unlockd?

* **Developers**: Integrate Unlockd’s functionalities into applications, enhancing them with advanced borrowing and lending features.
* **Lenders**: Earn stable, auto-compounding yields by contributing liquidity to Unlockd’s pools.
* **Borrowers**: Access instant loans against tokenized assets without complex verification processes.

***

Explore the rest of the documentation to learn more about how Unlockd works and how you can start leveraging our powerful SDK to integrate these capabilities into your projects.


# Unlockd SDK

The Unlockd SDK is a powerful tool designed to enable seamless integration of blockchain-based borrowing functionalities into your applications. It allows developers to leverage the Unlockd protocol, providing users with the ability to collateralize their assets and obtain loans in various cryptocurrencies against tokenized Real-World Assets (RWAs).

### Key Features

* **Permissionless Integration**: The Unlockd SDK offers a simple and intuitive interface for integrating our permissionless protocol into your applications.
* **Comprehensive Asset Management**: Easily manage user assets, including collateralization and loan origination processes.
* **AI-Powered Valuations**: Utilize advanced AI to ensure accurate asset valuations and optimal loan conditions tailored to individual needs.
* **Full Custody & Ownership**: Borrowers maintain full custody and ownership of their collateralized assets through their Unlockd Account.
* **Proven Security**: The protocol adheres to rigorous security standards and has been audited by multiple firms to ensure the highest level of safety.

### Use Cases

The Unlockd SDK is particularly beneficial for partners whose assets are already supported and whitelisted within the Unlockd protocol. Here are some key use cases:

* **Native Borrowing Integration**: Implement borrowing against tokenized assets directly within your product interface, offering users seamless access to liquidity.
* **Enhanced Marketplaces**: For marketplaces or platforms allowing the purchase of assets, offer financing options at the point of purchase, enabling a Buy Now, Pay Later (BNPL) experience for your users.
* **Custom Financial Solutions**: Develop custom financial solutions that leverage Unlockd's RWA-backed borrowing capabilities to provide innovative services to your user base.

{% hint style="info" %}
More information is available at our [Use Cases](/use-cases-code-examples/borrow-against-rwas) and [Examples](/getting-started/code-examples) sections.
{% endhint %}

By integrating the Unlockd SDK, you can unlock new financial possibilities for your users, providing them with secure and efficient access to liquidity backed by tokenized Real-World Assets.

***

Explore the rest of the documentation to learn more about how to integrate the Unlockd SDK and start leveraging these powerful functionalities in your applications.


# Partners & Revenue Sharing

Unlockd provides a unique opportunity for partners to integrate our liquidity solutions directly into their products. By leveraging the Unlockd SDK, partners can offer seamless RWA-backed borrowing functionalities to their users, with the added benefit of participating in a revenue-sharing program.

## Whitelisting and Supported Assets

While the Unlockd Protocol is permissionless, [Veris Labs](https://verislabs.xyz), and eventually the Unlockd DAO, determine which assets are whitelisted and supported. These assets typically come from our tokenizing partners, who collaborate closely with Unlockd to ensure their assets are seamlessly integrated into our platform.

## Tailored Partnerships

For development teams and organizations with supported assets, Unlockd offers tailored partnerships. These partnerships enable the integration of Unlockd's advanced borrowing and lending functionalities directly into their products. Partners can choose to brand the integrated solution as powered by Unlockd or offer it as a completely native experience.

## Revenue Sharing Program

The Revenue Sharing Program is designed to create a mutually beneficial relationship between Unlockd and its partners. Here's how it works:

* **Integration**: Partners integrate Unlockd's functionalities using the Unlockd SDK.
* **Loan Origination**: Loans originated through the partner's integration generate revenue from the interest rate spread.
* **Revenue Split**: The generated revenue is split between the partner and Unlockd. The specific terms of the revenue split are tailored to each partnership.

{% hint style="success" %}

#### Benefits for Partners

* **Increased Revenue**: Earn a share of the revenue generated from loans originated through your integration.
* **Enhanced Offerings**: Provide your users with advanced RWA-backed borrowing capabilities, enhancing your product's value proposition.
* **Flexibility**: Choose whether to brand the integrated solution as powered by Unlockd or as a completely native experience.
  {% endhint %}

***

By partnering with Unlockd, you can unlock new revenue streams and offer your users innovative financial services. For more information on how to become a partner and participate in the Revenue Sharing Program, please [contact us](/resources/support-and-contact).


# Installation

To start using the Unlockd SDK in your project, follow these steps:

## Prerequisites

* Node.js (version 16.0.0 < 17.0.0)
* npm (version 0.0.4 or higher)
* An Ethereum wallet (e.g., MetaMask)

## Installation

Install the SDK using your preferred package manager:

Using npm:

```bash
$ npm install @verislabs/unlockd-sdk
```

Using bower:

```bash
$ bower install @verislabs/unlockd-sdk
```

Using yarn:

```bash
$ yarn add @verislabs/unlockd-sdk
```

Using pnpm:

```bash
$ pnpm add @verislabs/unlockd-sdk
```


# SDK Modules


# Action Module

The Action module allows you to perform actions such as borrowing assets and repaying loans.

## Flow

<figure><img src="/files/zc8bR8u1fMNdSbSTZWh7" alt=""><figcaption><p>Action Flow</p></figcaption></figure>

## Types

### Assets

```typescript
export type Nft = {
  collection: string
  tokenId: string
}
```

### Signature

```typescript
export type Signature = {
  data: object
  signature: { v: number; r: string; s: string; deadline: number }
}

/* data object
interface SignAction {
  loan: SignLoanConfig;
  assets: string[];
  underlyingAsset: string;
  nonce: BigInt;
  deadline: BigInt;
}

interface SignLoanConfig {
  loanId: string;
  aggLoanPrice: BigInt;
  aggLtv: BigInt;
  aggLiquidationThreshold: BigInt;
  totalAssets: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}
*/
```

### ClientOptions

```typescript
export type ClientOptions = {
  network?: Chain
}

export type Chain = 'mainnet' | 'sepolia' | 'localhost'
```

***

## Functions

### Borrowing Assets

To borrow assets using the SDK, use the `borrow` function from the `action` module:

<pre class="language-typescript"><code class="lang-typescript"><strong>export const borrow = async (
</strong><strong>        amount: BigInt, 
</strong><strong>        assets: Array&#x3C;Nft>, 
</strong><strong>        signature: Signature, 
</strong><strong>        options?: ClientOptions
</strong><strong>    )
</strong></code></pre>

```typescript
const result = await borrow(amount, assets[], signature, options?);
```

### Repaying Loans

To repay a borrowed loan, use the `repay` function from the `action` module:

```typescript
export const repay = async (
        amount: BigInt, 
        signature: Signature, 
        options?: ClientOptions
    )
```

```typescript
const result = await repay(amount, signature, options?);
```

***

Refer to the Action Module documentation for more details on the `borrow` and `repay` functions.

{% content-ref url="/pages/hnXwWgQd5bRcF7bJjAlG" %}
[Broken mention](broken://pages/hnXwWgQd5bRcF7bJjAlG)
{% endcontent-ref %}


# Auction Module

The Auction module provides functionality for participating in auctions for NFT collateral.

<figure><img src="/files/mAzYNfLFrlJRCAXHq16G" alt=""><figcaption><p>Auction flow</p></figcaption></figure>

## Types

### Signature

```typescript
export type Signature = {
  data: object
  signature: { v: number; r: string; s: string; deadline: number }
}

/* data object
interface SignAuction {
  loan: SignLoanConfig;
  assets: string[];
  assetPrice: BigInt;
  assetLtv: BigInt;
  endTime: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}

interface SignLoanConfig {
  loanId: string;
  aggLoanPrice: BigInt;
  aggLtv: BigInt;
  aggLiquidationThreshold: BigInt;
  totalAssets: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}
*/
```

### ClientOptions

```typescript
export type ClientOptions = {
  network?: Chain
}

export type Chain = 'mainnet' | 'sepolia' | 'localhost'
```

## Functions

### Placing a Bid

To place a bid in an auction, use the `bid` function from the `auction` module:

```typescript
export const bid = async (
        amountToPay: BigInt, 
        amountOfDebt: BigInt, 
        signature: Signature, 
        options?: ClientOptions
    )
```

```typescript
const result = await bid(amountToPay, amountOfDebt, signature, options?);
```

### Redeeming an Auction

To redeem an auction, use the `redeem` function from the `auction` module:

```typescript
export const redeem = async (
        signature: Signature, 
        options?: ClientOptions
    )
```

```typescript
const result = await redeem(signature, options?);
```

### Finalizing an Auction

To finalize an auction, use the `finalize` function from the `auction` module:

```typescript
export const finalize = async (
        claimOnUWallet: boolean,
        orderId: string,
        signature: Signature,
        options?: ClientOptions
    )
```

```typescript
const result = await finalize(claimOnUWallet, orderId, signature, options?);
```

***

Refer to the Auction Module documentation for more details on the `bid`, `redeem` and`finalize`functions.


# Market Module

The Market module allows you to create, manage, and interact with market items.

<figure><img src="/files/k3sPaIcRLm3LBo2qlBwP" alt=""><figcaption><p>Market flow</p></figcaption></figure>

## Types

```typescript
export type CreateOrderInput = {
  startAmount: BigInt
  endAmount: BigInt
  startTime: number
  endTime: number
  debtToSell: BigInt
}
```

```typescript
export enum OrderTypes {
  ASC = 'ASC',
  DESC = 'DESC',
  NONE = 'none'
}
```

### Signature

```typescript
export type Signature = {
  data: object
  signature: { v: number; r: string; s: string; deadline: number }
}

/* data object
interface SignMarket {
  loan: SignLoanConfig;
  assetId: string;
  collection: string;
  tokenId: BigInt;
  assetPrice: BigInt;
  assetLtv: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}

interface SignLoanConfig {
  loanId: string;
  aggLoanPrice: BigInt;
  aggLtv: BigInt;
  aggLiquidationThreshold: BigInt;
  totalAssets: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}
*/
```

### ClientOptions

```typescript
export type ClientOptions = {
  network?: Chain
}

export type Chain = 'mainnet' | 'sepolia' | 'localhost'
```

## Functions

### Creating a Market Item

To create a market item, use the `create` function from the `market` module:

```typescript
export const create = async (
        underlyingAsset: Address,
        orderType: OrderType,
        config: CreateOrderInput,
        signature: Signature,
        options?: ClientOptions
    )
```

```typescript
const result = await create(underlyingAsset, orderType, config, signature, options?);
```

Refer to the [Market Module](https://devs.unlockd.finance/modules/debt-tokens-5) documentation for more details on the `create` function.

### Canceling a Market Item

To cancel a market item, use the cancel function from the `market` module:

```typescript
export const cancel = async (
        orderId: string, 
        options?: ClientOptions
    )
```

```typescript
const result = await cancel(orderId, options?);
```

Refer to the Market Module documentation for more details on the `cancel` function.

### Bidding a Market Item

To bid on a market item, use the marketBid function from the `market` module:

```typescript
export const marketBid = async (
        orderId: string,
        amountToPay: BigInt,
        amountOfDebt: BigInt,
        signature: Signature,
        options?: ClientOptions
    )
```

```typescript
const result = await marketBid(orderId, amountToPay, amountOfDebt, signature, options?);
```

Refer to the [Market Module](https://devs.unlockd.finance/modules/debt-tokens-5) documentation for more details on the `marketBid` function.

### Claiming a Market Item

To claim a market item, use the `claim` function from the `market` module:

```typescript
export const claim = async (
        claimOnUWallet: boolean,
        orderId: string,
        signature: Signature,
        options?: ClientOptions
    )
```

```typescript
const result = await claim(claimOnUWallet, orderId, signature, options?);
```

Refer to the Market Module documentation for more details on the `claim` function.

### Cancel Claiming a Market Item

To cancel a claim on a market item, use the `cancelClaim` function from the `market` module:

```typescript
export const cancelClaim = async (
        orderId: string, 
        signature: Signature, 
        options?: ClientOptions
    )
```

```typescript
const result = await cancelClaim(orderId, signature, options?);
```

Refer to the Market Module documentation for more details on the `cancelClaim` function.

### Buy Now Market Item

To do a buy now on a market item, use the `buyNow` function from the `market` module:

```typescript
export const buyNow = async (
        claimOnUWallet: boolean,
        orderId: string,
        amountToPay: BigInt,
        amountOfDebt: BigInt,
        signature: Signature,
        options?: ClientOptions
    )
```

```typescript
const result = await buyNow(claimOnUWallet, orderId, amountToPay, amountOfDebt, signature, options?);
```

Refer to the Market Module documentation for more details on the `buyNow` function.


# Buy Now Module

The Buy Now module allows you to buy assets instantly at a fixed price.

<figure><img src="/files/XhuJX24BGKpsNsQHHXcj" alt=""><figcaption><p>BuyNow flow</p></figcaption></figure>

## Types

### Signature

```typescript
export type Signature = {
  data: object
  signature: { v: number; r: string; s: string; deadline: number }
}

/* data object
interface SignBuyNow {
  asset: SignAsset;
  assetLtv: BigInt;
  assetLiquidationThreshold: BigInt;
  from: string;
  to: string;
  data: string;
  value: BigInt;
  marketAdapter: string;
  marketApproval: string;
  marketPrice: BigInt;
  underlyingAsset: string;
  nonce: BigInt;
  deadline: BigInt;
}

interface SignLoanConfig {
  loanId: string;
  aggLoanPrice: BigInt;
  aggLtv: BigInt;
  aggLiquidationThreshold: BigInt;
  totalAssets: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}
*/
```

### ClientOptions

```typescript
export type ClientOptions = {
  network?: Chain
}

export type Chain = 'mainnet' | 'sepolia' | 'localhost'
```

## Functions

### Buying an Asset

To buy an asset instantly, use the `buy` function from the `buyNow` module:

```typescript
export const buy = async (
        amount: string, 
        signature: Signature, 
        options?: ClientOptions
    )
```

```typescript
const result = await buy(amount, signature, options?);
```

Refer to the Buy Now Module documentation for more details on the `buy` function.


# Sell Now Module

The Sell Now module allows you to sell assets instantly at a fixed price.

<figure><img src="/files/Qq4JaYP4OPLG7AyyjEn4" alt=""><figcaption><p>Sell flow</p></figcaption></figure>

## Types

### Signature

```typescript
export type Signature = {
  data: object
  signature: { v: number; r: string; s: string; deadline: number }
}

/* data object
interface SignSellNow {
  loan: SignLoanConfig;
  assetId: string;
  marketAdapter: string;
  marketApproval: string;
  marketPrice: BigInt;
  underlyingAsset: string;
  from: string;
  to: string;
  data: string;
  value: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}

interface SignLoanConfig {
  loanId: string;
  aggLoanPrice: BigInt;
  aggLtv: BigInt;
  aggLiquidationThreshold: BigInt;
  totalAssets: BigInt;
  nonce: BigInt;
  deadline: BigInt;
}
*/
```

### ClientOptions

```typescript
export type ClientOptions = {
  network?: Chain
}

export type Chain = 'mainnet' | 'sepolia' | 'localhost'
```

## Functions

### Selling an Asset

To sell an asset instantly, use the `sell` function from the `sellNow` module:

```typescript
export const sell = async (
        asset: Nft, 
        signature: Signature, 
        options?: ClientOptions
    )
```

```typescript
const result = await sell(asset, signature, options?);
```

Refer to the Sell Now Module documentation for more details on the `sell` function.


# Wallet Module

The Wallet module provides functionality for creating and managing Unlockd abstract wallets.

<figure><img src="/files/RUPIJpJ5vBZS1uvCIfwU" alt=""><figcaption><p>Wallet Flow</p></figcaption></figure>

## Types

### ClientOptions

```typescript
export type ClientOptions = {
  network?: Chain
}

export type Chain = 'mainnet' | 'sepolia' | 'localhost'
```

## Functions

### Creating a Wallet

To create an Unlockd abstract wallet, use the `createWallet` function from the `wallet` module:

```typescript
export const createWallet = async (
        options?: ClientOptions
    ):Promise<void>
```

```typescript
const result = await createWallet(options?);
```

Refer to the Wallet Module documentation for more details on the `createWallet` function.

### Retrieving the User's Wallet Address

To retrieve the wallet address of the user, use the `getWallet` function from the `wallet` module:

```typescript
export const getWallet = async (
        options?: ClientOptions
    ): Promise<any>
```

```typescript
const result = await getWallet(options?);
```

Refer to the Wallet Module documentation for more details on the `getWallet` function.


# NFT Batch Transfer Module

The NFT Batch Transfer module allows you to send multiple NFTs to the Unlockd user wallet in a single transaction.

<figure><img src="/files/jwXYYzFWPQ2pNHkER1Cs" alt=""><figcaption><p>NftBatchTransfer Flow</p></figcaption></figure>

## Types

### Assets

```typescript
export type Nft = {
  collection: string
  tokenId: string
}
```

### ClientOptions

```typescript
export type ClientOptions = {
  network?: Chain
}

export type Chain = 'mainnet' | 'sepolia' | 'localhost'
```

## Functions

### Sending NFTs to Wallet

To send NFTs to the Unlockd user wallet, use the `sendNftsToWallet` function from the `nftBatchTransfer` module:

```typescript
export const sendNftsToWallet = async (
        nfts: {
            contractAddress: string,
            tokenId: string
        }[], 
        options?: ClientOptions
    ): Promise<void>
```

```typescript
const result = await sendNftsToWallet(nfts[], signature, options?);
```

Refer to the NFT Batch Transfer Module documentation for more details on the `sendNftsToWallet` function.


# Code Examples

This section includes some code examples that demonstrate how to use the Unlockd SDK in different scenarios.


# Borrowing Assets

```typescript
import { UnlockdSDK } from '@unlockd/sdk';

const unlockd = new UnlockdSDK();

async function borrowAssets() {
  try {
    const token = await unlockd.authenticate();
    const amount = 1000000000000000000; // 1 ETH in wei
    const assets = [
      {
        collection: '0x1234567890123456789012345678901234567890',
        tokenId: '1',
      },
    ];
    const signature = {
      v: 28,
      r: '0x1234567890123456789012345678901234567890123456789012345678901234',
      s: '0x1234567890123456789012345678901234567890123456789012345678901234',
      deadline: 1649327432,
    };
    const result = await unlockd.action.borrow(token, amount, assets, signature);
    console.log('Borrow successful:', result);
  } catch (error) {
    console.error('Error:', error);
  }
}

borrowAssets();
```


# Fetch prices

```typescript
import { UnlockdApi} from "@unlockdfinance/unlockd-ts";

const api= new UnlockdApi()
 const params = {
            "nfts":
                [
                    {
                        "collection": "0x1750d2e6f2fb7fdd6a751833f55007cf76fbb358",
                        "tokenId": "10"
                    }
                ],
            "underlyingAsset": "0x7b79995e5f793a07bc00c21412e50ecae098e7f9"
        }
const response = await api.prices(params.nfts, params.underlyingAsset)

```


# Login

```typescript
import { UnlockdApi} from "@unlockdfinance/unlockd-ts";

const api= new UnlockdApi()
const address='0x0000000000000000000000000000000000000000'
const message = await api.signatureMessage(address)
//Sign message from your wallet o with Pk
//...
const signedMessage='...'
const authToken = await api.validateMessage(signedMessage)
```


# Borrow Signature

```typescript
import {ActionRequest, UnlockdApi} from "@unlockdfinance/unlockd-ts";
const api= new UnlockdApi()

const params: ActionRequest = {
  loanId: '0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0',//Optional
  nfts: [{collection: '0x1234567890abcdefABCDEF1234567890abcdefAB', tokenId: 'testTokenId'}]//Optional 
}
const authToken = await api.borrowSignature(authToken, params)
```


# Minimum Repay

```typescript
import {minimumToRepay} from "@unlockdfinance/unlockd-ts";

const params = {
    initialLoans: [
        {
            valuation: 10000000n,
            ltv: 5000n,
        },
        {
            valuation: 10000000n,
            ltv: 5000n,
        },
    ],
    indicesToDelete: [1],
    totalDebt: 2n,
}
const result = minimumToRepay(params)
```


# Liquidation Price

```typescript
import {liquidationPrice} from "@unlockdfinance/unlockd-ts";
const oneEth = BigInt(1e18)

const params = {
    debt: (BigInt(50) * oneEth),
    liquidationThreshold: BigInt(8500)
}
const result = liquidationPrice(params)
```


# Health Factor

```typescript
import {healthFactor} from "@unlockdfinance/unlockd-ts";
const oneEth = BigInt(1e18)

const params = {
  collateral: (BigInt(100) * oneEth),
  debt: (BigInt(50) * oneEth),
  liquidationThreshold: BigInt(8500)
}
const result = healthFactor(params)
```


# Available to borrow

```typescript
import {availableToBorrow} from "@unlockdfinance/unlockd-ts";

const nfts = [
  {
    valuation: (BigInt(100) * oneEth).toString(),
    ltv: '5000'
  },
  {
    valuation: (BigInt(50) * oneEth).toString(),
    ltv: '3000'
  }
]

const result = availableToBorrow(nfts)
```


# UI Snippets

{% hint style="info" %}
Unlockd is excited to announce that plug-and-play, pre-configured front-end UI snippets **will be available soon**. These UI snippets are designed to make it easier for developers to integrate Unlockd's borrowing functionalities into their applications quickly and efficiently.

### Utility

The pre-configured UI snippets will provide ready-made components for common borrowing tasks such as collateralization, loan origination, BuyNowPayLater and repayment processes. This will significantly reduce development time and effort, allowing you to focus on building and enhancing your application's core features. These snippets are designed to seamlessly interact with the Unlockd SDK, ensuring smooth and efficient integration.

### Customizability

Understanding the importance of maintaining your brand identity, the UI snippets will be fully customizable. You will be able to tailor the look and feel of these components to match your application's branding perfectly. This ensures a cohesive user experience that aligns with your brand’s aesthetic and functional requirements.
{% endhint %}


# Reservoir Integration

**Unlockd** uses **Reservoir** to enhance its liquidation process for RWAs and NFTs. [Reservoir](https://docs.reservoir.tools/docs/how-to-get-distribution-for-your-protocol-using-reservoir) provides a comprehensive aggregation of liquidity from various marketplaces, ensuring that Unlockd can liquidate assets quickly and efficiently at the best available price. By utilizing Reservoir's infrastructure, Unlockd benefits from:

* **Liquidity Aggregation**: Access to aggregated bids and listings from multiple major marketplaces.
* **Upgrade Protection**: Automatic updates and new features from Reservoir without additional integration work.
* **Order Distribution**: Ability to distribute and cross-post orders across various marketplaces, maximizing exposure and liquidity.

This integration allows Unlockd to manage liquidations effectively, maintaining the value and efficiency of its protocol while ensuring debt recovery and minimizing risks of bad debt​.

Learn more about [Unlockd liquidation process](https://docs.unlockd.finance/protocol-mechanics/liquidations/liquidation-process) and the [role of Reservoir](https://docs.unlockd.finance/protocol-mechanics/liquidations/external-liquidation-gateways) in our Documentation.

## Maximizing SDK-Based Integration Performance

For any project with assets supported on Unlockd, particularly if you are building liquidity features using our SDK, **integrating your protocol with Reservoir is highly recommended** for optimal performance and efficiency.&#x20;

Reservoir aggregates liquidity from multiple marketplaces, ensuring assets are liquidated at the best prices. This enhances the efficiency of Unlockd’s liquidation process, maximizing returns and minimizing undervalued sales.&#x20;

By integrating their assets with Reservoir, partners ensure that when these assets are whitelisted as collateral, the Unlockd protocol can connect to Reservoir’s liquidity. This connection ensures the entire system works smoothly when integrating via the SDK.

But there's more:

## Benefits for RWA Issuers and Marketplaces

Even independently of Unlockd, integrating Reservoir offers significant advantages for RWA issuers and marketplaces:

* For RWA issuers, integrating with Reservoir allows access to a broad network of buyers, enhancing market reach and asset visibility. Streamlined management of listings and bids through Reservoir’s infrastructure simplifies operations and increases asset liquidity and value.
* For marketplaces, integrating with Reservoir ensures access to aggregated liquidity from various sources, maximizing exposure and sale opportunities. This broadens the market for listed assets, ensuring they are sold efficiently and at the best possible prices, benefiting both the marketplace and its users.


# Borrow against RWAs

{% hint style="info" %}
This guide provides an initial overview of using the Unlockd SDK for borrowing against RWAs functionalities. As we're in the early stages of documentation, your feedback is crucial. We encourage developers to explore this use case and help shape these materials.&#x20;

We're committed to supporting your integration journey. If you need personalized assistance or have suggestions, please [reach out to us directly](/resources/support-and-contact#support). Your input will help us create more comprehensive and useful documentation for the developer community.
{% endhint %}

## Overview

Borrowing against Real-World Assets (RWAs) is a powerful feature of the Unlockd protocol. By leveraging the Unlockd SDK, developers can enable users to collateralize their tokenized assets and obtain instant loans.&#x20;

This feature can be applied to loans with a single asset or even a portfolio of assets from different classes.&#x20;

If your product allows users to access an inventory or portfolio, you can natively display their Available To Borrow amount and allow them to bundle all items into a single or different loans without leaving your interface

## How-To Guide

This guide will walk you through the process of implementing a borrowing feature using the Unlockd SDK, allowing users to borrow against RWAs.

### Prerequisites

Before you begin, ensure you have the following set up:

* A React-based project
* The Unlockd SDK installed (`@verislabs/unlockd-sdk`)
* Wagmi library and its dependencies (viem, react-query) for Ethereum interactions. You can [Create a Project](https://wagmi.sh/react/getting-started#automatic-installation) or [Install it manually into your Project](https://wagmi.sh/react/getting-started#manual-installation).
* Basic understanding of React hooks and Ethereum transactions

{% hint style="info" %}
This example is written in TypeScript and uses the wagmi library along with its dependencies. However, it's important to note that wagmi is not required to use the Unlockd SDK. The SDK itself is only dependent on viem.

Developers are free to use their preferred tools and libraries for wallet connection and blockchain interactions. This example serves as one possible implementation approach.
{% endhint %}

### Implementation Steps

1. Set up the React component with necessary state variables and hooks.&#x20;
2. Implement login, connection and authentication
3. Fetch NFTs and prices from connected wallets.&#x20;
4. Create or refresh Unlockd wallet.&#x20;
5. Implement NFT selection functionality.&#x20;
6. Implement NFT approval and transfer to Unlockd wallet.&#x20;
7. Set up borrowing functionality against selected NFTs.&#x20;
8. Render the user interface with all interactive elements.

### Code Overview

#### 1. Set up the React component

In your component, first set up all required state variables (using useState, Context, or your own state manager) and wagmi hooks. This includes states for managing user accounts, NFT selections, approval statuses, and other essential data. For the Unlockd SDK you only need to initialize the api object.

```typescript
"use client";

import { useState, useEffect } from "react";
import {
    useAccount,
    useConnect,
    useDisconnect,
    useConfig,
    useWriteContract,
    useReadContracts,
} from "wagmi";
import {
    signMessage,
    readContract,
    writeContract,
    waitForTransactionReceipt,
    multicall,
    getBalance,
} from "wagmi/actions";
import { sepolia } from "wagmi/chains";
import { injected } from "wagmi/connectors";
import { parseUnits, Address, isAddress } from "viem";
import {
    UnlockdApi,
    Chains,
    UnderlyingsAsset,
    borrow,
    underlyingsAssets,
    createWallet,
    getWallet,
} from "@verislabs/unlockd-sdk";
import { nftAbi } from "../../abis/ERC721Abi";
import { nftBatchTransferAbi } from "../../abis/NftBatchTransferAbi";

interface NFTPriceData {
    collection: Address;
    tokenId: string;
    underlyingAsset: Address;
    price?: string;
}

const ALLOWED_COLLECTIONS: Address[] = [
    "0xa6a9acfdd1f64ec324ee936344cdb1457bdbddf0",
    "0x388043e55a388e07a75e9a1412fe2d64e48343a5",
];

const nftBatchTransferAddress =
    "0xaba905eba39b9a55fd0f910a6415ba91c3e9353d" as Address;

// The idea is to create a simple example of how to use the Unlockd SDK to borrow against NFTs.
// The user can connect their wallet, log in to Unlockd, approve a collection, select NFTs to transfer to their Unlockd wallet, and borrow against them.
// We are not demonstrating frontend code, or handling states, just the logic to interact with the Unlockd SDK and create a borrow transaction.
function App() {
    // Variables
    const userAccount = useAccount();
    const { connect, connectors } = useConnect();
    const { disconnect } = useDisconnect();
    const config = useConfig();

    const [unlockdAccount, setUnlockdAccount] = useState<Address>();
    const [token, setToken] = useState<string | null>(null);
    const [selectedNFTs, setSelectedNFTs] = useState<NFTPriceData[]>([]);
    const [selectedUnderlyingAsset, setSelectedUnderlyingAsset] =
        useState<Address>(underlyingsAssets(Chains.Sepolia)[UnderlyingsAsset.USDC]);
    const [approvalStatus, setApprovalStatus] = useState<
        Record<Address, "not-approved" | "approved" | "pending">
    >({});
    const [injectedWalletNFTs, setInjectedWalletNFTs] = useState<NFTPriceData[]>(
        [],
    );
    const [unlockdWalletNFTs, setUnlockdWalletNFTs] = useState<NFTPriceData[]>(
        [],
    );
    const [selectedUnlockdNFTs, setSelectedUnlockdNFTs] = useState<
        NFTPriceData[]
    >([]);
    const [borrowAmount, setBorrowAmount] = useState<string>("");

    const api = new UnlockdApi(Chains.Sepolia);
```

{% hint style="info" %}
In this example, all the code is contained within the root component (App) for simplicity. However, this functionality can be implemented in any React component.

The hooks used (`useAccount`, `useDisconnect`, etc.) are imported from wagmi, while `useState` is from React. These reactive variables can be stored and managed using any state management solution you prefer.

It's important to note the initialization of the Unlockd SDK, which is a crucial step:

<pre class="language-typescript"><code class="lang-typescript"><strong>const api = new UnlockdApi(Chains.Sepolia);
</strong></code></pre>

This line, found at the end of the component, sets up the Unlockd API instance for interacting with the Sepolia testnet. Make sure to initialize the SDK appropriately in your implementation.
{% endhint %}

#### 2. Implement login, connection and authentication

Set up functionality authenticating with Unlockd. This step calls the Unlockd SDK for obtaining an authentication token for the user's currently connected address by signing a blockchain message. The auth token is essential for user's interaction with the Unlockd SDK

```typescript
    // The login should call the unlockd API using the signatureMessage method to get the message to sign.
    // We get the message to sign, and then we trigger it to the user so he can sign it (signMessage).
    // After the user signs the message, we validate it using the validateMessage method, and returning the auth token we will need to interact with the protocol.
    const logIn = async () => {
        if (!userAccount.address) {
            return;
        }
        try {
            const message = await api.signatureMessage(userAccount.address);
            const signature = await signMessage(config, {
                account: userAccount.address,
                message: message.message,
            });
            const authToken = await api.validateMessage(
                userAccount.address,
                signature,
            );
            setToken(authToken.token);
        } catch (error) {
            console.error("Login failed:", error);
        }
    };
```

#### 3. Fetch and display NFTs from connected wallets.

Implement logic to fetch NFTs (RWAs) from both the user's connected wallet and their Unlockd wallet, if the wallet was created before (see [#4](#id-4.-create-or-refresh-unlockd-wallet) below).&#x20;

This involves querying blockchain data for NFT balances and token IDs, then passing this data to the Unlockd API to retrive price data for all NFTs. In this example, we are quering the blockchain directly to obtain the NFTs data (using the multicall action from wagmi for efficiency) but you can obtain this data from any means (such as using the Alchemy SDK).&#x20;

What you need to be aware of is the data structure expected by the Unlockd API when calling the prices method, as see in the priceParams.

```typescript
    // In order to borrow, we 1st should check if the user has any Allowed Collections in his wallet. If yes: we shouldt get the balance of it, and then get the token ID of each NFT in order to provide the price. If no: it ends here.
    // The fetchNftsAndPrices should be called from the injected wallet and the unlockd wallet, since both will hold assets.
    const fetchNFTsAndPrices = async (
        walletAddress: Address,
        selectedUnderlyingAsset: Address,
        setNFTs: React.Dispatch<React.SetStateAction<NFTPriceData[]>>,
    ) => {
        if (!walletAddress || !isAddress(walletAddress)) {
            console.error(`Invalid wallet address: ${walletAddress}`);
            return;
        }

        if (!selectedUnderlyingAsset || !isAddress(selectedUnderlyingAsset)) {
            console.error(
                `Invalid selected underlying asset: ${selectedUnderlyingAsset}`,
            );
            return;
        }

        try {
            const balanceContracts = ALLOWED_COLLECTIONS.map((collection) => ({
                address: collection,
                abi: nftAbi.abi,
                functionName: "balanceOf",
                args: [walletAddress],
            } as const));

            const balances = await multicall(config, { contracts: balanceContracts });

            const tokenIdContracts = balances.flatMap((balance, index) => {
                if (balance.status !== 'success') return [];

                const numTokens = Number(balance.result || 0);
                if (numTokens === 0) return [];

                return Array.from({ length: numTokens }, (_, i) => ({
                    address: ALLOWED_COLLECTIONS[index],
                    abi: nftAbi.abi,
                    functionName: "tokenOfOwnerByIndex",
                    args: [walletAddress, BigInt(i)],
                } as const));
            });

            const tokenIds = await multicall(config, { contracts: tokenIdContracts });

            const priceParams = tokenIds
                .filter((tokenIdResult) => tokenIdResult.status === 'success')
                .map((tokenIdResult, index) => ({
                    collection: tokenIdContracts[index].address,
                    tokenId: tokenIdResult.result.toString(),
                    underlyingAsset: selectedUnderlyingAsset,
                }));
            const pricesResponse = await api.prices(priceParams);

            const nftsPriceData = pricesResponse.map((result) => ({
                collection: result.collection as Address,
                tokenId: result.tokenId,
                underlyingAsset: selectedUnderlyingAsset,
                price: result.valuation,
            }));

            setNFTs(nftsPriceData);
        } catch (error) {
            console.error(
                `Error fetching NFTs and prices for wallet ${walletAddress}:`,
                error,
            );
        }
    };
```

{% hint style="info" %}
For each NFT, you need to create an object with the following structure:&#x20;

<pre class="language-typescript"><code class="lang-typescript">{ 
    collection: string, // NFT's collection address 
    tokenId: string, // NFT's ERC721 token id 
<strong>    underlyingAsset: string // the asset ETH or USDC you want to receive the price evaluation in 
</strong>} 
</code></pre>

Form an array with similar objects containing data about all the NFTs you want to evaluate, and pass it to the `prices()` method.
{% endhint %}

#### 4. Create or retrieve Unlockd wallet

Set up functionality to create a new Unlockd wallet (Unlockd Account) if the user doesn't have one, or refresh an existing one. This step is crucial for storing the assets that will be used as collateral for borrowing.

```typescript
    // In order to create a loan, you will need to own an unlockd wallet.
    // The assets (nfts) will be locked in your unlockd wallet.
    // If you don't have an unlockd wallet, it will be created.
    const createOrRefreshUnlockdWallet = async () => {
        try {
            let unlockdWallet = await getWallet({ network: Chains.Sepolia });
            if (!unlockdWallet) {
                await createWallet({ network: Chains.Sepolia });
                unlockdWallet = await getWallet({ network: Chains.Sepolia });
            }
            if (unlockdWallet) {
                setUnlockdAccount(unlockdWallet);
                await fetchNFTsAndPrices(unlockdWallet, selectedUnderlyingAsset, setUnlockdWalletNFTs);
            }
        } catch (error) {
            console.error("Error with Unlockd wallet:", error);
        }
    };
```

#### 5. Implement NFT selection functionality

Create a mechanism for users to select one or multiple NFTs, either for transferring to the Unlockd wallet or for using as collateral when borrowing. This function is reused in selecting NFTs in both the user's wallet and the Unlockd wallet.

```typescript
    // Allows the user to select 1 or multiple NFTs to transfer to the unlockd wallet or to borrow agains them, if used from the unlockd wallet.
    const toggleNFTSelection = (nft: NFTPriceData, isUnlockdWallet: boolean) => {
        const setNFTs = isUnlockdWallet ? setSelectedUnlockdNFTs : setSelectedNFTs;
        setNFTs((prev) =>
            prev.some(
                (item) =>
                    item.collection === nft.collection && item.tokenId === nft.tokenId,
            )
                ? prev.filter(
                    (item) =>
                        !(
                            item.collection === nft.collection &&
                            item.tokenId === nft.tokenId
                        ),
                )
                : [...prev, nft],
        );
    };
```

#### 6. Implement NFT approval and transfer to Unlockd wallet

Set up the process for approving NFT collections and transferring selected NFTs to the Unlockd wallet. This includes checking the current approval status for all selected NFTs, sending approval transactions for each collection, and transferring the selected NFTs in batch to the Unlockd wallet.

```typescript
    // This will validate if the user has approved the BatchNFTTransfer contract to transfer the NFTs from his injected wallet to the unlockd wallet.
    // If the approvals exist you dont need to approve again. If not, the user SHOULD APPROVE.
    // The SDK should provide a function to approve!
    const handleApprove = async (collectionAddress: Address) => {
        if (!userAccount.address) {
            return;
        }

        try {
            const isApproved = await readContract(config, {
                address: collectionAddress,
                abi: nftAbi.abi,
                functionName: "isApprovedForAll",
                args: [userAccount.address, nftBatchTransferAddress],
            });

            if (isApproved) {
                setApprovalStatus((prev) => ({
                    ...prev,
                    [collectionAddress]: "approved",
                }));
                return;
            }

            const response = await writeContract(config, {
                address: collectionAddress,
                abi: nftAbi.abi,
                functionName: "setApprovalForAll",
                args: [nftBatchTransferAddress, true],
            });

            setApprovalStatus((prev) => ({
                ...prev,
                [collectionAddress]: "pending",
            }));

            const transactionReceipt = await waitForTransactionReceipt(config, {
                hash: response,
            });

            if (transactionReceipt.status === "success") {
                setApprovalStatus((prev) => ({
                    ...prev,
                    [collectionAddress]: "approved",
                }));
            } else {
                setApprovalStatus((prev) => ({
                    ...prev,
                    [collectionAddress]: "not-approved",
                }));
            }
        } catch (error) {
            console.error("Approval failed:", error);
            setApprovalStatus((prev) => ({
                ...prev,
                [collectionAddress]: "not-approved",
            }));
        }
    };

    const { writeContract: batchTransfer } = useWriteContract();

    // This function will transfer the selected NFTs from the injected wallet to the unlockd wallet.
    // The approve of this contract and its usage is not mandatory.
    // The important is the the unlockd wallet has the NFTs.
    // This contract was developed and audited by us, so we use it, feel free to use it, or change to other preferred method.
    const handleBatchTransfer = async () => {
        if (!userAccount.address || !unlockdAccount || selectedNFTs.length === 0)
            return;
        try {
            const nftTransfers = selectedNFTs.map((nft) => ({
                contractAddress: nft.collection,
                tokenId: BigInt(nft.tokenId),
            }));

            batchTransfer({
                address: nftBatchTransferAddress,
                abi: nftBatchTransferAbi.abi,
                functionName: "batchTransferFrom",
                args: [nftTransfers, unlockdAccount],
            });

            await fetchNFTsAndPrices(userAccount.address, selectedUnderlyingAsset, setInjectedWalletNFTs);
            await fetchNFTsAndPrices(unlockdAccount, selectedUnderlyingAsset, setUnlockdWalletNFTs);
        } catch (error) {
            console.error("Batch transfer failed:", error);
        }
    };

```

#### 7. Set up borrowing functionality against selected NFTs.

Implement the core borrowing feature of the Unlockd protocol. This includes selecting NFTs from the Unlockd wallet, specifying borrow amounts, obtaining borrow signatures, and executing borrow transactions.

{% hint style="info" %}
This example demonstrates how all the previously described functionalities are implemented within a single component for simplicity. However, this is just one approach. Feel free to adapt and restructure this implementation to best suit your own application's architecture and requirements.
{% endhint %}

```typescript
    // This function will create a borrow transaction.
    // The user should select the NFTs he wants to borrow against (limit: 100), and the amount he wants to borrow.
    // If the user has an unlockd wallet with allowed NFTs, he can borrow against them.
    // We 1st generate an array of nfts (collection and tokenId), after, we build the params we need to call the backend for a borrow signature, by providing the nfts and the underlying asset.
    // The backend will return a signature, and we will use it to call the borrow function from the SDK, adding the amount, nfts and signature.
    // we use 6 decimals since the borrow amount is in USDC.
    const handleBorrow = async () => {
        if (selectedUnlockdNFTs.length === 0 || !token || !unlockdAccount) {
            return;
        }

        try {
            const nftsArray = selectedUnlockdNFTs.map((nft) => ({
                collection: nft.collection,
                tokenId: nft.tokenId,
            }));

            const params = {
                nfts: nftsArray,
                underlyingAsset: selectedUnderlyingAsset,
            };

            const signature = await api.borrowSignature(token, params);

            await borrow(BigInt(parseUnits(borrowAmount, 6)), nftsArray, signature, {
                network: Chains.Sepolia,
            });

            await fetchNFTsAndPrices(unlockdAccount, selectedUnderlyingAsset, setUnlockdWalletNFTs);
        } catch (error) {
            console.error("Borrow failed:", error);
        }
    };

    useEffect(() => {
        if (
            userAccount.isConnected &&
            userAccount.address &&
            isAddress(userAccount.address)
        ) {
            fetchNFTsAndPrices(userAccount.address, selectedUnderlyingAsset, setInjectedWalletNFTs);
        }
    }, [userAccount.isConnected, userAccount.address, selectedUnderlyingAsset]);

    useEffect(() => {
        if (token) {
            createOrRefreshUnlockdWallet();
        }
    }, [token]);

    const isAuth = userAccount.isConnected && !!token;
```

#### 8. Render the user interface with all interactive elements

Create and render the user interface components that tie all the functionality together. This includes elements for wallet connection, NFT selection, approval buttons, transfer initiation, borrow amount input, and the borrow action button.

```typescript
    return (
        <div>
            <h1>Unlockd Borrow Example</h1>

            {/* Connect wallet */}
            {(() => {
                if (!userAccount.isConnected) {
                    return (
                        <div>
                            <button onClick={() => connect({ connector: injected() })}>
                                Connect
                            </button>
                        </div>
                    );
                }

                return (
                    <div>
                        <p>Connected to {userAccount.address}</p>
                        <button onClick={() => disconnect()}>Disconnect</button>
                    </div>
                );
            })()}

            {/* Check current network */}
            {(() => {
                if (userAccount.isConnected && userAccount.chainId !== sepolia.id) {
                    return (
                        <div>
                            <p>Please switch to Sepolia network</p>
                        </div>
                    );
                }
                return null;
            })()}

            {/* Unlockd Log In */}
            {(() => {
                if (userAccount.isConnected && !isAuth) {
                    return (
                        <div>
                            <button onClick={logIn}>Log In to Unlockd</button>
                        </div>
                    );
                }
                return null;
            })()}

            {/* Select asset */}
            {(() => {
                if (isAuth) {
                    return (
                        <select
                            value={selectedUnderlyingAsset}
                            onChange={(e) =>
                                setSelectedUnderlyingAsset(e.target.value as Address)
                            }
                        >
                            {Object.entries(UnderlyingsAsset).map(([key, value]) => (
                                <option
                                    key={key}
                                    value={underlyingsAssets(Chains.Sepolia)[value]}
                                >
                                    {key}
                                </option>
                            ))}
                        </select>
                    );
                }
            })()}

            {/* Display collections */}
            {(() => {
                if (isAuth) {
                    return (
                        <>
                            {ALLOWED_COLLECTIONS.map((collectionAddress) => (
                                <div key={collectionAddress}>
                                    <h3>Collection: {collectionAddress}</h3>
                                    <button
                                        onClick={() => handleApprove(collectionAddress)}
                                        disabled={
                                            approvalStatus[collectionAddress] === "approved" ||
                                            approvalStatus[collectionAddress] === "pending"
                                        }
                                    >
                                        {approvalStatus[collectionAddress] === "approved"
                                            ? "Approved"
                                            : approvalStatus[collectionAddress] === "pending"
                                                ? "Approving..."
                                                : "Approve Collection"}
                                    </button>
                                    <p>
                                        Total NFTs in this collection:{" "}
                                        {
                                            injectedWalletNFTs.filter(
                                                (nft) => nft.collection === collectionAddress,
                                            ).length
                                        }
                                    </p>

                                    {injectedWalletNFTs
                                        .filter((nft) => nft.collection === collectionAddress)
                                        .map((nft) => (
                                            <div key={`${nft.collection}-${nft.tokenId}`}>
                                                <input
                                                    type="checkbox"
                                                    checked={selectedNFTs.some(
                                                        (item) =>
                                                            item.collection === nft.collection &&
                                                            item.tokenId === nft.tokenId,
                                                    )}
                                                    onChange={() => toggleNFTSelection(nft, false)}
                                                />
                                                <span>Token ID: {nft.tokenId}</span>
                                                <span>Price: {nft.price}</span>
                                            </div>
                                        ))}
                                    {injectedWalletNFTs.filter(
                                        (nft) => nft.collection === collectionAddress,
                                    ).length === 0 && <p>No NFTs found in this collection</p>}
                                </div>
                            ))}
                        </>
                    );
                }
            })()}

            {/* Transfer NFTs to Unlockd wallet */}
            {(() => {
                if (isAuth) {
                    return (
                        <button
                            onClick={handleBatchTransfer}
                            disabled={selectedNFTs.length === 0 || !unlockdAccount}
                        >
                            Transfer Selected NFTs to Unlockd Wallet
                        </button>
                    );
                }
            })()}

            {/* Create & display Unlockd wallet and the supported collections */}
            {(() => {
                if (isAuth) {
                    return (
                        <>
                            <h2>Unlockd Wallet</h2>
                            <p>Unlockd Wallet: {unlockdAccount || "Not created"}</p>
                            <button onClick={createOrRefreshUnlockdWallet}>
                                Create/Refresh Unlockd Wallet
                            </button>
                            {unlockdAccount && (
                                <>
                                    <h3>NFTs in Unlockd Wallet</h3>
                                    {unlockdWalletNFTs.map((nft) => (
                                        <div key={`${nft.collection}-${nft.tokenId}`}>
                                            <input
                                                type="checkbox"
                                                checked={selectedUnlockdNFTs.some(
                                                    (item) =>
                                                        item.collection === nft.collection &&
                                                        item.tokenId === nft.tokenId,
                                                )}
                                                onChange={() => toggleNFTSelection(nft, true)}
                                            />
                                            <span>Collection: {nft.collection}</span>
                                            <span>Token ID: {nft.tokenId}</span>
                                            <span>Price: {nft.price}</span>
                                        </div>
                                    ))}

                                    <div>
                                        <input
                                            type="text"
                                            value={borrowAmount}
                                            onChange={(e) => setBorrowAmount(e.target.value)}
                                            placeholder="Borrow Amount"
                                        />
                                        <button
                                            onClick={handleBorrow}
                                            disabled={
                                                selectedUnlockdNFTs.length === 0 || !borrowAmount
                                            }
                                        >
                                            Borrow Against Selected NFT
                                        </button>
                                    </div>
                                </>
                            )}
                        </>
                    );
                }
            })()}
        </div>
    );
}

export default App
```


# 'Buy Now, Pay Later' financing

{% hint style="info" %}
This guide provides an initial overview of using the Unlockd SDK for the 'Buy Now, Pay Later' functionality. As we're in the early stages of documentation, your feedback is crucial. We encourage developers to explore this use case and help shape these materials.&#x20;

We're committed to supporting your integration journey. If you need personalized assistance or have suggestions, please [reach out to us directly](/resources/support-and-contact#support). Your input will help us create more comprehensive and useful documentation for the developer community.
{% endhint %}

## Overview

'Buy Now, Pay Later' (BNPL) financing is a payment solution that allows users to purchase tokenized assets immediately while deferring payment indefinitely (thanks to Unlockd's open-ended loans).

&#x20;By integrating BNPL functionalities using the Unlockd SDK, developers can provide a seamless and flexible financing option directly at the point of sale, backed by Real-World Assets.

### How BNPL Works

1. **User Selection**: During the checkout process, users select the 'Buy Now, Pay Later' option as their preferred payment method.
2. **Risk Assessment**: Unlockd conducts a real-time appraisal of the user's tokenized assets to determine their value. This appraisal helps in setting an appropriate Loan-to-Value (LTV) ratio for the loan.
3. **Down Payment Selection**: Users are required to select a down payment amount, which may sometimes be a minimum required amount based on the value of the asset and the LTV ratio.
4. **Loan Origination**: Once the down payment is made, a loan is originated on the Unlockd protocol. The user's tokenized assets are used as collateral to secure the loan.
5. **Instant Payment**: The merchant receives the full payment for the asset immediately from Unlockd's liquidity pool, ensuring they do not bear any financial risk.
6. **Open-Ended Loan**: The loan remains open-ended, allowing the user to repay at their convenience as long as the health factor of their collateralized asset remains within acceptable limits. There are no fixed repayment schedules.
7. **Collateral Management**: The user’s collateralized assets are held securely in their Unlockd Account. These assets remain in the user’s custody and ownership, with Unlockd intervening only to prevent unauthorized transactions during the loan period.
8. **Health Factor Monitoring**: Users need to maintain the health factor of their loan, which is continuously monitored. If the health factor drops below a certain threshold, the user may need to add more collateral or repay part of the loan to restore the balance.
9. **Completion**: Once the loan is fully repaid, the collateralized assets are released back to the user without any restrictions.

### Benefits of BNPL with Unlockd

* **Seamless Integration**: Easily incorporate BNPL options into existing checkout flows using the Unlockd SDK.
* **Enhanced Flexibility**: Users enjoy the flexibility of deferred payments without immediate financial burden.
* **Improved Cash Flow for Merchants**: Merchants receive full payment upfront, improving their cash flow and reducing financial risk.
* **Lower Risk**: The use of tokenized assets as collateral reduces the risk for both users and the protocol, enabling better loan terms and interest rates.

## How-To Guide

This guide will walk you through the process of implementing a borrowing feature using the Unlockd SDK, allowing users to borrow against RWAs.

### Prerequisites <a href="#prerequisites" id="prerequisites"></a>

Before you begin, ensure you have the following set up:

* A React-based project
* The Unlockd SDK installed (`@verislabs/unlockd-sdk`)
* Wagmi library and its dependencies (viem, react-query) for Ethereum interactions. You can [Create a Project](https://wagmi.sh/react/getting-started#automatic-installation) or [Install it manually into your Project](https://wagmi.sh/react/getting-started#manual-installation).
* Basic understanding of React hooks and Ethereum transactions

{% hint style="info" %}
This example is written in TypeScript and uses the wagmi library along with its dependencies. However, it's important to note that wagmi is not required to use the Unlockd SDK. The SDK itself is only dependent on viem.

Developers are free to use their preferred tools and libraries for wallet connection and blockchain interactions. This example serves as one possible implementation approach.
{% endhint %}

### Implementation Steps <a href="#implementation-steps" id="implementation-steps"></a>

1. Set up the React component with necessary state variables and hooks.&#x20;
2. Implement login, connection and authentication
3. Create or refresh Unlockd wallet (Unlockd Account).&#x20;
4. Implement BNPL transaction execution.&#x20;
5. Render the user interface with all interactive elements.

### Code Overview

{% hint style="warning" %}
Implementing the Buy Now, Pay Later (buyNow in the SDK) functionality in a testnet environment is more complex due to the following reasons:

1. Two-wallet setup: You need one wallet to mint and list an NFT for sale on <https://testnets.reservoir.tools/sepolia>, and a second wallet to execute the purchase.
2. Testnet limitations: Reservoir does not allow token swaps on testnets, including Sepolia. This affects how BNPL transactions are structured in the test environment.
3. Currency differences: In the testnet implementation, we use WETH. However, on mainnet, USDC is typically used for RWA purchases.

These factors make recreating a full BNPL flow on testnet more challenging. Keep these differences in mind when testing and developing your BNPL integration, as the actual mainnet implementation will differ.

If you need any help, [contact us](/resources/support-and-contact#support). We actually like being bothered.
{% endhint %}

#### 1. Set up the React component with necessary state variables and hooks&#x20;

Initialize the main App component and set up all required state variables using React hooks. This includes states for managing user accounts, selected assets, and BNPL transaction details.

```typescript
"use client";

import { useState, useEffect } from "react";
import {
    useAccount,
    useConnect,
    useDisconnect,
    useConfig,
    useWriteContract,
} from "wagmi";
import {
    signMessage,
    readContract,
    writeContract,
    waitForTransactionReceipt,
} from "wagmi/actions";
import { sepolia } from "wagmi/chains";
import { injected } from "wagmi/connectors";
import { parseUnits, Address, isAddress } from "viem";
import {
    UnlockdApi,
    Chains,
    UnderlyingsAsset,
    borrow,
    underlyingsAssets,
    createWallet,
    getWallet,
} from "@verislabs/unlockd-sdk";

interface NFTPriceData {
    collection: Address;
    tokenId: string;
    underlyingAsset: Address;
    price?: string;
}

const ALLOWED_COLLECTIONS: Address[] = [
    "0xa6a9acfdd1f64ec324ee936344cdb1457bdbddf0",
    "0x388043e55a388e07a75e9a1412fe2d64e48343a5",
];

// Choose the asset I want
// Validate if possible to buy from reservoir
// If yes: check if the user has a unlockdWallet: if not, create one, if yes: continue.
// Validate the user amount, the selected NFT, and get the buynow signature.
// BuyNow call getCalculations with the signature and the (minAmount,)
// BuyNow

function App() {
    // Variables
    const userAccount = useAccount();
    const { connect, connectors } = useConnect();
    const { disconnect } = useDisconnect();
    const config = useConfig();

    const [unlockdAccount, setUnlockdAccount] = useState<Address>();
    const [token, setToken] = useState<string | null>(null);
    const [selectedUnderlyingAsset, setSelectedUnderlyingAsset] =
        useState<Address>(underlyingsAssets(Chains.Sepolia)[UnderlyingsAsset.USDC]);
    const [selectedUnlockdNFTs, setSelectedUnlockdNFTs] = useState<
        NFTPriceData[]
    >([]);
    const [borrowAmount, setBorrowAmount] = useState<string>("");
    const [collection, setCollection] = useState<string>("");
    const [tokenId, setTokenId] = useState<number>(0);
    const [amount, setAmount] = useState<number>(0);

    const api = new UnlockdApi(Chains.Sepolia);
```

{% hint style="info" %}
In this example, all the code is contained within the root component (App) for simplicity. However, this functionality can be implemented in any React component.

The hooks used (`useAccount`, `useDisconnect`, etc.) are imported from wagmi, while `useState` is from React. These reactive variables can be stored and managed using any state management solution you prefer.

It's important to note the initialization of the Unlockd SDK, which is a crucial step:

```typescript
typescriptCopyconst api = new UnlockdApi(Chains.Sepolia);
```

This line, found at the end of the component, sets up the Unlockd API instance for interacting with the Sepolia testnet. Make sure to initialize the SDK appropriately in your implementation.
{% endhint %}

#### 2. Implement login, connection and authentication

Set up functionality authenticating with Unlockd. This step calls the Unlockd SDK for obtaining an authentication token for the user's currently connected address by signing a blockchain message. The auth token is essential for user's interaction with the Unlockd SDK

```typescript
    // The login should call the unlockd API using the signatureMessage method to get the message to sign.
    // We get the message to sign, and then we trigger it to the user so he can sign it (signMessage).
    // After the user signs the message, we validate it using the validateMessage method, and returning the auth token we will need to interact with the protocol.
    const logIn = async () => {
        if (!userAccount.address) {
            return;
        }
        try {
            const message = await api.signatureMessage(userAccount.address);
            const signature = await signMessage(config, {
                account: userAccount.address,
                message: message.message,
            });
            const authToken = await api.validateMessage(
                userAccount.address,
                signature,
            );
            setToken(authToken.token);
        } catch (error) {
            console.error("Login failed:", error);
        }
    };
```

#### 3. Create or refresh Unlockd wallet (Unlockd Account)&#x20;

Implement logic to create a new Unlockd wallet if the user doesn't have one, or refresh an existing one. This step is crucial for managing the user's assets and loans.

```typescript
    // In order to do a buyNow, you will need to own an unlockd wallet.
    // The assets (nfts) will be locked in your unlockd wallet.
    // If you don't have an unlockd wallet, it will be created.
    const createOrRefreshUnlockdWallet = async () => {
        try {
            let unlockdWallet = await getWallet({ network: Chains.Sepolia });
            if (!unlockdWallet) {
                await createWallet({ network: Chains.Sepolia });
                unlockdWallet = await getWallet({ network: Chains.Sepolia });
            }
            if (unlockdWallet) {
                setUnlockdAccount(unlockdWallet);
            }
        } catch (error) {
            console.error("Error with Unlockd wallet:", error);
        }
    };
```

#### 4. Implement BNPL transaction execution&#x20;

Set up the core BNPL functionality using the Unlockd SDK. This involves getting the buy now signature, calculating the loan terms, and executing the BNPL transaction.

```typescript
    const handleBuyNow = async () => {
        if (selectedUnlockdNFTs.length === 0 || !token || !unlockdAccount) {
            return;
        }

        try {
            const nftsArray = selectedUnlockdNFTs.map((nft) => ({
                collection: nft.collection,
                tokenId: nft.tokenId,
            }));

            const params = {
                nfts: nftsArray,
                underlyingAsset: selectedUnderlyingAsset,
            };

            const signature = await api.borrowSignature(token, params);

            await borrow(BigInt(parseUnits(borrowAmount, 6)), nftsArray, signature, {
                network: Chains.Sepolia,
            });
        } catch (error) {
            console.error("Borrow failed:", error);
        }
    };

    useEffect(() => {
        if (token) {
            createOrRefreshUnlockdWallet();
        }
    }, [token]);

    const isAuth = userAccount.isConnected && !!token;
```

#### 5. Render the user interface with all interactive elements

Create and render the user interface components that tie all the functionality together. This includes elements for wallet connection, asset selection, and initiating the BNPL transaction.

```typescript
    return (
        <div>
            <h1>Unlockd Borrow Example</h1>

            {/* Connect wallet */}
            {(() => {
                if (!userAccount.isConnected) {
                    return (
                        <div>
                            <button onClick={() => connect({ connector: injected() })}>
                                Connect
                            </button>
                        </div>
                    );
                }

                return (
                    <div>
                        <p>Connected to {userAccount.address}</p>
                        <button onClick={() => disconnect()}>Disconnect</button>
                    </div>
                );
            })()}

            {/* Check current network */}
            {(() => {
                if (userAccount.isConnected && userAccount.chainId !== sepolia.id) {
                    return (
                        <div>
                            <p>Please switch to Sepolia network</p>
                        </div>
                    );
                }
                return null;
            })()}

            {/* Unlockd Log In */}
            {(() => {
                if (userAccount.isConnected && !isAuth) {
                    return (
                        <div>
                            <button onClick={logIn}>Log In to Unlockd</button>
                        </div>
                    );
                }
                return null;
            })()}

            {/* Select asset */}
            {(() => {
                if (isAuth) {
                    return (
                        <select
                            value={selectedUnderlyingAsset}
                            onChange={(e) =>
                                setSelectedUnderlyingAsset(e.target.value as Address)
                            }
                        >
                            {Object.entries(UnderlyingsAsset).map(([key, value]) => (
                                <option
                                    key={key}
                                    value={underlyingsAssets(Chains.Sepolia)[value]}
                                >
                                    {key}
                                </option>
                            ))}
                        </select>
                    );
                }
            })()}

            {/* Create & display Unlockd wallet and the supported collections */}
            {(() => {
                if (isAuth) {
                    return (
                        <>
                            <h2>Unlockd Wallet</h2>
                            <p>Unlockd Wallet: {unlockdAccount || "Not created"}</p>
                            <button onClick={createOrRefreshUnlockdWallet}>
                                Create/Refresh Unlockd Wallet
                            </button>
                            {unlockdAccount && (
                                <>
                                    <h3>NFT to buyNow</h3>
                                    <div>
                                        <input
                                            type="text"
                                            value={collection}
                                            onChange={(e) => setCollection(e.target.value)}
                                            placeholder="Collection"
                                        />
                                    </div>
                                    <div>
                                        <input
                                            type="number"
                                            value={tokenId}
                                            onChange={(e) => setTokenId(parseInt(e.target.value))}
                                        />
                                    </div>
                                    <div>
                                        <input
                                            type="number"
                                            value={amount}
                                            onChange={(e) => setAmount(parseInt(e.target.value))}
                                        />
                                    </div>
                                    <div>
                                        <button onClick={handleBuyNow}>Buy Now</button>
                                    </div>
                                </>
                            )}
                        </>
                    );
                }
            })()}
        </div>
    );
}

export default App;
```


# Best Practices

When using the Unlockd SDK, follow these best practices to ensure a smooth integration and optimal performance:

### Error Handling

* Always wrap SDK method calls in try-catch blocks to handle errors gracefully.
* Handle specific error cases based on the error code and display appropriate error messages to the user.
* Log errors for debugging and monitoring purposes.

### Security

* Keep your API key and secret secure and avoid exposing them in client-side code.
* Use secure communication channels (e.g., HTTPS) when making API requests.
* Validate and sanitize user input to prevent security vulnerabilities.

### Performance

* Minimize the number of API requests to improve performance and reduce latency.
* Use caching mechanisms to store frequently accessed data and avoid unnecessary API calls.
* Implement pagination or lazy loading for large datasets to load data incrementally.

### Testing

* Write unit tests to verify the correctness of your integration code.
* Test error handling scenarios to ensure your application can handle errors gracefully.
* Conduct thorough testing before deploying to production.

### Keeping Up to Date

* Regularly check for updates and new versions of the Unlockd SDK.
* Review the changelog and update your integration code accordingly.
* Stay informed about any deprecations or breaking changes in the SDK.

By following these best practices, you can ensure a reliable and efficient integration of the Unlockd SDK into your application.


# Changelog


# Support & Contact

Unlockd is dedicated to providing comprehensive support to ensure a seamless experience for our users, developers and partners. If you have any questions, need assistance, or are interested in exploring partnership opportunities, please reach out to us using the contact information below.

## Support

For technical support, troubleshooting, or any other inquiries related to the Unlockd protocol or SDK, please contact our support team. We are here to help you with any issues you may encounter and provide guidance on using our platform and tools effectively.

* **Email**: <support@unlockd.finance>

## Partnerships

If you are interested in partnering with Unlockd, have questions about our revenue-sharing program, or would like to discuss potential collaborations, please get in touch with our partnerships team. We are always looking to work with innovative projects and organizations to expand the ecosystem.

* **Email**: <partnerships@unlockd.finance>


