Interacting with Smart Contracts

How to find the right smart contract to use for your dApp?

reNFT is rental infrastructure for the metaverse, which means we've designed our SDK to be abstract enough to support redeployments on any kind of arbitrary blockchain network; this ensures we provide battle-tested, exhaustive and truly decentralized rental infrastructure for the unbounded range of tokenized assets that support the ongoing Web3 revolution.

NFTs and their use cases are fundamentally rewriting about how we think about games, music, art, marketplaces and events; and preconceived notions of rentals are no exception. The reNFT SDK fully embraces the abstract and diverse inevitability of Web3, and in the following documentation, we're going to talk through how you can leverage this to your full advantage.

Selecting a Deployment

In order to interact with a reNFT smart contract, we quickly need to run through a concept called Deployments. These are the mechanism by which we enable deterministic resolution of the interfaces to different rental contracts that have been deployed across the metaverse.

For example, a Deployment respects the ideas that:

  • Different smart contracts have different purposes; such as orchestrating trustless collateralized lending, non-collateralized lending, reward sharing between counterparties and more!

  • In future, we expect updated versions of these smart contracts can be deployed at whim, flaunting new-and-improved interfaces and functionality, and we wish to take advantage of these easily without invalidating or obscuring these differences in pre-existing client implementations.

  • Our infrastructure needs to respect that blockchains enable the same smart contracts to be deployed to multiple different addresses.

  • Our smart contracts can even be deployed across many kinds of blockchains!

For these reasons, we need to propose a mental model which encapsulates the unique challenges of building client applications which scale to support the endless possibilities of what can be expressed on-chain.

⚠️ It's important to select the correct deployment for your intended use case.

In the reNFT SDK, a Deployment takes the following form:

{
  readonly contractType: ContractType;
  readonly version: Version;
  readonly contractAddress: string;
  readonly network: Network;
}

This is all the information we will need to encapsulate any smart contract deployment out there in the metaverse. Really.

Let's see how we'd find a deployment of our collateral-free contract, nicknamed Sylvester, on the Polygon network:

import {
  findDeployments,
  NETWORK_POLYGON_MAINNET,
  ContractType,
} from "@renft/sdk";

const [...maybeMatchingDeployments] = findDeployments({
  network: NETWORK_POLYGON_MAINNET,
  contractType: ContractType.SYLVESTER,
});

This would return all available instances of Sylvester on Polygon. At the time of writing, this would actually return two different deployments you can use: v0 and v1!

You can add further properties to refine a particular deployment configuration to ensure only a single resolution is found, for example:

import {
  findDeployments,
  NETWORK_POLYGON_MAINNET,
  ContractType,
} from "@renft/sdk";

const [deployment] = findDeployments({
  network: NETWORK_POLYGON_MAINNET,
  contractType: ContractType.SYLVESTER,
  contractAddress: '0x4e52b73aa28b7ff84d88ea3a90c0668f46043450',
});

In this instance, we have specified sufficient criteria to resolve a single deployment of Sylvester on Polygon.

In most instances, you'll find it a lot easiest to reference an existing deployment directly, for example:

import { DEPLOYMENT_SYLVESTER_POLYGON_MAINNET_V1 } from "@renft/sdk";

The advantage of using a specific deployment reference is that we receive a narrowed-type of the deployment, instead of dealing with deployment attributes which are scoped to the search criteria when specified in a call to findDeployments. This leaves no room for ambiguity in the functional properties the interface supports, and allows TypeScript to helpfully narrow this for you.

Different versions of a deployment specify different call interfaces; for example, the lend function of Sylvester v0 was extended in Sylvester v1 to provide additional functionality such as the ability to control whether lendings are permitted to auto-renew upon completion of a cycle. This helps lenders earn more by automatically re-enabling the availability of their rental, and simultaneously costs them less gas in the process.

Resolving a Smart Contract

Once we've selected a Deployment, it's super easy to resolve the equivalent ethers-compatible Contract object.

To do this, we make a call to getRenftContract():

import {
  DEPLOYMENT_AZRAEL_ETHEREUM_MAINNET_V0,
  getRenftContract,
} from "@renft/sdk";
import { ethers } from "ethers";

const collateralizedLendingContract = getRenftContract({
  deployment: DEPLOYMENT_AZRAEL_ETHEREUM_MAINNET_V0,
  signer: ethers.Wallet.createRandom(),
});

Notice in this instance that to resolve a smart contract using a deployment, we must also provide a signer. This signer is responsible for signing (authenticating) transactions made using the contract, so you'll usually want to create a new Contract instance whenever a new Wallet is connected.

The getRenftContract() function is cheap to invoke, so to avoid unnecessary complexity or potential errors caused by accidentally holding onto stale Contract references, it usually makes sense to call getRenftContract() immediately before making a transaction opposed to caching them.

The Deployment you provide to getRenftContract() is all the context you'll need to start creating your very first transactions.

⚠️ Remember that getRenftContract() may not return a Contract if the deployment you have passed is invalid; this can happen if your search query passed into findDeployments fails to resolve to a valid Deployment.

It is always good practice to check that getRenftContract() has returned a valid Contract by using a conditional check for truthiness of the result object, i.e.

import {
  findDeployments,
  NETWORK_AVALANCHE_MAINNET,
  ContractType,
} from "@renft/sdk";

const [maybeDeployment] = findDeployments({
  network: NETWORK_AVALANCHE_MAINNET,
  contractType: ContractType.SYLVESTER,
});

const maybeContract = getRenftContract({
  deployment: maybeDeployment,
  signer: ethers.Wallet.createRandom(),
});

if (!maybeContract) throw new Error("1) What");

Last updated