Bounty Manager

Introduction

This guide focusses on the most common patterns of interactions with the Bounty Manager Module. Please refer to the Technical Reference section to see all public getters and setters.

The guide includes code snippets for both Inverter Network's TypeScript SDK and React SDK. Please refer to the relevant code snippets based on the SDK you are using.

Setup Requirements

  1. Set up Inverter Network SDK: Refer to the Quick Start guides for detailed instructions. See the React SDK Guide or TypeScript SDK Guide for more information.

  2. Deploy a Workflow: Refer to the Deploy a Workflow guide for detailed instructions. See the React SDK Guide or TypeScript SDK Guide for more information.

  3. Retrieve a deployed Workflow: Refer to the Operate a Workflow guide for detailed instructions. See the React SDK Guide or TypeScript SDK Guide for more information.

Managing Bounty Manager Roles

Managing roles for the Bounty Manager involves several steps, including reading role, generating role IDs, assigning roles, and revoking them.

Retrieving the roles

The Bounty Manager utilizes several roles to handle the issuance, verification, and claiming of bounties. The first step is to retrieve the available roles in the Bounty Manager, as demonstrated below:

TS SDK

const VERIFIER_ROLE = await workflow.optionalModule.LM_PC_Bounties_v1.read.VERIFIER_ROLE.run()

const BOUNTY_ISSUER_ROLE = await workflow.optionalModule.LM_PC_Bounties_v1.read.BOUNTY_ISSUER_ROLE.run()

const CLAIMANT_ROLE = await workflow.optionalModule.LM_PC_Bounties_v1.read.CLAIMANT_ROLE.run()

React SDK

  // Combined query for fetching all roles
  const rolesQuery = useQuery({
    queryKey: ['roles'],
    enabled: !!workflow.data,
    queryFn: async () => {
      if (!workflow.data) throw new Error('No workflow instance found')
      
      const VERIFIER_ROLE = await workflow.data.optionalModule.LM_PC_Bounties_v1.read.VERIFIER_ROLE.run()
      const BOUNTY_ISSUER_ROLE = await workflow.data.optionalModule.LM_PC_Bounties_v1.read.BOUNTY_ISSUER_ROLE.run()
      const CLAIMANT_ROLE = await workflow.data.optionalModule.LM_PC_Bounties_v1.read.CLAIMANT_ROLE.run()

      return {
        VERIFIER_ROLE,
        BOUNTY_ISSUER_ROLE,
        CLAIMANT_ROLE
      }
    }
  })

Check If User Has Module Roles

The following two-step process validates whether a given address has roles assigned to it:

TS SDK

// Step 1. Generate the role ids
const [GEN_VERIFIER_ROLE, GEN_BOUNTY_ISSUER_ROLE, GEN_CLAIMANT_ROLE] =
      await Promise.all(
        [VERIFIER_ROLE, BOUNTY_ISSUER_ROLE, CLAIMANT_ROLE].map((id) => {
          return workflow.authorizer.read.generateRoleId.run([
            workflow.optionalModule.LM_PC_Bounties_v1.address,
            id,
          ])
        })
      )
      
// Step 2. Check if the user has the role
const [HAS_VERIFIER_ROLE, HAS_BOUNTY_ISSUER_ROLE, HAS_CLAIMANT_ROLE] =
      await Promise.all(
        [GEN_VERIFIER_ROLE, GEN_BOUNTY_ISSUER_ROLE, GEN_CLAIMANT_ROLE].map(
          (genId) => {
            return workflow.authorizer.read.hasRole.run([
              genId,
              <wallet_address>,
            ])
          }
        )
      )

React SDK

  // Combined query for generating role IDs
  const generatedRoleIdsQuery = useQuery({
    queryKey: ['generatedRoleIds', rolesQuery.dataUpdatedAt],
    enabled: !!workflow.data && !!rolesQuery.data,
    queryFn: async () => {
      if (!workflow.data || !rolesQuery.data) throw new Error('Required data not available')

      const roleIds = [
        rolesQuery.data.VERIFIER_ROLE,
        rolesQuery.data.BOUNTY_ISSUER_ROLE,
        rolesQuery.data.CLAIMANT_ROLE
      ]

      const generatedIds = await Promise.all(
        roleIds.map((id) => {
          return workflow.data.authorizer.read.generateRoleId.run([
            workflow.data.optionalModule.LM_PC_Bounties_v1.address,
            id,
          ])
        })
      )

      return generatedIds
    }
  })

  // Combined query for checking user roles
  const userRolesQuery = useQuery({
    queryKey: ['userRoles', generatedRoleIdsQuery.dataUpdatedAt, '<wallet_address>'],
    enabled: !!workflow.data && !!generatedRoleIdsQuery.data,
    queryFn: async () => {
      if (!workflow.data || !generatedRoleIdsQuery.data) throw new Error('Required data not available')

      const [
        GEN_VERIFIER_ROLE,
        GEN_BOUNTY_ISSUER_ROLE,
        GEN_CLAIMANT_ROLE
      ] = generatedRoleIdsQuery.data

      const hasRoles = await Promise.all(
        roleIds.map((genId) => {
          return workflow.data.authorizer.read.hasRole.run([
            genId,
            '<wallet_address>',
          ])
        })
      )

      return hasRoles
    }
  })

