Integrate xNFT package into your Project

Introduction to xNFT

What is an xNFT Package?

This package handles CRUD (Create, Read, Updated, and Delete) operations for applications deployed to an xNFT (formerly Application NFT or AppNFT).

Here are some things you need to know about an xNFT:

  • It can have single or multiple applications. For example, a database, backend, and frontend website deployed as three applications under an xNFT with an ID 1535.

  • You need XCT tokens to run your xNFTs, and you have to deposit them into the xNFT.

  • The package is modular; this means you can extend and modify it according to your specific use case.

The xNFT package handles:

  • Contract interactions.

  • Application encryption and decryption.

  • Manages uploading and downloading of application payloads to and from decentralized storage platforms.

  • Perform CRUD operations on your application–Application manager.

  • Estimates and manages the cost of running an application; this happens through a drip manager.


Prerequisite

To get the best out of this package and the contract calls to perform for specific use cases. You need to:

To carry out these contract initialization and calls appropriately. You should include the required files for your specific use case in your project’s root directory.


Installation

Run this in your project's root directory:

npm i @decloudlabs/stk-v2

Initialize Contract Service

It contains the required definitions for contract application deployment to initialize contract service. Implement this code:

 const contractService = newSTKContractService(
    	provider: ethers.providers.Provider,
    	signer: Signer,
    	contractAddresses: ContractAddresses,
    	walletAddress: ETHAddress
	)

The code initializes an instance of STKContractService which is a function that takes four parameters. It sets up a service for interacting with smart contracts.

Explaining the parameters:

  • provider: ethers.providers.Provider: It is a read-only abstraction within the ether.js library, representing a connection to the Ethereum blockchain. It also allows for sending transactions to and reading data from the blockchain. Common examples of predefined Provider are JSON RPC providers, alchemy, etc.

  • signer: It is an abstraction of an Ethereum account that allows for the signing of transactions and messages. It is crucial for performing write operations on the blockchain.

  • contractAddresses: This is an object with a collection of deployed addresses, which is essential for interaction.

  • walletAddress: Thethereum address associated with your wallet would be used for performing the transactions. Ensure the signer is authorized to act on behalf of this address.

Example Code
import { ethers } from 'ethers';
import { newSTKContractService } from '@decloudlabs/stk-v2';

const provider = new ethers.providers.JsonRpcProvider('...'); // replace '...' with your url
const signer = provider.getSigner();
const contractAddresses = {
    //assign your contract addresses for each
    contract1: '...', 
    contract2: '...',
};

const walletAddress = '...'; // Your wallet address should be assigned to walletAddress.

const contractService = newSTKContractService(
    provider,
    signer,
    contractAddresses,
    walletAddress
);

Initialize Umbral Service

Implement this code:

const umbralService = new UIUmbralService();
	await umbralService.initAppCrypto();

Initializing this in your project allows you to use the Umbral service, a threshold proxy re-encryption scheme. This would let you carry out secured data sharing through encryption and decryption.

  • The second line loads the encryption and decryption module.

  • The first line initializes the UIUmbralService class.

Example Code
import { UIUmbralService } from "@decloudlabs/stk-v2";

const umbralService = new UIUmbralService();
	await umbralService.initAppCrypto();

Initialize Browser Encryptor

To initialize a service that handles browser wallet-based functions like getting signatures and using a browser wallet.

const browserKeyEncryptor = new STKBrowserETHKeyEncrypter(address,  provider);

This code does the following:

  • Creates an instance of STKBrowserETHKeyEncrypter, which takes two arguments, address and provider.

  • address is your wallet address.

  • The STKBrowserETHKeyEncrypter encrypts the private key associated with your provided wallet address.

Example Code
import { STKBrowserETHKeyEncrypter } from "@decloudlabs/stk-v2";

const address = '...'; // your wallet address

const browserKeyEncryptor = new STKBrowserETHKeyEncrypter(address, 
provider); // remember provider was assigned to your ethers Provider earlier in your code

Initialize Application Encryptor

Implement the code:

const appEncryptor = new STKAppEncrypt(browserKeyEncryptor,  umbralService);

Implementing this code does the following:

  • Creates an encryption service within your application that relies on the browser-specific encryption handled by browserKeyEncryptor and the proxy re-encryption provided by the umbral service.

