@renft/sdk
Version:
**ReNFT** is a multi-chain highly gas-optimised NFT rental protocol and platform that can be whitelabel integrated into any project to enable collateral-free in-house renting, lending, and reward share (scholarship automation).
219 lines (192 loc) • 6.14 kB
text/typescript
import { Signer } from '@ethersproject/abstract-signer';
import { getAddress } from '@ethersproject/address';
import { Contract, ContractInterface } from '@ethersproject/contracts';
import isEqual from 'react-fast-compare';
import {
DEPLOYMENT_AZRAEL_ETHEREUM_MAINNET_V0,
DEPLOYMENT_RESOLVER_AVALANCHE_FUJI_TESTNET_V0,
DEPLOYMENT_RESOLVER_AVALANCHE_MAINNET_V0,
DEPLOYMENT_RESOLVER_ETHEREUM_GOERLI_TESTNET_V0,
DEPLOYMENT_RESOLVER_ETHEREUM_MAINNET_V0,
DEPLOYMENT_RESOLVER_POLYGON_MAINNET_V0,
DEPLOYMENT_RESOLVER_POLYGON_MAINNET_V1,
DEPLOYMENT_SYLVESTER_ETHEREUM_GOERLI_TESTNET_V0,
DEPLOYMENT_SYLVESTER_ETHEREUM_MAINNET_V0,
DEPLOYMENT_SYLVESTER_POLYGON_MAINNET_V0,
DEPLOYMENT_SYLVESTER_POLYGON_MAINNET_V1,
DEPLOYMENT_WHOOPI_AVALANCHE_FUJI_TESTNET_V0,
DEPLOYMENT_WHOOPI_AVALANCHE_MAINNET_V0,
Deployments,
EVMNetworkType,
isValidContractVersion,
RenftContractType,
} from '../core';
import { CONTRACT_ABI_VERSIONS } from './consts';
import { createInterfaceVersions } from './interfaces';
import {
AbstractRenftContractDeployment,
CreateVersionedContractInterfaceResult,
} from './types';
export const RENFT_CONTRACT_DEPLOYMENTS: Deployments[] = [
DEPLOYMENT_AZRAEL_ETHEREUM_MAINNET_V0,
DEPLOYMENT_SYLVESTER_ETHEREUM_MAINNET_V0,
DEPLOYMENT_SYLVESTER_ETHEREUM_GOERLI_TESTNET_V0,
DEPLOYMENT_SYLVESTER_POLYGON_MAINNET_V0,
DEPLOYMENT_SYLVESTER_POLYGON_MAINNET_V1,
DEPLOYMENT_WHOOPI_AVALANCHE_FUJI_TESTNET_V0,
DEPLOYMENT_WHOOPI_AVALANCHE_MAINNET_V0,
DEPLOYMENT_RESOLVER_ETHEREUM_MAINNET_V0,
DEPLOYMENT_RESOLVER_ETHEREUM_GOERLI_TESTNET_V0,
DEPLOYMENT_RESOLVER_POLYGON_MAINNET_V0,
DEPLOYMENT_RESOLVER_AVALANCHE_FUJI_TESTNET_V0,
DEPLOYMENT_RESOLVER_AVALANCHE_MAINNET_V0,
DEPLOYMENT_RESOLVER_POLYGON_MAINNET_V1,
];
export function hasValidReNftContractType<T extends Deployments>({
contractType,
}: T): boolean {
return Object.values(RenftContractType).includes(
contractType as RenftContractType
);
}
export function hasValidNetwork<T extends Deployments>({
network,
}: T): boolean {
return (
Object.values(EVMNetworkType).includes(network.type) && network.chainId > 0
);
}
export function hasValidStartBlock<T extends Deployments>({
startBlock,
}: T): boolean {
return startBlock >= 0;
}
export function hasValidContractAddress<T extends Deployments>({
contractAddress,
}: T): boolean {
try {
return getAddress(contractAddress) === contractAddress;
} catch {
return false;
}
}
export function hasValidContractVersion<T extends Deployments>({
contractType,
version,
}: T): boolean {
return isValidContractVersion({ contractType, version });
}
export function isValidDeployment<T extends Deployments>(
deployment: T
): boolean {
const validityChecks = [
hasValidContractAddress,
hasValidReNftContractType,
hasValidNetwork,
hasValidStartBlock,
hasValidContractVersion,
] as const;
return validityChecks.every(check => check(deployment));
}
export function findDeployments<T extends Deployments>(search: Partial<T>) {
return RENFT_CONTRACT_DEPLOYMENTS.filter(
(maybeMatchingDeployment: Deployments): maybeMatchingDeployment is T => {
const definedKeys = Object.keys(search);
const filterObject = Object.fromEntries(
Object.entries(maybeMatchingDeployment).filter(([k]) =>
definedKeys.includes(k)
)
);
return isEqual(filterObject, search);
}
);
}
export function findSingleDeploymentOrThrow<T extends Deployments>(
search: Partial<T>
) {
const [deployment, ...rest] = findDeployments<T>(search);
if (!deployment)
throw new Error(
`[findSingleDeploymentOrThrow]: No deployment found for search: ${JSON.stringify(
search
)}`
);
if (rest.length)
throw new Error(
`[findSingleDeploymentOrThrow]: Multiple deployments found for search: ${JSON.stringify(
search
)}`
);
return deployment;
}
// Find a single contract address for a given deployment. Will throw if none-or-many
// matching deployments are found.
export function getContractAddressForDeployment<T extends Deployments>(
search: Omit<Partial<T>, 'contractAddress'>
): string {
const matchingDeployment = findSingleDeploymentOrThrow<T>(
search as Partial<T>
);
const { contractAddress } = matchingDeployment!;
return contractAddress;
}
export function getDeploymentAbi<T extends RenftContractType>({
contractType,
version,
}: {
readonly contractType: T;
readonly version: keyof CreateVersionedContractInterfaceResult[T];
}): ContractInterface {
const contractAbiVersions = CONTRACT_ABI_VERSIONS[contractType];
// @ts-expect-error We can't infer the following yet.
const maybeContractAbi = contractAbiVersions[version];
if (!maybeContractAbi)
throw new Error(
`[getDeploymentAbi]: Unable to find abi for combination "${String(
contractType
)}", "${String(version)}".`
);
return maybeContractAbi;
}
export function getContractForDeployment<T extends RenftContractType>({
contractAddress,
contractType,
version,
signer,
}: {
readonly contractAddress: string;
readonly contractType: T;
readonly version: keyof CreateVersionedContractInterfaceResult[T];
readonly signer: Signer | null;
}): Contract {
const abi = getDeploymentAbi({ contractType, version });
return new Contract(contractAddress, abi, signer ?? undefined);
}
export function getRenftContract<
ContractType extends keyof CreateVersionedContractInterfaceResult,
Version extends keyof CreateVersionedContractInterfaceResult[ContractType]
>({
deployment,
signer,
}: {
readonly deployment: AbstractRenftContractDeployment<ContractType, Version>;
readonly signer: Signer | null;
}) {
const {
contractAddress,
contractType,
version,
network: { type: networkType },
} = deployment;
const contract = getContractForDeployment({
contractAddress,
contractType,
signer,
version,
});
const { [contractType]: contractFunctions } = createInterfaceVersions(
contract,
networkType
);
return contractFunctions[version];
}