Grant and Revoke Role

The following section demonstrates how to grant and revoke roles for a wallet address.

TS SDK

const walletAddress = sdk.walletClient.account.address

const args = [ 
    <VERIFIER_ROLE | BOUNTY_ISSUER_ROLE | CLAIMANT_ROLE>,
    walletAddress
] as const

// Grant role
const grantRoleTransactionHash = await workflow.optionalModule.LM_PC_Bounties_v1.write.grantModuleRole.run(args)

// Revoke role
const revokeRoleTransactionHash = await workflow.optionalModule.LM_PC_Bounties_v1.write.revokeModuleRole.run(args)

React SDK

  // Mutation for granting roles
  const grantRoleMutation = useMutation({
    mutationFn: async ({ role, walletAddress }: { role: NonNullable<rolesQuery['data'][number]>, walletAddress: `0x${string}` }) => {
      if (!workflow.data) throw new Error('No workflow instance found')

      const args = [role, walletAddress] as const
      return await workflow.data.optionalModule.LM_PC_Bounties_v1.write.grantModuleRole.run(args)
    }
  })

  // Mutation for revoking roles
  const revokeRoleMutation = useMutation({
    mutationFn: async ({ role: NonNullable<rolesQuery['data'][number]>, walletAddress: `0x${string}` }) => {
      if (!workflow.data) throw new Error('No workflow instance found')

      const args = [role, walletAddress] as const
      return await workflow.data.optionalModule.LM_PC_Bounties_v1.write.revokeModuleRole.run(args)
    }
  })

Bounty Creation

Add a Bounty

The following section explains how to add a bounty to the Bounty Manager:

TS SDK

import { decodeEventLog, parseAbiItem } from 'viem'

let bountyId = string

const addBountyTransactionHash = await workflow.optionalModule.LM_PC_Bounties_v1.write.addBounty.run([
    // Minimum Payment Amount
    '1000',
    // Maximum Payment Amount
    '10000',
    // Details ( type any )
    {
        timestamp: 1734448333
        message: 'hello' 
    }
], {
    confirmations: 1,
    // !Optional! You can get the list of bounty id's from a different endpoint
    onConfirmation: (receipt) => {
      // Define the ABI for the BountyAdded event
      const addBountyOutputAbi = parseAbiItem(
        'event BountyAdded(uint256 indexed bountyId, uint256 minimumPayoutAmount, uint256 maximumPayoutAmount, bytes details)'
      )

      // Decode the logs using the ABI
      const decodedLogs = decodeEventLog({
        abi: [addBountyOutputAbi],
        data: receipt.logs[0].data,
        topics: receipt.logs[0].topics,
      })

      // Retrieve the bountyId from the decoded logs
      bountyId = decodedLogs.args.bountyId.toString()
    },
})

React SDK

  // Add Single Bounty Mutation
  const addBountyMutation = useMutation({
    mutationFn: async ({ minAmount, maxAmount, details }) => {
      if (!workflow.data) throw new Error('No workflow instance found')
      
      let bountyId = string

      const transactionHash = await workflow.data.optionalModule.LM_PC_Bounties_v1.write.addBounty.run(
        [minAmount, maxAmount, details],
        {
          confirmations: 1,
          onConfirmation: (receipt) => {
            const addBountyOutputAbi = parseAbiItem(
              'event BountyAdded(uint256 indexed bountyId, uint256 minimumPayoutAmount, uint256 maximumPayoutAmount, bytes details)'
            )

            const decodedLogs = decodeEventLog({
              abi: [addBountyOutputAbi],
              data: receipt.logs[0].data,
              topics: receipt.logs[0].topics,
            })

            bountyId = decodedLogs.args.bountyId.toString()
          },
        }
      )
      
     return { transactionHash, bountyId }
    }
  })

Add Bounty in Batches

The following section explains how to add multiple bounties in batches

TS SDK

import { decodeEventLog, parseAbiItem } from 'viem'