Example Code
import { STKAppEncrypt } from '@decloudlabs/stk-v2';

// This assumes you have called browserKeyEncryptor and umbralService earlier in your code.
const appEncryptor = new STKAppEncrypt(browserKeyEncryptor, umbralService);

Initialize Application Decryptor

Implement the code:

const appDecryptor = new STKAppDecrypt(browserKeyEncryptor,
umbralService);

Implementing this code does the following:

  • This creates a decryption service within your application through browser-specific browserKeyEncryptor and proxy re-encryption by the umbral service.

Example Code
import { STKAppDecrypt } from "@decloudlabs/stk-v2";

// This assumes you have called browserKeyEncryptor and umbralService earlier in your code.
const appDecryptor = new STKAppDecrypt(browserKeyEncryptor, umbralService);

Initialize Browser Cache

To initialize, implement this code:

const appBrowserCache = new STKAppBrowserCache();

This code saves applications in the browser cache, which helps increase the overall efficiency and responsiveness of the application.

Example Code
import { STKAppBrowserCache } from "@decloudlabs/stk-v2";

const appBrowserCache = new STKAppBrowserCache();

Send Application Payload to and Recieve Application Payload from a Decentralized Storage Platform

Implement this code:

const appSenderStorage = new IPFSSenderStorage(
    	appBrowserCache,
	);
const lighthouseStorage = new LighthouseSenderStorage(
    	appBrowserCache, yourLighthouseAPIKey
	);

Implementing the code does the following:

  • The IPFSSenderStorage has an argument, appBrowserCache.

  • appBrowserCache is used to cache data from the storage server to boost efficiency.

  • The lighthouseStorage has two arguments, appBrowserCache and yourLighthouseAPIKey.

  • appBrowserCache does the same action as in IPFSSenderStorage.

  • yourLighthouseAPIKey is the API key you created to use the Lighthouse storage protocol.

Example Code
import { IPFSSenderStorage, LighthouseSenderStorage } from '@decloudlabs/stk-v2';

const yourLighthouseAPIKey = '...' // Your Lighthouse API key

// Assumes you have defined appBrowserCache earlier in your code. 
const appSenderStorage = new IPFSSenderStorage(appBrowserCache);
const lighthouseStorage = new LighthouseSenderStorage(appBrowserCache, yourLighthouseAPIKey);

Initialize Multi-Application Storage

To do this, implement the code:

const multiStorage = new STKAppMultiStorage();

This code enables the application manager to send the application data to a desired platform or read the application data from a storage platform.

Example Code
import { STKAppMultiStorage } from '@decloudlabs/stk-v2';

const multiStorage = new STKAppMultiStorage();

Add Individual Storage Platforms to Multi-Application Storage

To do this, implement the code:

multiStorage.addAppStorage(appSenderStorage);
multiStorage.addAppStorage(lighthouseStorage);

The code does the following:

  • The code uses the storage platforms used and declared when sending and receiving application payload from a decentralized storage platform.

  • It adds the two storage platforms, appSenderStorage and lighthouseStorage, to the multi-application storage.

Example Code
import { STKAppMultiStorage, IPFSSenderStorage, LighthouseSenderStorage } from '@decloudlabs/stk-v2';

// You need to have created instances of IPFSSenderStorage and LighthouseSenderStorage and STKAppMultiStorage earlier

multiStorage.addAppStorage(appSenderStorage);
multiStorage.addAppStorage(lighthouseStorage);

Initialize Drip Manager

To initialize a drip manager that manages and calculates the cost of subscriptions. Implement this code:

const dripRateManager = new UIDripRateManager(10 ** 12 + '', contractService);
  • The code ensures that subscriptions are through a drip model.

  • It ensures application users are billed appropriately for an application service.

Example Code
import { UIDripRateManager } from '@decloudlabs/stk-v2';

// Assumes contractService has initialized earlier in your code
const dripRateManager = new UIDripRateManager(10 ** 12 + '', contractService);

Initialize Application Manager

Implement the code:

const appManager = new STKAppManager(
    	appBrowserCache,
    	multiStorage,
    	contractService: EtherContracts,
    	appEncryptor,
    	appDecryptor,
    	saveAppToLocal: (app: ContractApp) => Promise<APICallReturn<string>>, // usable to save the app data to redux
    	saveSubParamToLocal: (
        	subParam: SubscriptionParam
    	) => Promise<APICallReturn<string>>, // save subscription params to redux or local state
    	removeAppFromLocal: (
        	nftID: string,
        	appID: string
    	) => Promise<APICallReturn<string>> // remove app from local state
	)
};

The code does the following:

  • Initiates a class STKAppManager with several parameters representing different services from previous sections, like appBrowserCache, multiStorage, appEncryptor, etc.

  • The Function saveAppToLocal saves your application data to a local state or a redux store.

  • The function saveSubParamToLocal saves subscription parameters to a local state or redux store.

  • The function removeAppFromLocal is used to remove an application from the redux store. Using this effectively deletes the application data from storage.

Example Code
import { STKAppManager, ContractApp, SubscriptionParam, APICallReturn } from '@decloudlabs/stk-v2';

// Assumes appBrowserCache, multiStorage, EtherContracts, appEncryptor, appDecryptor were called earlier in your code
const appManager = new STKAppManager(
    appBrowserCache,
    multiStorage,
    EtherContracts,
    appEncryptor,
    appDecryptor,
    (app: ContractApp) => Promise.resolve({}), // replace with your actual implementation
    (subParam: SubscriptionParam) => Promise.resolve({}), // replace with your actual implementation
    (nftID: string, appID: string) => Promise.resolve({}) // replace with your actual implementation
);

Mint and Fetch NFT

To mint and fetch the ID of an NFT, implement this code:

const fetchNFTID = async () => {
    const mintResp = await contractService.callContractWrite(
	contractService.AppNFTMinter.mint(address, {
        	value: 10 ** 11,
        	from: address,
    	})
	);
    
    if(!mintResp.success) throw new Error("failed to mint");
    
    const nftCountResp = await appCrypto.contractService.callContractRead(
   		 AppNFT.balanceOf(address),
   		 (res) => res.toNumber()
   	 );
    if(!nftCountResp.success) throw new Error("failed to fetch NFT");
    
    const nftCount = nftCountResp.data;
    
    const nftReq = appCrypto.contractService.callContractRead(
   	 AppNFT.tokenOfOwnerByIndex(address, nftCount-1),
   	 (res) => res.toNumber()
    );
    if(!nftReq.success) throw new Error("failed to fetch the nftID");
    
    const nftID = nftReq.data;
}

The code does the following:

  • The fetchNFTID function helps you mint a new NFT and fetch the ID of the NFT owned by a specific address.

Example Code
import { fetchNFTID } from './filepath';

fetchNFTID()

Fetch Application List

Use this code to implement:

const appList = await appManager.contractCall.getAppList(nftID);

Implement this code does the following:

  • It retrieves the applications that are linked to specific NFTs.

  • After retrieving, services related to the applications can be accessed or used.

Example Code
import { STKAppManager, ContractApp, SubscriptionParam, APICallReturn } from '@decloudlabs/stk-v2';

// Initialize the necessary services and callbacks
// Replace these with your actual implementations
const appBrowserCache = {};
const multiStorage = {};
const EtherContracts = {};
const appEncryptor = {};
const appDecryptor = {};

const saveAppToLocal = (app: ContractApp): Promise<APICallReturn<string>> => {
    // Implementation here
};

const saveSubParamToLocal = (subParam: SubscriptionParam): Promise<APICallReturn<string>> => {
    // Implementation here
};

const removeAppFromLocal = (nftID: string, appID: string): Promise<APICallReturn<string>> => {
    // Implementation here
};

// Create a new instance of STKAppManager
const appManager = new STKAppManager(
    appBrowserCache,
    multiStorage,
    EtherContracts,
    appEncryptor,
    appDecryptor,
    saveAppToLocal,
    saveSubParamToLocal,
    removeAppFromLocal
);

// Use the getAppList method
// Replace 'nftID' with your actual NFT ID
const nftID = 'nftID';
appManager.contractCall.getAppList(nftID)
    .then(appList => {
        console.log(appList);
    })
    .catch(error => {
        console.error('Failed to get app list:', error);
    });

Last updated