Instantiate New Quest

Configure a New Quest.

Instantiate a new Quest with your Quest details, specify the maximum number of players that can join a Quest and configure your Milestone and Token Gated Logic.

Set the factoryId to 0 to create a new Kinora Factory instantiation. This will deploy your custom suite of the KinoraAccessControls, KinoraEscrow, KinoraMetrics, KinoraNFTCreator and KinoraQuestData contracts.

If you already have instantiated contracts from the factory suite, set your factoryId to create more quests within this suite. You can verify, add and remove additional wallet addresses as whitelisted envokers within your suite.

const { postId, factoryId, questId, transactionHash, error, errorMessage, factoryAccessControls, factoryEscrow, factoryQuestData, factoryMetrics, factoryNFTCreator } = await newEnvoker.instantiateNewQuest({
  factoryId: 0,
  questDetails: {
    title: "Chromadin Chronicle",
    description:
      "Engage in a Chromadin video binge session for Season 1 and Season 2 of The Dial Pirate Radio . Interactions, mirrors and comments on episodes accrue bonus points.",
    cover: "ipfs://QmQk9TqFivUqc6ktosoZVVih9o1uiY3r5Z7F3GCC1FpaJS",
  },
  maxPlayerCount: 100,
  milestones, // see Milestones for struct configuration
  joinQuestTokenGatedLogic: tokenGatedLogic // see Token Gated Logic for struct configuration
});

Instantiate Quest Props

/* The Factory Id. Set to 0 for instantiating a new Kinora Factory instance, otherwise specify the Id that you are a verified envoker for.*/
factoryId: number;

/* The Quest details struct. The description and cover will be shown within the Lens Post across Lens interfaces. */
questDetails: { title: string; description: string; cover: `ipfs://${string}` };

/* The max number of unique players that can join the quest */
maxPlayerCount: number;

/* Factory Access Controls Contract. */
factoryAccessControls: `0x${string}`; 
 
/* Factory Escrow Contract. */
factoryEscrow: `0x${string}`; 

/* Factory Quest Data Contract. */
factoryQuestData: `0x${string}`; 

/* Factory Metrics Contract. */
factoryMetrics: `0x${string}`; 

/* Factory NFT Creator Contract. */
factoryNFTCreator: `0x${string}`;

/* All milestones within the Quest. See Milestone struct details for how to configure. */
milestones: Milestone[];

/* Token Gated logic for joining the Quest. See Token Gated Logic for struct configuration. */
joinQuestTokenGatedLogic: GatingLogic;

/* (Optional) Wallet instance for Polygon Network. If you have passed the wallet object to the constructor, it is not necessary here. */
wallet?: ethers.Wallet;

/* (Optional) Set to true to approve ERC20 reward tokens to be transfered to the KinoraEscrow contract through the SDK. If set to false, ensure to approve your token spend with the KinoraOpenAction Contract before calling instantiateNewQuest. */
approveRewardTokens?: ethers.Wallet;

Response Object

/* If no error is thrown, returns the Post Id of the Lens Quest Publication envoked. */
postId?: `0x${string}`;

/* The factory Id of the Kinora Factory Instance used to create the new quest. Use this for instantiating new Quests within the same group of factory contracts. */
factoryId?: number;

/* Id of the quest instantiated within the Factory contracts. */
questId?: number;

/* If no error is thrown, returns the Transactions Hash of the Lens Quest Publication transaction. */
transactionHash?: `0x${string}`;

/* Boolean to indicate whether an error was encountered during instantiation. */
error: boolean;

/* Message of the encountered error. */
errorMessage?: string;

Token Gated Logic

Every Quest and its subsequent Milestones may be configured with optional Token-Gated Logic.

This logic verifies if a player possesses the specified configuration of ERC721 and/or ERC20 tokens on Polygon Mainnet in their wallet, determining their eligibility to join a Quest and meet completion requirements for Milestones.

Ensure that the length of your ERC20 Addresses array aligns with that of your ERC20 Thresholds array, with corresponding indices. Likewise, ensure alignment between your ERC721 Addresses and ERC721 Token IDs and/or ERC721 Token URIs, should IDs or URIs be specified. If no Token IDs or Token URIs are specified at a given index (i.e., an empty array is input), then all Token IDs for that contract will be accepted.

Threshold values for ERC20 tokens need to be indicated in wei. Ensure the correct wei value is provided for the specified ERC20 token.

const tokenGatedLogic = {
  erc721TokenURIs: [[], [], ["ipfs://QmamNo25wv4ZQpmp14uXHxJnmp8GE6QNFA1fCug531TPoH", "ipfs://QmciaqC7cntDVvxxXHBYWWzBVRZQrGajHRZ3ydbVUcaUaH"]], 
  erc721TokenIds: [[], ["3", "13", "130"]], 
  erc721Addresses: ["0x453511e08F3AF28F0A47620bb5f32479F4E2e280", "0x1ACeCeDBC54d65D72338A2c0b55b479aF5B45870", "0x0147435c505390Bb1E657c8EBc373DcEdfDe0F08"],
  erc20Addresses: ["0x6968105460f67c3bf751be7c15f92f5286fd0ce5"],
  erc20Thresholds: ["10000000000000000000"],
  oneOf: true,
}

Gated Logic Props

/* The specific token URIs within a ERC721 contract that must be held. Leave an empty array if all token URIs within a contract are valid. This form is only valid for NFT Contracts that inherent IERC721Enumerable interface. It exists to support NFT collection structures that mint from the same NFT contract. */
erc721TokenURIs: string[][];

/* The specific token Ids within a ERC721 contract that must be held. Leave an empty array if all token Ids within a contract are valid.*/
erc721TokenIds: number[][];

/* The Polygon Mainnet contract addresses of the ERC721 compatible contract addresses. */
erc721Addresses: `0x${string}`[];

/* The Polygon Mainnet contract addresses of the ERC20 compatible contract addresses. */
erc20Addresses: `0x${string}`[];

/* The specific threshold values, in wei, that must be held of the ERC20 tokens. */
erc20Thresholds: string[];

/* Set oneOf to true if a player only needs to hold one of the specified ERC721 or ERC20 tokens. Set oneOf to false if they must hold all specified tokens. */
oneOf: boolean;

Milestones

Configure the reward and eligibility requirements for each Milestone within your Quest. You can specify as many Milestones as you like per Quest.

For a player to be eligible to complete a Milestone, you can specify both Token Gated Logic for the Milestone as well as video metric thresholds.

const milestones = [{
  gated: tokenGatedLogic,
  reward: milestoneOneReward,
  milestone: 1,
  details: milestoneOneDetailsObject,
  eligibility: milestoneOneEligibilityObject
}, 
{
  gated: tokenGatedLogic,
  reward: milestoneTwoReward,
  milestone: 2,
  details: milestoneTwoDetailsObject,
  eligibility: milestoneTwoEligibilityObject
}]

Milestone Props

/* The token gated logic that a player must meet to be eligible to complete a milestone. It follows the same struct set up as for above. */
gated: GatingLogic; 

/* The Reward struct, specifying the ERC20 or ERC721 rewards that a player receives upon milestone completion. See below for more details on how to configure Rewards. */
reward: Reward[]; 

/* The milestone order. Milestones are completed in ascending order i.e. Milestone 1 must be completed by a player before moving to Milestone 2. */
milestone: number;

/* The Milestone details struct and video post, title, description.*/
details: { title: string; 
           description: string; 
           cover: `ipfs://${string}`, 
           videoCovers: {
               cover: `ipfs://${string}` | undefined;
               title: string;
               description: string;
           }[]; 
};

/* The eligibility video metrics for each Livepeer video. See below for more details on how to configure Milestone Eligibility. */
eligibility: MilestoneEligibility;

Milestone Eligibility

Milestone eligibility specifies the video metric criteria for each video within a milestone.

Milestone Eligibility Props

/* An array detailing the video metric criteria a player must satisfy for a specific Livepeer video playback ID and the Lens Post Id associated with the video. 

Specify other factoryIds from other contract suites if you wish to include player videometrics logged on-chain from within these suites in the milestone eligibility calculation. If not, pass an empty array.

*/
internalCriteria?: {
    factoryIds: number[];
    playbackId: string;
    postId: string;
    playbackCriteria: MilestoneEligibilityCriteria;
}[];

PlaybackCriteria Props

All numerical playback props signify threshold minimums that a player must achieve for the respective video, if nothing is set the threshold defaults to 0. For boolean values, if it is set as false or not inputted, it is ignored as criteria.

/* Min. Average View Duration (AVD):
   The minimum mean time duration a player spends viewing a video (in seconds) across multiple viewings.
   It's calculated by summing the total view time and dividing by the total number of views. */
minAVD?: number;

/* Min. Video Play Count:
   The minimum number of times a video must be played from start to end without seeking. */
minPlayCount?: number;

/* Total Duration (Specify in Seconds)
   The minimum cumulative time duration of the video being played.
   It's calculated by summing the duration of each viewing session. */
