abridged/collabland-dev

ETH Shanghai Hackathon: Bounty 1: Create Verifiable Credentials Collab.Land Can Consume

Closed this issue · 2 comments

Prize Title

Create Verifiable Credentials [Collab.Land](http://Collab.Land) Can VC Gate From

Prize Bounty

5000 USDC

Possible introduction to MetaCartel for further funding

Challenge Description

[Collab.Land](http://Collab.Land) is a token gating platform that allows communities to restrict access to private communities based on NFT and Fungible token ownership. Verifiable Credentials (VCs) are a new category of signed off chain messages.
https://en.wikipedia.org/wiki/Verifiable_credentials

[Collab.Land](http://Collab.Land) will soon allow token gating based on 3rd party Verifiable Credentials. Some examples include token gating based on: “Your platforms most active users”, “different skill tiers in your online game as defined by VCs issued to users”, “Web2 memberships that come with VCs”. The possibilities are endless. What matters though is the value creation for your users and the Collab.Land ecosystem.

Your challenge is to generate VCs for users from a new or existing platform and propose how they could be used by a community admin to gate community specific chats or features.

For example, all United Airlines Platinum Members gain access to a VC gated customer support real time chat room... You get the idea.

Following the W3C Verifiable Credentials Standard. This proposal from Nuggets to [Collab.Land](http://Collab.Land) provides many useful links: https://github.com/orgs/abridged/discussions/64

Collab.Lands current VC schema: APPENDIX A

Submission Requirements

  1. a powerpoint/ google slides - still working on an example to share. these are the current slide titles
  • Executive Brief
  • Simple Summary
  • Abstract
  • Motivation
  • Specifics
  • Opportunity
  • User Impact
  1. a video explaining the proposal under 5 minutes (shows energy and personality)
  2. A video demoing the code and VC generation in under 5 minutes

Judging Criteria

A successful project will show a compelling use case for issuing VCs for use by Community Admins.

Criteria will include:
Value created for the end user
effort put in by the team/ maturity of POC
Feasibility of becoming a widely used product/ use case

Value add to the [Collab.Land](http://Collab.Land) ecosystem

Compelling story that users can understand

Ability to follow through and take the POC into an MVP and eventually production

Winner Announcement Date

One winner will be chosen from all the submissions and will be announced 1 week after submissions close.

Resources

Teams can ask questions in our Discord here:
https://discord.gg/thmcqDx3ys
Note: real time support is only available for hackathon related questions.
Our FAQ and all other questions should be directed to https://collabland.freshdesk.com/

Refer to the "Consume VCs" bounty for additional information: https://github.com/orgs/abridged/discussions/63

APPENDIX A

//=============================================================================
// Licensed Materials - Property of Abridged, Inc.
// (C) Copyright Abridged, Inc. 2022
//=============================================================================

import {auth} from '@collabland/api-security';
import {debugFactory, generateIdSync} from '@collabland/common';
import {userOnly} from '@collabland/component-authorization';
import {experimental} from '@collabland/component-platform';
import {VeramoService, VERAMO_SERVICE} from '@collabland/component-veramo';
import {BindingScope, inject, injectable} from '@loopback/core';
import {get, oas, post, requestBody} from '@loopback/rest';
import {SecurityBindings} from '@loopback/security';
import {DISCORD_SERVICE} from '../keys';
import type {DiscordService} from '../services/discord.service';
import {getDiscordGuildId} from '../services/discord.service';
import {VC} from '../types';

const debug = debugFactory('collabland:veramo-vc');

export interface UserInfo {
  provider?: string;
  alias: string;
  nickname: string;
  name: string;
  picture: string;
}

interface UserProfile {
  id: string;
  verifying: VerifyingRoles[];
}

interface VerifyingRoles {
  communityId: string;
  roleId: string;
  roleName: string;
}

interface DiscordUser {
  roles: string[];
  avatar: string;
  joined_at: string;
  user: {
    id: string;
    username: string;
    avatar: string;
    discriminator: string;
    public_flags: number;
  };
}

interface CredentialSubjectData {
  id: string;
  discordUserId: string;
  discordUserName: string; //`${user.username}#${user.discriminator}`,
  discordUserAvatar: string; //user.displayAvatarURL({ size: 512 }),
  discordGuildId: string; //
  discordGuildName: string;
  discordGuildAvatar: string; //guild.iconURL({ size: 512 }),
  discordRoleId: string;
  discordRoleName: string;
  description: string;
  exp?: number;
}
interface CredentialSubject {
  data: CredentialSubjectData | undefined;
  error?: string;
}

interface VCResponse {
  data: VC[];
  error?: string;
}

@experimental()
@auth()
@userOnly()
@injectable({scope: BindingScope.SINGLETON})
export class VeramoController {
  constructor(
    @inject(VERAMO_SERVICE)
    private veramoService: VeramoService,
    @inject(DISCORD_SERVICE) private discordService: DiscordService,
  ) {}

  /**
   * Get user VCs
   */
  @get('/veramo/did/{user}')
  @oas.response(200, {
    type: 'array',
    items: {
      type: 'object',
      properties: {
        chain_id: {type: 'string'},
        deployment_address: {type: 'string'},
      },
    },
  })
  async getUserVCs(@inject(SecurityBindings.USER) currentUser: UserProfile) {
    return [];
  }

  /**
   * Generate VCs for based on roles
   *
   * @example credentialSubject: {
        // user
        discordUserId: '412814486332899338',
        discordUserName: `Agnes | Collab.Land#2623`,
        discordUserAvatar: '14f020ef756a37284281ebbba8ea84a9',
        // community
        discordGuildId: '903656119656910908',
        discordGuildName: 'CL',
        discordGuildAvatar: '',
        // role
        discordRoleId: '907634262461784084',
        discordRoleName: 'BKA',
        description: 'user Agnes has role BKA in community CL',
      },
   *
   */
  @post('/veramo/identity')
  @oas.response(200, {
    type: 'object',
  })
  async generateVc(
    @inject(SecurityBindings.USER) currentUser: UserProfile,
    @requestBody()
    profile: {aeToken: string; verifying: VerifyingRoles[]},
  ): Promise<VCResponse> {
    debug('decode AE token', currentUser);
    debug('roles', profile.verifying);

    const builtCredentials: CredentialSubject[] = [];

    for (const role of profile.verifying) {
      const roleInfo = await this.buildRoleInfo(role, currentUser.id);
      builtCredentials.push(roleInfo);
    }
    debug('builtCredentials', builtCredentials);
    // TODO: enable other platforms
    const userId = `DIS#USER#${currentUser.id}`;
    return this.getIdentity(userId, builtCredentials);
  }

  async getIdentity(userId: string, verifying: CredentialSubject[]) {
    return this.getIdentityAndUpdateProfile(userId, verifying);
  }

  /**
   * Veramo agent generates VC
   * @param userId
   * @param verifying
   * @returns
   */
  async getIdentityAndUpdateProfile(
    userId: string,
    verifying: CredentialSubject[],
  ): Promise<VCResponse> {
    const issuer = await this.veramoService.agent.didManagerGetOrCreate({
      alias: 'CollabLand',
      // provider: userInfo.provider,
    });

    const vcs: VC[] = [];
    for (const credential of verifying) {
      if (credential.error) {
        return {error: `Invalid community id: ${credential.error}`, data: []};
      }
      const vc = (await this.veramoService.agent.createVerifiableCredential({
        save: true,
        proofFormat: 'jwt',
        credential: {
          id: generateIdSync(),
          '@context': ['https://www.w3.org/2018/credentials/v1'],
          type: ['VerifiableCredential', 'DiscordRole'],
          issuer: {id: issuer.did},
          issuanceDate: new Date().toISOString(),
          credentialSubject: credential.data,
        },
      })) as unknown as VC;
      debug('VC', vc);
      vcs.push(vc);
    }
    return {data: vcs};
  }

  /**
   * Build role information based on the given userId and communityId
   * @param role selected role
   * @param userId example: 412814486332899338
   * @returns
   */
  async buildRoleInfo(role: VerifyingRoles, userId: string, ttl = -1) {
    const guildId = getDiscordGuildId(role.communityId);
    if (!guildId)
      return {
        error: `Invalid CommunityPk ${role.communityId}`,
        data: undefined,
      } as CredentialSubject;
    const commInfo = await this.discordService.getGuildInfo(guildId);
    const userInfo = (await this.discordService.getMemberInfo(
      guildId,
      userId,
    )) as unknown as DiscordUser;
    debug('Community info', commInfo);
    debug('User info', userInfo);
    const data = {
      id: userId,
      // user
      discordUserId: userId,
      discordUserName: `${userInfo.user.username}#${userInfo.user.discriminator}`,
      discordUserAvatar: userInfo.user.avatar
        ? this.discordService.cdn.avatar(userId, userInfo.user.avatar)
        : '',
      // community
      discordGuildId: commInfo.id,
      discordGuildName: commInfo.name,
      discordGuildAvatar: commInfo.icon
        ? this.discordService.cdn.icon(commInfo.id, commInfo.icon)
        : '',
      // role
      discordRoleId: role.roleId,
      discordRoleName: role.roleName,
      description: `${userInfo.user.username}#${userInfo.user.discriminator} has role ${role.roleName} in Discord community ${commInfo.name}`,
    } as CredentialSubjectData;
    if (ttl !== -1) {
      data.exp = Date.now() / 1000 + ttl;
    }
    return {
      data,
    } as CredentialSubject;
  }
}

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


This issue now has a funding of 5000.0 USDC (5000.0 USD @ $1.0/USDC) attached to it.

Issue Status: 1. Open 2. Started 3. Submitted 4. Done


Work has been started.

These users each claimed they can complete the work by 2 hours ago.
Please review their action plans below:

1) fifteen42 has been approved to start work.

DAOU can turn your activities, connections, contributions, etc in DAOs into social capital by Social Oracle Protocol and link your social capital to your soulbound token (SBT).
2) shreyan001 has been approved to start work.

I am currently building a decentralized social network/DAO for political communities where entry is based on on-chain surveys, which can be done only once, I do require guidance for this project off the hackathon.
3) archenetwork has been approved to start work.

Arche is a community oriented space for game enthusiasts for a better Web 3.0 experience. We aims to bring good Web3 games to people and bridge the connection amongst GameFi players to enjoy, earn, create, govern and assembling those to build a better game Metaverse and society.

Learn more on the Gitcoin Issue Details page.