Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@manonthemat
Last active December 13, 2021 04:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save manonthemat/9017c96261915b696a36f69e6d62930b to your computer and use it in GitHub Desktop.
Save manonthemat/9017c96261915b696a36f69e6d62930b to your computer and use it in GitHub Desktop.
Benefits Application using Neo4j and the Cardano Blockchain

Benefits Application

Project Scope

A benefits application where organizations (e.g. companies) can contribute funds regularly (always on the first day of a month) to the benefits accounts of individuals (e.g. employees).

Individuals should have detailed and aggregate insights into their benefits. Funds in a benefit account can be spent with suitable service providers.

Deliverables

DB schema

draft of the graph model

Using the GRANDstack, we can effortlessly translate a GraphQL schema into the actual data layer with Neo4j and generate queries and mutations through augmented schema-generation on Apollo-Server.

The nodes and relationships in black represent simple core data that can and should be created through the GraphQL API and its created mutations. For greater flexibility and avoidance of UUID clashes, these nodes share a common index on the id property through implementation of the Queryable interface.

The red nodes and relationships are created through custom mutations that interact with the Plutus Application Backend. Upon successful (cron-scheduled) execution, this data represents metadata that should not be modified directly; augmented schema-generation for mutations for these nodes and relationships need to be disabled. The same applies for the blue path, which represents the generated data upon successful redemption of an Offer by a Beneficiary. Nonetheless, the unspent UTXO nodes carry the balance of Beneficiary, Contributor, and Provider wallets. These nodes implement a resolver that aggregates balances.

Useful reading: Primer on (e)UTXO

interface Queryable {
  id: ID! @id
}

enum FundingFrequency {
  MONTHLY
  QUARTERLY
  ANNUALLY
}

type Beneficiary implements Queryable {
  id: ID! @id
  name: String!
  email: String
  wallet: [Wallet] @relationship(type: "OWNS", direction: OUT)
}

"""The contributing entity (e.g. a company)."""
type Contributor implements Queryable {
  id: ID! @id
  name: String!
  """This are the _Benefit Accounts_ mentioned in the business requirements."""
  contributions: [Contribution] @relationship(type: "MAKES", direction: OUT)
  wallets: [Wallet] @relationship(type: "OWNS", direction: OUT)
}

"""The deposit data that is used for scheduling transactions."""
type Contribution implements Queryable {
  id: ID! @id
  name: String!
  """The funding amount in cent."""
  amount: Int!
  usedWallet: Wallet! @relationship(type: "USING", direction: OUT)
  """The wallets of beneficiaries to send `amount` funds to periodically."""
  fundsWallets: [Wallet] @relationship(type: "FUNDS", direction: OUT)
  offers: [Offer] @relationship(type: "COVERS", direction: OUT)
  fundingFrequency: FundingFrequency!
  createdAt: DateTime! @timestamp(operations: [CREATE])
  updatedAt: DateTime @timestamp(operations: [UPDATE])
}

type Offer implements Queryable {
  id: ID! @id
  name: String!
  """The monthly cost of being able to use this offer in cent."""
  cost: Int!
  provider: Provider! @relationship(type: "OFFERS", direction: IN)
  wallet: Wallet! @relationship(type: "USING", direction: OUT)
}

type Provider implements Queryable {
  id: ID! @id
  name: String!
  offers: [Offer] @relationship(type: "OFFERS", direction: OUT)
  wallets: [Wallet] @relationship(type: "OWNS", direction: OUT)
}

union WalletOwner = Beneficiary | Contributor | Provider

type Wallet {
  pkh: ID! @id
  owner: WalletOwner @relationship(type: "OWNS", direction: IN)
  utxos: [UTXO] @relationship(type: "WITH", direction: OUT)
  createdTransactions: [Transaction] @relationship(type: "CREATED", direction: OUT)
  balances: [Balance] @cypher(statement:"""
    MATCH (this)<-[:FUNDS]-(c:Contribution)<-[:FOR]-(u:UTXO)<-[:WITH]-(this)
    WHERE NOT EXISTS((u)-[:IS_INPUT_OF]->())
    RETURN {
      contribution: c,
      spendable: sum(u.amount)
    }
  """)
}

"""Pseudo-type to pair a wallet's contributions and their aggregate spending power."""
type Balance {
  contribution: Contribution
  spendable: Int
}

type UTXO @exclude(operations: [CREATE, UPDATE, DELETE]) {
  utxoid: ID! @id
  createdAt: DateTime! @timestamp(operations: [CREATE]) @readonly
  """The balance of this UTXO in cent."""
  amount: Int! @readonly
  """If true, this UTXO has been spent and cannot be applied as input for another transaction."""
  isSpent: Boolean! @cypher(statement:"""RETURN EXISTS((this)-[:IS_INPUT_OF]->())""")
  wallet: Wallet! @relationship(type: "WITH", direction: IN) @readonly
  """The `Contribution` category this UTXO can be spent on. If null, `amount` can be spent on anything (incl. withdrawal)."""
  contribution: Contribution @relationship(type: "FOR", direction: OUT) @readonly
  createdBy: Transaction @relationship(type: "CREATED", direction: IN) @readonly
  cocreated: Transaction @relationship(type: "IS_INPUT_OF", direction: OUT)
}

type Transaction @exclude(operations: [CREATE, UPDATE, DELETE]) {
  txid: ID! @id
  createdAt: DateTime! @timestamp(operations: [CREATE])
  inputs: [UTXO] @relationship(type: "IS_INPUT_OF", direction: IN) @readonly
  outputs: [UTXO] @relationship(type: "CREATED", direction: OUT) @readonly
  contribution: [Contribution] @relationship(type: "FOR", direction: OUT) @readonly
}

APIs

GraphQL API

Provided through the schema. However, several transactional nodes and relationships must not be created in isolation via augmented mutations (e.g. Transaction, UTXO and their relationships).

Smart Contract API (Plutus Application Backend)

The PAB facilitates the actual payment from Contributor to Beneficiary and from Beneficiary to Provider.

The Mediation Contract serves as an example how a similar contract can be built for this benefits application.

Cron Job

The cron job queries the Contribution nodes and its relevant connections to create the payloads to be sent to the PAB. These requests will be sent to a queue and be purposefully held for review for a configurable duration. There, funding requests can be manually edited, cancelled, or approved. Untouched funding requests during this review period will be automatically sent out to the PAB - which results in the actual payment within seconds.

Test Plan

  • e2e
  • resilience testing
Front End
  • cypress
GraphQL API
  • unit tests
  • load tests
Smart Contracts

High Level Architecural Diagram

FYI, the end point at arrow 7 is a bus

Glossary

Term Description
Beneficiary An entity (commonly a natural person, such as an employee) that may own one or multiple accounts.
Contribution A scheduled deposit to one or many wallets held by beneficiaries that recurs on a cycle (monthly, quarterly, or annually).
Contributor An entity (e.g. company) that contributes funds to Wallet nodes (via the smart contract API which yields resulting Transaction and UTXO nodes).
Offer A service (e.g. yoga class) offered by a Provider.
Provider An entity (e.g. yoga studio) that provides a service (Offer) that can be utilized by a Beneficiary using the funds in their Wallet.
Wallet A loose synonym is Account. (In reality, this is closer to being a collection of payment addresses that is associated with one entity that controls a private key.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment