# Quest Rewards

**Different types of Quest Rewards**

* Give Resources
* Give Assets
* Give Prizes
* Give Discord Roles

Each of the above rewards can be identified and dealt with in FE from the following data types:&#x20;

```typescript
// Shared properties of a reward
const baseRewardSchema = z.object({
  rewardConfigurationId: z.string(),
  rewardId: z.string(),
  createdAt: z.string().datetime().optional(),
  updatedAt: z.string().datetime().optional(),
  questId: z.string(),
});
```

In the Quest object, look for the `reward`field. Depending on the reward defined in the admin panel, we need to use the field `slug`to identify the type of reward. in the SDK you will find the following zod schemas you can use to validate/identify rewards:&#x20;

```typescript
const questEventWithRewardsSchema = z.union([
  giveAssetsRewardSchema,
  manualClaimRewardSchema,
  giveResourceRewardSchema,
  assignDiscordRoleRewardSchema,
  giveResourceProgressiveRewardSchema,
]);
```

Of course if you don't want to opt-in to zod, you can also use plain typescript to identify these rewards with:&#x20;

```typescript
export type CreditResourceType = z.infer<typeof giveResourceRewardSchema>;
export type AssignDiscordRoleType = z.infer<
  typeof assignDiscordRoleRewardSchema
>;

export type ManualClaimRewardType = z.infer<typeof manualClaimRewardSchema>;
export type GivePrizesRewardType = z.infer<typeof givePrizesRewardSchema>;
export type GiveAssetsType = z.infer<typeof giveAssetsRewardSchema>;
export type GiveResourcesProgressive = z.infer<
  typeof giveResourceProgressiveRewardSchema
>;
```

### Claiming Rewards

Quest reward allocation is manual and require a user action to claim rewards once their quest is completed. Once the quest is completed, use the following methods to identify rewards allocated to the user to prepare for the claim phase:&#x20;

#### In server components

```typescript
function getQuestRewardAllocations(
  api: AxiosInstance,
  questId: string,
): Promise<{
    allocations: QuestRewardAllocationsType[];
    total: number;
    offset: number;
}>;
```

The above function will fetch all reward allocations for the given quest id per user. Remember to populate Authorization headers in the axios config beforehand.&#x20;

#### In client components

In the client components use the following hook to receive all rewards allocated to the user. an enabled option is provided to pass in when the quest is completed.&#x20;

```typescript
function useQuestRewardAllocations(
  questId?: string,
  options?: Partial<{ enabled: boolean }>,
);
```

### Claiming Rewards

Once you have the reward allocation, you can start claiming the rewards by checking whether or not a reward has a requirement, currently only one reward types might have requirements, but you can streamline this by checking the `requirements`field in the allocation.&#x20;

If there is no requirement, the claiming process is easy as what follows:&#x20;

#### In server components

```typescript
function postClaimQuestRewardAllocation(
  api: AxiosInstance,
  questId: string,
  allocationId: string,
  payload?: any,
): Promise<QuestRewardAllocationsType>;
```

#### In client components

```typescript
function useMutateQuestReward();
```

> **NOTE:** the payload field is not required when there is no requirement, it is mainly used for passing requirements.

**Claiming Prizes**

Since prizes are custom rewards, their delivery mechanism is a bit different, some of them are automatically distributed, but some other require admin approval. In addition to that, custom rewards may require some additional information from the user before they can be delivered. For example, a Steam gift card may require email address from the user, or for some other prizes a postal address may be required. This information is defined in the requirements field.&#x20;

In the following section we will deal with prize requirements and how can we prepare data to send to our API.&#x20;

Currently following requirements are supported via SDK:&#x20;

```typescript
const rewardRequirementSchema = z.discriminatedUnion('name', [
  emailQuestRewardRequirementSchema,
  addressQuestRewardRequirementSchema,
  walletAddressQuestRewardRequirementSchema,
]);
```

Since currently only Prize rewards contain requirements, we can use a similar approach to determine prize requirements:&#x20;

```tsx
function RewardRequirements({ quest, }) {
  const reward = first(quest?.rewards);
  const prizeReward = givePrizesRewardSchema.safeParse(reward);

  if (prizeReward.error) {
    console.log(prizeReward.error); // Keeping this for debugging purposes
    return null;
  }

  const requirements = prizeReward.data.rewardHandlerArgs.requirements ?? [];

  if (requirements.length === 0 || quest?.status !== QUEST_STATUS.COMPLETED) {
    return null;
  }

  return (
    <RequirementForm requirements={requirements} />
  );
}

```

Then we can identify required fields based on the type definitions we cover previously, note that this example is using `react-hook-form`, but you can use any form library you prefer:

```tsx
  <form onChange={form.handleSubmit(onSuccessHandler, onInvalidHandler)}>
        {requirements.map((requirement) =>
          match(requirement)
            {/* Address is a compound field, we are using nesting with dot notation here. */}
            .with({ name: 'address' }, () => (
              <div>
                {Object.entries(requirement.args).map(([key, value]) => (
                  <FormField
                    key={key}
                    control={form.control}
                    name={`address.${key}`}
                    render={({ field, fieldState }) => (
                      <FormItem>
                        <FormLabel>{value.label}</FormLabel>
                        <FormControl>
                          <Input
                            autoFocus
                            placeholder={value.label}
                            {...field}
                          />
                        </FormControl>
                        <FormMessage>{fieldState.error?.message}</FormMessage>
                      </FormItem>
                    )}
                  />
                ))}
              </div>
            ))
            .with({ name: 'wallet_address' }, () => (
              <FormField
                key={requirement.requirementId}
                control={form.control}
                name={'walletAddress'}
                render={({ field, fieldState }) => (
                  <FormItem>
                    <FormLabel>{requirement.displayName}</FormLabel>
                    <FormControl>
                      <Input placeholder={requirement.displayName} {...field} />
                    </FormControl>
                    <FormDescription>{requirement.description}</FormDescription>
                    <FormMessage>{fieldState.error?.message}</FormMessage>
                  </FormItem>
                )}
              />
            ))
            .otherwise(() => (
              <FormField
                key={requirement.requirementId}
                control={form.control}
                name={requirement.name}
                render={({ field, fieldState }) => (
                  <FormItem>
                    <FormLabel>{requirement.displayName}</FormLabel>
                    <FormControl>
                      <Input placeholder={requirement.displayName} {...field} />
                    </FormControl>
                    <FormDescription>{requirement.description}</FormDescription>
                    <FormMessage>{fieldState.error?.message}</FormMessage>
                  </FormItem>
                )}
              />
            )),
        )}
  </form>
```

Each requirement field has its own structure but follows a similar structure:&#x20;

#### Email Requirement

```typescript
const emailQuestRewardRequirementSchema =
  baseQuestRewardRequirementSchema.extend({
    name: z.literal('email'),
    data: z
      .object({
        email: z.string().optional(),
      })
      .optional(),
    args: z.object({
      email: z.object({
        type: z.literal('email'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
    }),
  });
```

> Note: data.email might be empty, but if it has value, it means the user has already populated this information, and it can be re-used as a default value.&#x20;

#### Wallet Address Requirement

```typescript
const walletAddressQuestRewardRequirementSchema =
  baseQuestRewardRequirementSchema.extend({
    name: z.literal('wallet_address'),
    data: z
      .object({
        walletAddress: z.string().optional().nullable(),
      })
      .optional(),
    args: z.object({
      walletAddress: z.object({
        type: z.literal('string'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
    }),
  });
```

> Note: There are no "validation" on the accepted wallet address format, it should be done in the client.&#x20;

#### Address Requirement

```typescript
const addressQuestRewardRequirementSchema =
  baseQuestRewardRequirementSchema.extend({
    name: z.literal('address'),
    data: z
      .object({
        addressLine1: z.string().optional(),
        addressLine2: z.string().optional(),
        addressLine3: z.string().optional(),
        city: z.string().optional(),
        state: z.string().optional(),
        country: z.string().optional(),
        postalCode: z.string().optional(),
      })
      .optional(),
    args: z.object({
      addressLine1: z.object({
        type: z.literal('string'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
      addressLine2: z.object({
        type: z.literal('string'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
      addressLine3: z.object({
        type: z.literal('string'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
      city: z.object({
        type: z.literal('string'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
      state: z
        .object({
          type: z.literal('string'),
          label: z.string(),
          hidden: z.boolean(),
          required: z.boolean(),
        })
        .optional(),
      country: z.object({
        type: z.literal('string'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
      postalCode: z.object({
        type: z.literal('string'),
        label: z.string(),
        hidden: z.boolean(),
        required: z.boolean(),
      }),
    }),
  });
```

### Attaching requirements to payload

Once you gather all required information, you can pass these information in the format of `Record<string, string | Record<string, string>>`  as the `payload`argument we've discussed above in the claiming rewards section.&#x20;

### Determining prize status

Once the claim is completed, make sure you are hooking to `distributionStatus`field in the allocation entity.&#x20;

Distribution status can be one of the following

```typescript
enum QUEST_REWARD_DISTRIBUTION_STATUS {
    DISTRIBUTED = "DISTRIBUTED",
    ALLOCATED = "ALLOCATED"
}
```

> Note: Only when the status is `QUEST_REWARD_DISTRIBUTION_STATUS.DISTRIBUTED`you can assume that the reward is delivered, otherwise, the reward is pending in the queue to be delivered.&#x20;
