Only this pageAll pages
Powered by GitBook
1 of 32

Unlockd SDK

About

Loading...

Loading...

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

USE CASES (code examples)

Loading...

Loading...

Resources

Loading...

Loading...

Loading...

Introduction

Welcome to the Unlockd SDK documentation.

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.

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.

More information is available at our and sections.

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.

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
    Examples

    Code Examples

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

    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:

    Using bower:

    Using yarn:

    Using pnpm:

    Borrowing Assets

    Buy Now Module

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

    Types

    Signature

    SDK Modules

    $ npm install @verislabs/unlockd-sdk
    $ bower install @verislabs/unlockd-sdk
    $ yarn add @verislabs/unlockd-sdk
    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();

    Login

    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)

    Liquidation Price

    import {liquidationPrice} from "@unlockdfinance/unlockd-ts";
    const oneEth = BigInt(1e18)
    
    const params = {
        debt: (BigInt(50) * oneEth),
        liquidationThreshold: BigInt(8500)
    }
    const result = liquidationPrice(params)

    Borrow Signature

    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)
    ClientOptions

    Functions

    Buying an Asset

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

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

    BuyNow flow
    $ pnpm add @verislabs/unlockd-sdk
    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;
    }
    */
    export type ClientOptions = {
      network?: Chain
    }
    
    export type Chain = 'mainnet' | 'sepolia' | 'localhost'
    export const buy = async (
            amount: string, 
            signature: Signature, 
            options?: ClientOptions
        )
    const result = await buy(amount, signature, options?);

    Auction Module

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

    Auction flow

    Types

    Signature

    ClientOptions

    Functions

    Placing a Bid

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

    Redeeming an Auction

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

    Finalizing an Auction

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


    Refer to the Auction Module documentation for more details on the bid, redeem andfinalizefunctions.

    Action Module

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

    Flow

    Action Flow

    Types

    Assets

    Signature

    ClientOptions


    Functions

    Borrowing Assets

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

    Repaying Loans

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


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

    Health Factor

    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;
    }
    */

    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: [email protected]

    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: [email protected]

    UI Snippets

    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.

    Changelog

    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)

    Fetch prices

    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)
    

    Minimum Repay

    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)
    export type ClientOptions = {
      network?: Chain
    }
    
    export type Chain = 'mainnet' | 'sepolia' | 'localhost'
    export const bid = async (
            amountToPay: BigInt, 
            amountOfDebt: BigInt, 
            signature: Signature, 
            options?: ClientOptions
        )
    const result = await bid(amountToPay, amountOfDebt, signature, options?);
    export const redeem = async (
            signature: Signature, 
            options?: ClientOptions
        )
    const result = await redeem(signature, options?);
    export const finalize = async (
            claimOnUWallet: boolean,
            orderId: string,
            signature: Signature,
            options?: ClientOptions
        )
    const result = await finalize(claimOnUWallet, orderId, signature, options?);
    export type Nft = {
      collection: string
      tokenId: string
    }
    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;
    }
    */
    export type ClientOptions = {
      network?: Chain
    }
    
    export type Chain = 'mainnet' | 'sepolia' | 'localhost'
    export const borrow = async (
            amount: BigInt, 
            assets: Array<Nft>, 
            signature: Signature, 
            options?: ClientOptions
        )
    const result = await borrow(amount, assets[], signature, options?);
    export const repay = async (
            amount: BigInt, 
            signature: Signature, 
            options?: ClientOptions
        )
    const result = await repay(amount, signature, options?);

    Sell Now Module

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

    Sell flow

    Types

    Signature

    ClientOptions

    Functions

    Selling an Asset

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

    Refer to the Sell Now Module documentation for more details on the sell 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.

    NftBatchTransfer Flow

    Types

    Assets

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

    ClientOptions

    Functions

    Sending NFTs to Wallet

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

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

    Market Module

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

    Types

    Signature

    Available to borrow

    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;
    }
    */

    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.

    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)
    export type ClientOptions = {
      network?: Chain
    }
    
    export type Chain = 'mainnet' | 'sepolia' | 'localhost'
    export const sell = async (
            asset: Nft, 
            signature: Signature, 
            options?: ClientOptions
        )
    const result = await sell(asset, signature, options?);
    export type ClientOptions = {
      network?: Chain
    }
    
    export type Chain = 'mainnet' | 'sepolia' | 'localhost'
    export const sendNftsToWallet = async (
            nfts: {
                contractAddress: string,
                tokenId: string
            }[], 
            options?: ClientOptions
        ): Promise<void>
    const result = await sendNftsToWallet(nfts[], signature, options?);
    ClientOptions

    Functions

    Creating a Market Item

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

    Refer to the 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:

    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:

    Refer to the 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:

    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:

    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:

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

    Market flow

    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, 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.

    Benefits for Partners

    • Increased Revenue: Earn a share of the revenue generated from loans originated through your integration.


    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 .

    Wallet Module

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

    Wallet Flow

    Types

    ClientOptions

    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:

    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:

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

    Reservoir Integration

    Unlockd uses Reservoir to enhance its liquidation process for RWAs and NFTs. 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 and the 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.

    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.

    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.

    export type CreateOrderInput = {
      startAmount: BigInt
      endAmount: BigInt
      startTime: number
      endTime: number
      debtToSell: BigInt
    }
    export enum OrderTypes {
      ASC = 'ASC',
      DESC = 'DESC',
      NONE = 'none'
    }
    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;
    }
    */
    export type ClientOptions = {
      network?: Chain
    }
    
    export type Chain = 'mainnet' | 'sepolia' | 'localhost'
    export const create = async (
            underlyingAsset: Address,
            orderType: OrderType,
            config: CreateOrderInput,
            signature: Signature,
            options?: ClientOptions
        )
    const result = await create(underlyingAsset, orderType, config, signature, options?);
    export const cancel = async (
            orderId: string, 
            options?: ClientOptions
        )
    const result = await cancel(orderId, options?);
    export const marketBid = async (
            orderId: string,
            amountToPay: BigInt,
            amountOfDebt: BigInt,
            signature: Signature,
            options?: ClientOptions
        )
    const result = await marketBid(orderId, amountToPay, amountOfDebt, signature, options?);
    export const claim = async (
            claimOnUWallet: boolean,
            orderId: string,
            signature: Signature,
            options?: ClientOptions
        )
    const result = await claim(claimOnUWallet, orderId, signature, options?);
    export const cancelClaim = async (
            orderId: string, 
            signature: Signature, 
            options?: ClientOptions
        )
    const result = await cancelClaim(orderId, signature, options?);
    export const buyNow = async (
            claimOnUWallet: boolean,
            orderId: string,
            amountToPay: BigInt,
            amountOfDebt: BigInt,
            signature: Signature,
            options?: ClientOptions
        )
    const result = await buyNow(claimOnUWallet, orderId, amountToPay, amountOfDebt, signature, options?);

    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.

  • contact us
    Unlockd liquidation process
    role of Reservoir
    export const createWallet = async (
            options?: ClientOptions
        ):Promise<void>
    const result = await createWallet(options?);
    export const getWallet = async (
            options?: ClientOptions
        ): Promise<any>
    const result = await getWallet(options?);

    'Buy Now, Pay Later' financing

    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.

    We're committed to supporting your integration journey. If you need personalized assistance or have suggestions, please reach out to us directly. Your input will help us create more comprehensive and useful documentation for the developer community.

    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).

    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.

    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

    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 or .

    • Basic understanding of React hooks and Ethereum transactions

    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.

    Implementation Steps

    1. Set up the React component with necessary state variables and hooks.

    2. Implement login, connection and authentication

    3. Create or refresh Unlockd wallet (Unlockd Account).

    4. Implement BNPL transaction execution.

    Code Overview

    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 , 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.

    1. Set up the React component with necessary state variables and hooks

    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.

    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:

    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.

    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

    3. Create or refresh Unlockd wallet (Unlockd Account)

    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.

    4. Implement BNPL transaction execution

    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.

    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.

    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.
  • 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.

  • 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.

  • 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.

  • 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.

  • Completion: Once the loan is fully repaid, the collateralized assets are released back to the user without any restrictions.

  • Render the user interface with all interactive elements.

    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. We actually like being bothered.

    Create a Project
    Install it manually into your Project
    https://testnets.reservoir.tools/sepolia
    "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);
    typescriptCopyconst api = new UnlockdApi(Chains.Sepolia);
        // 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);
            }
        };
        // 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);
            }
        };
        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;
        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;

    Borrow against RWAs

    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.

    We're committed to supporting your integration journey. If you need personalized assistance or have suggestions, please reach out to us directly. Your input will help us create more comprehensive and useful documentation for the developer community.

    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.

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

    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 or .

    • Basic understanding of React hooks and Ethereum transactions

    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.

    Implementation Steps

    1. Set up the React component with necessary state variables and hooks.

    2. Implement login, connection and authentication

    3. Fetch NFTs and prices from connected wallets.

    4. Create or refresh Unlockd wallet.

    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.

    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:

    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.

    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

    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 below).

    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).

    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.

    For each NFT, you need to create an object with the following structure:

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

    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.

    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.

    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.

    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.

    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.

    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.

    Implement NFT selection functionality.
  • Implement NFT approval and transfer to Unlockd wallet.

  • Set up borrowing functionality against selected NFTs.

  • Render the user interface with all interactive elements.

  • Create a Project
    Install it manually into your Project
    #4
    "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);
    const api = new UnlockdApi(Chains.Sepolia);
        // 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);
            }
        };
        // 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,
                );
            }
        };
    { 
        collection: string, // NFT's collection address 
        tokenId: string, // NFT's ERC721 token id 
        underlyingAsset: string // the asset ETH or USDC you want to receive the price evaluation in 
    } 
        // 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);
            }
        };
        // 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],
            );
        };
        // 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);
            }
        };
    
        // 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;
        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
    Market Module
    Market Module