let bountyDetails = [
    // Minimum Payment Amounts
    ['1000`, '2000', '3000'],
    // Maximum Payment Amount
    ['10000', '20000', '30000'],
    // Details ( type any )
    [{
        timestamp: 1734448333
        message: 'hello' 
    },
    {
        timestamp: 1734448334
        message: 'hello1' 
    },
    {
        timestamp: 1734448335
        message: 'hello2' 
    }]
] as const

const addBountyTransactionHash = await workflow.optionalModule.LM_PC_Bounties_v1.write.addBountyBatch.run(bountyDetails)

React SDK

  // Add Batch Bounties Mutation
  const addBountyBatchMutation = useMutation({
    mutationFn: async (bountyDetails) => {
      if (!workflow.data) throw new Error('No workflow instance found')

      return await workflow.data.optionalModule.LM_PC_Bounties_v1.write.addBountyBatch.run(
        bountyDetails
      )
    }
  })

Read Bounty

The following section explains how to read a given bounty information based on the bountyId

TS SDK

const bountyInformation = await workflow.optionalModule.LM_PC_Bounties_v1.read.getBountyInformation.run(bountyId)

const {
    minimumPayoutAmount
    maximumPayoutAmount
    details: {
        timestamp,
        message
    }
    locked
} = bountyInformation

console.log(bountyInformation)

React SDK

// Get Bounty Information Query
  const useBountyInformation = (bountyId) => useQuery({
    queryKey: ['bountyInformation', bountyId],
    enabled: !!bountyId && !!workflow.data,
    queryFn: async () => {
      if (!workflow.data) throw new Error('No workflow instance found')

      return await workflow.data.optionalModule.LM_PC_Bounties_v1.read.getBountyInformation.run(bountyId)
    }
  })

Bounty Claim

Add Bounty Claim

The following section explains how to submit a bounty claim:

TS SDK

import { decodeEventLog, parseAbiItem } from 'viem'

let claimId = string

const contributers = 
  [
    // Bounty Id
    bountyId,
    // Contributers
    [{ addr: sdk.walletClient.account.address, claimAmount: '100' }],
    // Details ( type any )
    {
      claimUrl: 'https://www.google.com',
    },
  ] as const

const claimTransactionHash = await workflow.optionalModule.LM_PC_Bounties_v1.write.addClaim.run(
          contributers,
          {
            confirmations: 1,
            // !Optional! You can get the list of claim id's from a different endpoint
            onConfirmation: (receipt) => {
              const claimAddedAbi = parseAbiItem(
                'event ClaimAdded(uint256 indexed claimId,uint256 indexed bountyId,(address,uint256)[] contributors,bytes details)'
              )

              const decodedLogs = decodeEventLog({
                abi: [claimAddedAbi],
                data: receipt.logs[0].data,
                topics: receipt.logs[0].topics,
              })

              claimId = decodedLogs.args.claimId.toString()
            },
          }
        )

React SDK

  // Add Claim Mutation
  const addClaimMutation = useMutation({
    mutationFn: async ({ bountyId, contributors, details }) => {
      if (!workflow.data) throw new Error('No workflow instance found')
      
      let bountyId: string

      const transactionHash = await workflow.data.optionalModule.LM_PC_Bounties_v1.write.addClaim.run(
        [bountyId, contributors, details],
        {
          confirmations: 1,
          onConfirmation: (receipt) => {
            const claimAddedAbi = parseAbiItem(
              'event ClaimAdded(uint256 indexed claimId,uint256 indexed bountyId,(address,uint256)[] contributors,bytes details)'
            )

            const decodedLogs = decodeEventLog({
              abi: [claimAddedAbi],
              data: receipt.logs[0].data,
              topics: receipt.logs[0].topics,
            })

            return decodedLogs.args.claimId.toString()
          },
        }
      )
      
      return { transactionHash, bountyId }
    }
  })

Read Bounty Claim

The following section explains how to read a submitted bounty claim

TS SDK

const claimInformation = await workflow.optionalModule.LM_PC_Bounties_v1.read.getClaimInformation.run(claimId)

const {
  details: { claimUrl },
  bountyId,
  claimed,
  contributors,
} = claimInformation

console.log(claimInformation)

React SDK

  // Get Claim Information Query
  const useClaimInformation = (claimId) => useQuery({
    queryKey: ['claimInformation', claimId],
    enabled: !!claimId && !!workflow.data,
    queryFn: async () => {
      if (!workflow.data) throw new Error('No workflow instance found')

      return await workflow.data.optionalModule.LM_PC_Bounties_v1.read.getClaimInformation.run(claimId)
    }
  })

Verify Bounty Claim

Verify a Claim

The following section explains how to verify a claim providing the claimId and contributors as parameters:

TS SDK

const hash = await workflow.optionalModule.LM_PC_Bounties_v1.write.verifyClaim.run([
    claimId,
    contributors,
])

React SDK

  // Verify Claim Mutation
  const verifyClaimMutation = useMutation({
    mutationFn: async ({ claimId, contributors }) => {
      if (!workflow.data) throw new Error('No workflow instance found')

      return await workflow.data.optionalModule.LM_PC_Bounties_v1.write.verifyClaim.run([
        claimId,
        contributors,
      ])
    }
  })

  return {
    addBountyMutation,
    addBountyBatchMutation,
    useBountyInformation,
    addClaimMutation,
    useClaimInformation,
    verifyClaimMutation
  }
}

Last updated