minDuration?: number;

/* Quote the Publication:
   Boolean value indicating whether a player must quote the video publication. */
quote?: boolean;

/* Mirror the Publication:
   Boolean value indicating whether a player must mirror the video publication. */
mirror?: boolean'

/* Like the Publication:
   Boolean value indicating whether a player must like the video publication. */
react?: boolean;

/* Bookmark the Publication:
   Boolean value indicating whether a player must bookmark the video publication. */
bookmark?: boolean;

/* Comment the Publication:
   Boolean value indicating whether a player must comment on the video publication. */
comment?: boolean;

/* Min. Secondary Quote On Quote:
   The minimum number of quotes on the player's quote publication of the video. */
minSecondaryQuoteOnQuote?: number;

/* Min. Secondary Mirror On Quote:
   The minimum number of mirror on the player's quote publication of the video. */
minSecondaryMirrorOnQuote?: number;

/* Min. Secondary Comment On Quote:
   The minimum number of comment on the player's quote publication of the video. */
minSecondaryCommentOnQuote?: number;

/* Min. Secondary React On Quote:
   The minimum number of reacts on the player's quote publication of the video. */
minSecondaryReactOnQuote?: number;

/* Min. Secondary Collect On Quote:
   The minimum number of collects on the player's quote publication of the video. */
minSecondaryCollectOnQuote?: number;

/* Min. Secondary Quote On Comment:
   The minimum number of quotes on the player's comment publication of the video. */
minSecondaryQuoteOnComment?: number;

/* Min. Secondary Mirror On Comment:
   The minimum number of mirror on the player's comment publication of the video. */
minSecondaryMirrorOnComment?: number;

/* Min. Secondary Comment On Comment:
   The minimum number of comment on the player's comment publication of the video. */
minSecondaryCommentOnComment?: number;

/* Min. Secondary React On Comment:
   The minimum number of reacts on the player's comment publication of the video. */
minSecondaryReactOnComment?: number;

/* Min. Secondary Collect On Comment:
   The minimum number of collects on the player's comment publication of the video. */
minSecondaryCollectOnComment?: number;

Milestone Reward

To encourage players towards Milestone completion, rewards are allocated in the form of either ERC20 or ERC721 tokens, which players can claim upon completion. Multiple rewards can be set per Milestone.

The specified ERC20 tokens and their amounts must be contained in your wallet, as they are sent to the Kinora Escrow contract at the point of Quest instantiation, ensuring that players will receive their rewards.

The amount sent to the Escrow contract is the amount specified for each Milestone multiplied by the maximum player count. For example if the Quest specifies a Maximum of 20 players and a Milestone ERC20 reward of 10 wei, then 20 x 10 wei will be sent to the Escrow contract to cover all Players.

Players can only withdraw and claim these tokens upon meeting the established milestone eligibility and being verified by the envoker through the setPlayerEligibleToClaimMilestone function. An emergency admin withdraw function is incorporated, which, when triggered, terminates the Quest and precludes players from continuing, as their rewards will no longer reside within the Escrow contract.

In the case of ERC721 rewards, you need to specify the token URI intended for the Milestone reward NFT. Upon valid achievement of a milestone, players will be eligible to mint the NFT from the Kinora NFT Creator contract.

You must approve the KinoraOpenAction Contract to transfer ERC20 rewards from your wallet to the KinoraEscrow Contract. You can do this manually, or set approveRewardTokens to true when calling instantiateNewQuest to approve directly through the SDK.

Remember that ERC20 token reward amounts must be specified in their correct wei amount.

import { RewardType } from "kinora-sdk"

const milestoneOneReward = {
  type: RewardType.ERC721,
  erc721URI: "ipfs://QmRPLqEtcgyiuJZ6kvg6qTrrk7ki7s8arkpHBJGFYNwgeD",
}

const milestoneTwoReward = {
  type: RewardType.ERC20,
  erc20tokenAddress: "0x6968105460f67c3bf751be7c15f92f5286fd0ce5",
  erc20TokenAmount: "100000"
}

Reward Props

/* The Reward Type. Either ERC721 or ERC20. */
type: RewardType; 

/* The token URI ipfs hash of the NFT. Leave empty if type is ERC20. */
erc721URI?: `ipfs://${string}`;

/* The Polygon Mainnet contract address of the ERC20 token. Leave empty if type is ERC721. */
erc20tokenAddress?: `0x${string}`; 

/* Must be specified in wei. Amount of the ERC20 token a player is rewarded upon completion. Leave empty if type is ERC721. */
erc20TokenAmount?: string; 

Last updated