To interact with our smart contracts, we always recommend using our SDK. We view the SDK as the source of truth for all of our deployments and business logic; most importantly, it versions where our smart contracts reside in the metaverse and defines objectively how they work.
To work with collateral-free lendings, we'd use the Sylvester contract:
import { getRenftContract, DEPLOYMENT_SYLVESTER_ETHEREUM_MAINNET_V0, SylvesterV0FunctionInterface,} from"@renft/sdk";import { ethers } from"ethers";constsigner=ethers.Wallet.createRandom(); // In practical dApps, this would be the user's wallet!constrenft:SylvesterV0FunctionInterface=getRenftContract({ deployment:DEPLOYMENT_SYLVESTER_ETHEREUM_MAINNET_V0, signer,});
💭 We name our contracts after famous cats! You can find the name mappings here. Meow!
Any of the following functions support batching. This is shown below for the lend() instruction, where the user lends an AstroCat and a CatPlsr within the same function call:
dApps invoke lend() when they wish to create a new on-chain lending. A lending is when a market maker asserts that they wish to allow other users to temporarily have rights to the asset for the duration of the rental.
Since the lend() function is designed to batch multiple NFTs together, you can create a single-element lending by using single-element arrays for each of the required parameters:
import { NFTStandard, PaymentToken, packPrice } from"@renft/sdk";constnftStandard=NFTStandard.E1155;constnftAddress="0x0db8c099b426677f575d512874d45a767e9acc3c";consttokenID="1";constlendAmount=1; // Quantity of the NFT to lend.constmaxRentDuration=1; // Duration is measured in days.constdailyRentPrice=packPrice("1"); // Create a properly formatted rental price.constpaymentToken=PaymentToken.WETH;awaitrenft.lend( [nftStandard], [nftAddress], [tokenID], [lendAmount], [maxRentDuration], [dailyRentPrice], [paymentToken]);
A renting is created when a user discovers a compelling lending on the blockchain and decides to take the maker up on their offer. In this instance, the user is the taker of the lending. Much like the previous example, we call the rent() function with vectorized data representing each individual token to be rented in a gas-optimized batch.
import { NFTStandard } from"@renft/sdk";constnftStandard=NFTStandard.E1155;constnftAddress="0x0db8c099b426677f575d512874d45a767e9acc3c";consttokenID="1";constlendingID="1"; // this information is pulled from the subgraphconstrentDuration=1; // in daysconstrentAmount=1;awaitrenft.rent( [nftStandard], [nftAddress], [tokenID], [lendingID], [rentDuration], [rentAmount]);
Stopping a Rental
For a non-collateralized rental, even though no real possession of the NFT is given (it is a virtualized rights-to-ownership), the renter must signal that they have concluded "using" the NFT with a call to stopRent().
If the renter fails to make this call, the lender is permitted to invoke claimRent(), explained in the following section, to redeem the full amount of rent.
💭 Invocations to stopLend() are disabled until the lender calls claimRent() first.
import { NFTStandard } from"@renft/sdk";constnftStandard=NFTStandard.E1155;constnftAddress="0x0db8c099b426677f575d512874d45a767e9acc3c";consttokenID="1";constlendingID="1"; // from subgraphconstrentingID="1"; // from subgraphawaitrenft.stopRent( [nftStandard], [nftAddress], [tokenID], [lendingID], [rentingID],);
Lenders can claim rent by calling the claimRent() function.
Remember, the smart contract enforces the rules of lending and renting; you can try to claim the rent of an ongoing lending which doesn't belong to you, but the transaction will be reverted!
Finally, there's the stopLend function, which is called by the lender. This prevents the provided assets from being rented out any longer, much to the chagrin of prospective renters.
In reNFT, prices are returned in a custom format. This is a performance optimization which enables an entire lending to fit snugly inside a single storage slot, which saves gas.
To unpack them, we can use the unpackPrice() function:
import { PaymentToken, unpackPrice } from"@renft/sdk";// Convert a low-level representation of an ERC-20 used on// the marketplace into a TypeScript-friendly enum.constparsePaymentToken= (tkn:string):PaymentToken=> {switch (tkn) {case"0":returnPaymentToken.SENTINEL;case"1":returnPaymentToken.WETH;case"2":returnPaymentToken.DAI;case"3":returnPaymentToken.USDC;case"4":returnPaymentToken.USDT;case"5":returnPaymentToken.TUSD;default:returnPaymentToken.DAI; }};// In this example, let's imagine we've read Lending collection data// from a Sylvester subgraph. Here's how we'd transform the low-level// blockchain data into high-level, human (and feline) friendly types.constlendingsDataToLendings= ( theGraphLendings:TheGraphLending[]) => {consttheGraphToLending= (theGraphLending:TheGraphLending) => {return { lendingID:theGraphLending.id, lenderAddress:theGraphLending.lenderAddress,// Convert the price back into a BigNumber. dailyRentPrice:unpackPrice(theGraphLending.dailyRentPrice), maxRentDuration:Number(theGraphLending.maxRentDuration), lendAmount:Number(theGraphLending.lendAmount), paymentToken:parsePaymentToken(theGraphLending.paymentToken), lentAt:Number(theGraphLending.lentAt), }; };returntheGraphLendings.map(theGraphToLending);};