Using Open Banking Data to Maximize Mortgage Savings. CSESoc X Pearler Competition Winner.

Overview

Mortgage Manager

Overview

Mortgage Manager is an open-source online home-loan comparison tool using product data from the Consumer Data Standards Banking APIs. Authorised deposit-taking institutions are obligated to provide information on the products that they offer via a set of APIs defined by a standardised set of specifications allowing for machine processing.

The individual institutions are responsible for hosting and mainting their respective APIs so to get an overview of all products offered in Australia I expanded my Open Banking Tracker which downloads all product information daily and publishes it to GitHub to also include an overview file for home-loans specififcally which is used to generate the main table in Mortgage Manager.

This data from GitHub is used to power the Mortgage Manager website which allows users to quickly and easily compare over 1000 mortgage products from 100+ brands. The website is written in React & TypeScript and includes features to filter and sort the returned matches along with viewing additional information such as eligability requirements and fees.

Light Mode Dark Mode
Light Mode Dark Mode
Fee Info Rate Info
image image

Collecting the Data

The Open Banking Tracker was started to simplify the process of accessing Austrlian banking product information by aggregating the data products from over 110 institutions. The Australian Competition & Consumer Commission maintains a register of all brands obligated under the Consumer Data Right to offer publicly available Banking APIs.

https://api.cdr.gov.au/cdr-register/v1/banking/register
{
    "legalEntityId": "988846E1-DF94-EA11-A831-000D3A8842E1",
    "legalEntityRef": "DHBNK000001",
    "legalEntityName": "Westpac Banking Corporation",
    "industry": "banking",
    "emailAddress": "[email protected]",
    "serviceAddressStreetAddress1": "The Proper Officer, Legal & Secretariat, Level 18",
    "serviceAddressStreetAddress2": "275 Kent Street",
    "serviceAddresSuburb": "Sydney",
    "serviceAddresState": "NSW",
    "serviceAddressPostcode": "2000",
    "website": "https://www.westpac.com.au/",
    "cdrPolicyUrl": "https://www.westpac.com.au/consumer-data-right-policy",
    "abn": "33007457141",
    "accreditationDate": "2020-07-31T14:00:00Z",
    "logoUrl": "https://banking.westpac.com.au/wbc/banking/Themes/Default/Desktop/WBC/Core/Images/logo_white_bg.png.ce5c4c19ec61b56796f0e218fc8329c558421fd8.png",
    "brands": [
    {
        "brandRef": "000142",
        "brandName": "Bank of Melbourne",
        "brandDescription": "You have the will. We have the way.",
        "industry": "banking",
        "website": "https://www.bankofmelbourne.com.au",
        "cdrPolicyUrl": "https://www.bankofmelbourne.com.au/content/dam/bom/downloads/bom_cdr.pdf",
        "logoUrl": "https://www.bankofmelbourne.com.au/content/dam/bom/images/home/BOM-logo_1200x1200.jpg",
        "productReferenceDataApi": "https://digital-api.bankofmelbourne.com.au/cds-au/v1/banking/products",
        "softwareProducts": [],
        "status": "ACTIVE",
        "participantType": "Data Holder"
    },
    {
        "brandRef": "000141",
        "brandName": "BankSA",
        "brandDescription": "Backing the growth of South Australia. ",
        "industry": "banking",
        "website": "https://www.banksa.com.au",
        "cdrPolicyUrl": "https://www.banksa.com.au/content/dam/bsa/downloads/bsa_cdr_policy.pdf",
        "logoUrl": "https://www.banksa.com.au/content/dam/bsa/images/home/BSA-logo_1200x1200.jpg",
        "productReferenceDataApi": "https://digital-api.banksa.com.au/cds-au/v1/banking/products",
        "softwareProducts": [],
        "status": "ACTIVE",
        "participantType": "Data Holder"
    },
    {
        "brandRef": "000002",
        "brandName": "Westpac",
        "industry": "banking",
        "website": "https://www.westpac.com.au/",
        "cdrPolicyUrl": "https://www.westpac.com.au/consumer-data-right-policy",
        "logoUrl": "https://banking.westpac.com.au/wbc/banking/Themes/Default/Desktop/WBC/Core/Images/logo_white_bg.png.ce5c4c19ec61b56796f0e218fc8329c558421fd8.png",
        "productReferenceDataApi": "https://digital-api.westpac.com.au/cds-au/v1/banking/products",
        "softwareProducts": [],
        "status": "ACTIVE",
        "participantType": "Data Holder"
    }
    ],
    "legalEntityAssociations": [],
    "status": "ACTIVE",
    "lastUpdated": "2022-06-10T04:25:53Z",
    "participantType": "Data Holder"
}

This register is provided in a machine readable format which can easily be processed using a simple Python script to extract the productReferenceDataApi and other information for each brand. This script saves the processed data as a JSON file that can be used to download the individual product data from each brand.

r = requests.get('https://api.cdr.gov.au/cdr-register/v1/banking/register')
available_brands = r.json()
available_brands = available_brands['registerDetails']

for entity in available_brands:
    for brand in entity['brands']:
        if 'productReferenceDataApi' in brand:
            brands[brand['brandRef']]['productReferenceDataApi'] = brand['productReferenceDataApi']

brands_file = open("brands/brands.json", "w")
json.dump(brands, brands_file, indent = 4)
brands_file.close()
{
    "000142": {
        "legalEntityId": "988846E1-DF94-EA11-A831-000D3A8842E1",
        "legalEntityName": "Westpac Banking Corporation",
        "accreditationDate": "2020-07-31T14:00:00Z",
        "brandRef": "000142",
        "brandName": "Bank of Melbourne",
        "productReferenceDataApi": "https://digital-api.bankofmelbourne.com.au/cds-au/v1/banking/products",
        "logoUrl": "https://www.bankofmelbourne.com.au/content/dam/bom/images/home/BOM-logo_1200x1200.jpg",
        "cdrPolicyUrl": "https://www.bankofmelbourne.com.au/content/dam/bom/downloads/bom_cdr.pdf",
        "website": "https://www.bankofmelbourne.com.au",
        "brandDescription": "You have the will. We have the way."
    },
    "000141": {
        "legalEntityId": "988846E1-DF94-EA11-A831-000D3A8842E1",
        "legalEntityName": "Westpac Banking Corporation",
        "accreditationDate": "2020-07-31T14:00:00Z",
        "brandRef": "000141",
        "brandName": "BankSA",
        "productReferenceDataApi": "https://digital-api.banksa.com.au/cds-au/v1/banking/products",
        "logoUrl": "https://www.banksa.com.au/content/dam/bsa/images/home/BSA-logo_1200x1200.jpg",
        "cdrPolicyUrl": "https://www.banksa.com.au/content/dam/bsa/downloads/bsa_cdr_policy.pdf",
        "website": "https://www.banksa.com.au",
        "brandDescription": "Backing the growth of South Australia. "
    },
    "000002": {
        "legalEntityId": "988846E1-DF94-EA11-A831-000D3A8842E1",
        "legalEntityName": "Westpac Banking Corporation",
        "accreditationDate": "2020-07-31T14:00:00Z",
        "brandRef": "000002",
        "brandName": "Westpac",
        "productReferenceDataApi": "https://digital-api.westpac.com.au/cds-au/v1/banking/products",
        "logoUrl": "https://banking.westpac.com.au/wbc/banking/Themes/Default/Desktop/WBC/Core/Images/logo_white_bg.png.ce5c4c19ec61b56796f0e218fc8329c558421fd8.png",
        "cdrPolicyUrl": "https://www.westpac.com.au/consumer-data-right-policy",
        "website": "https://www.westpac.com.au/"
    }
}

The next step in the process is to download the list of all offered products from each brand by reffering to the provided productReferenceDataApi in the processed file. These endpoints must return data according the the GET /banking/products API documented here. The data is downloaded and saved for each brand using another Python script.

raw_file = open("brands/brands.json", "r")
brands = json.load(raw_file)
raw_file.close()

for brand in brands:
    try:
        response = get_data(brands[brand]['productReferenceDataApi'])
        path = 'brands/products/' + brand + ".json"

        raw_file = open(path, "w")
        json.dump(response, raw_file, indent = 4)
        raw_file.close()
{
  "data": {
    "products": [
      {
        "productId": "string",
        "effectiveFrom": "string",
        "effectiveTo": "string",
        "lastUpdated": "string",
        "productCategory": "BUSINESS_LOANS",
        "name": "string",
        "description": "string",
        "brand": "string",
        "brandName": "string",
        "applicationUri": "string",
        "isTailored": true,
        "additionalInformation": {
          "overviewUri": "string",
          "termsUri": "string",
          "eligibilityUri": "string",
          "feesAndPricingUri": "string",
          "bundleUri": "string",
          "additionalOverviewUris": [
            {
              "description": "string",
              "additionalInfoUri": "string"
            }
          ],
          "additionalTermsUris": [
            {
              "description": "string",
              "additionalInfoUri": "string"
            }
          ],
          "additionalEligibilityUris": [
            {
              "description": "string",
              "additionalInfoUri": "string"
            }
          ]
        },
        "cardArt": [
          {
            "title": "string",
            "imageUri": "string"
          }
        ]
      }
    ]
  },
  "links": {
    "self": "string",
    "first": "string",
    "prev": "string",
    "next": "string",
    "last": "string"
  },
  "meta": {
    "totalRecords": 0,
    "totalPages": 0
  }
}

This response will contain a list containing an overview of the products offered by the brand with each product featuring a unique ID which can be used to download more specific details. This specific product data can be downloaded using the GET /banking/products/{productId} API documented here. These detailed product information documents are once again downloaded by another Python script which works through all the product overview documents previously downloaded.

for file in os.listdir('brands/products/'):
    raw_file = open("brands/products/"+file, "r")
    brand = json.load(raw_file)
    raw_file.close()

    try:
        for product in brand['data']['products']:
            try:
                url = (brands[id]['productReferenceDataApi'] + "/" + product['productId'])
                path = 'brands/product/' + id + "/" + product['productId'] + ".json"

                raw_file = open(path, "w")
                json.dump(response, raw_file, indent = 4)
                raw_file.close()
{
  "data": {
    "productId": "string",
    "effectiveFrom": "string",
    "effectiveTo": "string",
    "lastUpdated": "string",
    "productCategory": "BUSINESS_LOANS",
    "name": "string",
    "description": "string",
    "brand": "string",
    "brandName": "string",
    "applicationUri": "string",
    "isTailored": true,
    "additionalInformation": {
      "overviewUri": "string",
      "termsUri": "string",
      "eligibilityUri": "string",
      "feesAndPricingUri": "string",
      "bundleUri": "string",
      "additionalOverviewUris": [
        {
          "description": "string",
          "additionalInfoUri": "string"
        }
      ],
      "additionalTermsUris": [
        {
          "description": "string",
          "additionalInfoUri": "string"
        }
      ],
      "additionalEligibilityUris": [
        {
          "description": "string",
          "additionalInfoUri": "string"
        }
      ]
    },
    "cardArt": [
      {
        "title": "string",
        "imageUri": "string"
      }
    ],
    "features": [
      {
        "featureType": "ADDITIONAL_CARDS",
        "additionalValue": "string",
        "additionalInfo": "string",
        "additionalInfoUri": "string"
      }
    ],
    "constraints": [
      {
        "constraintType": "MAX_BALANCE",
        "additionalValue": "string",
        "additionalInfo": "string",
        "additionalInfoUri": "string"
      }
    ],
    "eligibility": [
      {
        "eligibilityType": "BUSINESS",
        "additionalValue": "string",
        "additionalInfo": "string",
        "additionalInfoUri": "string"
      }
    ],
    "fees": [
      {
        "name": "string",
        "feeType": "DEPOSIT",
        "amount": "string",
        "balanceRate": "string",
        "transactionRate": "string",
        "accruedRate": "string",
        "accrualFrequency": "string",
        "currency": "string",
        "additionalValue": "string",
        "additionalInfo": "string",
        "additionalInfoUri": "string",
        "discounts": [
          {
            "description": "string",
            "discountType": "BALANCE",
            "amount": "string",
            "balanceRate": "string",
            "transactionRate": "string",
            "accruedRate": "string",
            "feeRate": "string",
            "additionalValue": "string",
            "additionalInfo": "string",
            "additionalInfoUri": "string",
            "eligibility": [
              {
                "discountEligibilityType": "BUSINESS",
                "additionalValue": "string",
                "additionalInfo": "string",
                "additionalInfoUri": "string"
              }
            ]
          }
        ]
      }
    ],
    "lendingRates": [
      {
        "lendingRateType": "BUNDLE_DISCOUNT_FIXED",
        "rate": "string",
        "comparisonRate": "string",
        "calculationFrequency": "string",
        "applicationFrequency": "string",
        "interestPaymentDue": "IN_ADVANCE",
        "repaymentType": "INTEREST_ONLY",
        "loanPurpose": "INVESTMENT",
        "tiers": [
          {
            "name": "string",
            "unitOfMeasure": "DAY",
            "minimumValue": 0,
            "maximumValue": 0,
            "rateApplicationMethod": "PER_TIER",
            "applicabilityConditions": {
              "additionalInfo": "string",
              "additionalInfoUri": "string"
            },
            "additionalInfo": "string",
            "additionalInfoUri": "string"
          }
        ],
        "additionalValue": "string",
        "additionalInfo": "string",
        "additionalInfoUri": "string"
      }
    ]
  },
  "links": {
    "self": "string"
  },
  "meta": {}
}

There are over 4000 individual files from over 110 different insitutions with a total size of over 50 MB detailing the various banking products that they offer and manually downloading this information for a specific application would be practically impossible. The Open Banking Tracker respository simplififies this process by automatically collecting these files daily and uploading them to GitHub along with creating an archived ZIP files of all products each day which can be used for historical reference.

image

The advantage of using Git for hosting and updating these files is the ability to view the specific changes when a product is updated such as a new interest rate or fee amount. The collection of Python scripts is run daily using GitHub actions and takes approximately 2 hours to complete the entire process.

on: 
  workflow_dispatch:
  schedule:
    - cron: "0 0 * * *"

jobs:
  update-data:
    name: Fetch data
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Setup Python
        uses: actions/setup-python@v3

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Download latest list
        run: python main.py

      - name: Download Product Overviews
        run: python products.py

      - name: Download Products
        run: python product.py

      - name: Commit changes
        uses: EndBug/add-and-commit@v9
        with:
          message: Updated files
          default_author: github_actions

Displaying the Data

The Mortgage Manager website is created with TypeScript, React 18 along with Material UI and material-table. This was my first major project creating using React so many of the specific implementations may not follow best practices but where possible I did attempt to create high-quality code with type checking.

image

The main highlight of the site is the main table displaying all the matching home-loans along with information about them such as interest rate, period, brand, and estimated monthly repayment. This table is created using material-table a powerful library which extends the functionality of the table object from Material UI. The material-table object requires a columns and data entry to be provided to designate what data should be displayed and how. The overview data is loaded from the Open Banking Tracker on GitHub into React using useEffect.

  useEffect(() => {
    fetch(
      `https://raw.githubusercontent.com/LukePrior/open-banking-tracker/main/aggregate/RESIDENTIAL_MORTGAGES/data.json`
    )
      .then((response) => response.json())
      .then((data) => {
        setStore(data);
      });
  }, []);

The columns object contains various advanced features to control the rendering, search properites, sorting algoritms and over overides that are tailored specifically for mortgage product information. The following code snippet shows the section of columns dedicated to the monthly repayment column and how it is calculated and sorted.

{
    title: "Amount",
    field: "amount",
    render: (rowData) => {
    let i = rowData.rate[rowData.i].rate / 12 / 100;
    let n = 300;
    let p;
    if (value === null) {
        p = 0;
    } else if (typeof value === "string") {
        p = parseFloat(value);
    } else {
        p = value;
    }
    let r = Math.round((p * (i * (1 + i) ** n)) / ((1 + i) ** n - 1));
    return <p>${r.toLocaleString()}</p>;
    },
    customSort: (a, b) => {
    return a.rate[a.i].rate - b.rate[b.i].rate;
    }
}

The individual products contain vastly more information that can be displayed on a single row in the table so a expanded view was implemented that allows detailed information for any product to be loaded. This data includes properties such as extended descriptions, features, eligability requirements, special rates, and fees. The data for any specific product is downloaded from GitHub when the expandedn view is opened with specific views programmed to display if the data cannot be found or the connection is lost.

const handleClick = async () => {
    setIsLoading(true);

    try {
      const response = await fetch(url, {
        method: "GET",
        headers: {
          Accept: "application/json"
        }
      });

      const result = await response.json();

      setDetailed(result);
    } catch (err) {
      setErr("Error loading");
    } finally {
      setIsLoading(false);
    }
};

The information is split across multiple selectable tabs to minimise the height of the expanded view and allow for easier navigation of the website. These tabs are another component provided by Material UI helping to ensure that a conistent design is maintained across all sections and views.

image

<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
    <Tabs
    value={value}
    onChange={handleChange}
    aria-label="basic tabs example"
    variant="scrollable"
    >
    <Tab label="Details" {...a11yProps(0)} sx={{ width: 150 }} />
    <Tab label="Features" {...a11yProps(1)} sx={{ width: 150 }} />
    <Tab label="Requirements" {...a11yProps(2)} sx={{ width: 150 }} />
    <Tab label="Rates" {...a11yProps(3)} sx={{ width: 150 }} />
    <Tab label="Fees" {...a11yProps(4)} sx={{ width: 150 }} />
    </Tabs>
</Box>

The specific tabs that can potentially contain a large amount of information such as features, requirements, rates, and fees are further organised with individual chip modules for each individual icons. These chips can be selected to display teh specific information for each item. This information is drawn on a new component to add depth to the website per Material Design guidelines.

image

The tool needs to accept specific user parameters in order to only show relevant mortgages such as those for a specific amount, period, or repayment type. This can be achieved by created a user interface where several options can be selected to filter the available mortgages. This interface is constructed using Material UI components such as buttons and text fields that allow the user to either select a specific option or show all available products.

<Grid item style={{ display: "flex" }}>
    <ToggleButtonGroup
    value={loanPeriod}
    exclusive
    onChange={handleLoanPeriod}
    color="primary"
    >
    <ToggleButton value="0">Variable</ToggleButton>
    <ToggleButton value="1">1 YR</ToggleButton>
    <ToggleButton value="2">2 YR</ToggleButton>
    <ToggleButton value="3">3 YR</ToggleButton>
    <ToggleButton value="4">4 YR</ToggleButton>
    <ToggleButton value="5">5 YR</ToggleButton>
    <ToggleButton value="10">10 YR</ToggleButton>
    </ToggleButtonGroup>
</Grid>

image

These buttons and fields trigger a filter of the table data when they are updated, this filter checks all the fields and only returns products that meet all of them.

let newData = localStore.filter((product: any) => {
    let flag = false;
    product.rate.forEach((rate: rateData, index: number) => {
    if (
        (loanPeriod === null ||
        (loanPeriod === "0" &&
            rate.lendingRateType === "VARIABLE" &&
            flag === false) ||
        ("period" in rate &&
            rate.period === parseInt(loanPeriod) * 12 &&
            flag === false)) &&
        (loanPayment === null ||
        (loanPayment === "0" &&
            rate.repaymentType === "PRINCIPAL_AND_INTEREST") ||
        (loanPayment === "1" && rate.repaymentType === "INTEREST_ONLY")) &&
        (loanBig === null ||
        (loanBig === "0" &&
            ["000002", "000004", "000006", "000008"].includes(
            product.brandId
            )))
    ) {
        product.i = index;
        flag = true;
    }
    });
    return flag;
});

The final major componenet I implemented was an advanced dark-mode which follows Material Design guidelines and can remember user/device preferences between sessions. The Material UI library supports the concepts of themes which dictate the style of all components on the site, this theme can easily be switched to the reccomended dark-mode design by implementing a toggle and state system. The system will first check if the user has previously selected a preference by checking local storage before falling back to system preference if no previous state could be found.

image

I also implemented a few other features such as a header and footer containing more information about the site along with a simple Google Analytics tag to track visitor numbers.

Hosting the Site

Mortgage Manager is hosted on GitHub Pages for free using the gh-pages npm package.

You might also like...

It shows an effective way to correct bus arrival information using data analytics based on Amazon Serverless such as Kiness Data Stream, Kinesis Data Firehose, S3, and Lambda.

It shows an effective way to correct bus arrival information using data analytics based on Amazon Serverless such as Kiness Data Stream, Kinesis Data Firehose, S3, and Lambda.

Amazon Serverless를 이용한 실시간 버스 정보 수집 및 저장 본 github repository는 버스 정보를 주기적으로 수집하여 분석할 수 있도록, Amazon Serverless인 Amazon Kinesis Data Stream, Kinesis Data

Nov 13, 2022

LunaSec - Open Source Security Software built by Security Engineers. Scan your dependencies for Log4Shell, or add Data Tokenization to prevent data leaks. Try our live Tokenizer demo: https://app.lunasec.dev

LunaSec - Open Source Security Software built by Security Engineers. Scan your dependencies for Log4Shell, or add Data Tokenization to prevent data leaks. Try our live Tokenizer demo: https://app.lunasec.dev

Our Software We're a team of Security Engineers on a mission to make awesome Open Source Application Security tooling. It all lives in this repo. Here

Jan 7, 2023

🧙 Mage is an open-source data management platform that helps you clean data and prepare it for training AI/ML models.

🧙 Mage is an open-source data management platform that helps you clean data and prepare it for training AI/ML models.

Intro Mage is an open-source data management platform that helps you clean data and prepare it for training AI/ML models. What does this do? The curre

Jan 4, 2023

JSON Visio is data visualization tool for your json data which seamlessly illustrates your data on graphs without having to restructure anything, paste directly or import file.

JSON Visio is data visualization tool for your json data which seamlessly illustrates your data on graphs without having to restructure anything, paste directly or import file.

JSON Visio is data visualization tool for your json data which seamlessly illustrates your data on graphs without having to restructure anything, paste directly or import file.

Jan 4, 2023

A table component for your Mantine data-rich applications, supporting asynchronous data loading, column sorting, custom cell data rendering, row context menus, dark theme, and more.

A table component for your Mantine data-rich applications, supporting asynchronous data loading, column sorting, custom cell data rendering, row context menus, dark theme, and more.

Mantine DataTable A "dark-theme aware" table component for your Mantine UI data-rich applications, featuring asynchronous data loading support, pagina

Jan 4, 2023

Tool to sign data with a Cardano-Secret-Key // verify data with a Cardano-Public-Key // generate CIP-8 & CIP-36 data

Tool to sign data with a Cardano-Secret-Key // verify data with a Cardano-Public-Key // generate CIP-8 & CIP-36 data

Tool to sign data with a Cardano-Secret-Key // verify data with a Cardano-Public-Key // generate CIP-8 & CIP-36 data

Dec 21, 2022

Reference for How to Write an Open Source JavaScript Library - https://egghead.io/series/how-to-write-an-open-source-javascript-library

Reference for How to Write an Open Source JavaScript Library The purpose of this document is to serve as a reference for: How to Write an Open Source

Dec 24, 2022

An Open-Source Platform to certify open-source projects.

An Open-Source Platform to certify open-source projects.

OC-Frontend This includes the frontend for Open-Certs. 📜 After seeing so many open-source projects being monetized 💵 without giving any recognition

Oct 23, 2022
Owner
Luke Prior
Software Developer, Technical Writer, and Electronics Enthusiast.
Luke Prior
To understand the history of SACCOs, a Savings and Credit cooperative otherwise known as a Sacco is a type of corporation that aims at pooling money together.

To understand the history of SACCOs, a Savings and Credit cooperative otherwise known as a Sacco is a type of corporation that aims at pooling money together. Depending on the Saccos, there are different types of supplies of merchandise. These include and are not limited to recognition servers, sedimentation, and call home eggs installation. More to these items include check glades, bankers, checks standing orders and safe detentions, and salary progress.

incredicoder 2 Oct 19, 2022
A health-focused app for users to be able to track workouts and nutritional data with a social media component to inspire friendly competition among the users.

A health-focused app for users to be able to track workouts and nutritional data with a social media component to inspire friendly competition among the users.

Jon Jackson 3 Aug 26, 2022
Example Breakout games using small libraries/engines/templates for the js13kGames competition.

js13kBreakouts We implemented the same breakout style game with small libraries/engines/templates for the js13kGames competition! Live Demos LittleJS

js13kGames 16 Sep 17, 2022
Maximize your social capital in DAOs.

Introduction DAOU uses Soulbound Token and Social Oracle to convert your activity data in DAOs: Discord activity and particapations, Forum Q&A, Github

DAOU 16 Aug 21, 2022
CTF (Capture The Flag) is a type of information security competition that challenges contestants to find solutions or complete various tasks.

WHAT IS CTF? CTF (Capture The Flag) is a type of information security competition that challenges contestants to find solutions or complete various ta

Nicolas Saputra Gunawan 18 Dec 12, 2022
Converts your GitHub commits to LinkedIn posts, to maximize exposure.

linkedpush (site) Converts your GitHub pushes to LinkedIn posts, to maximize exposure. linkedpush-dep-tictok.mp4 How it works Sequence diagram Setup 0

Sebastian Sosa 8 Dec 3, 2022
WaffleHacks 2022 - Winner of 'Dream Big and Create More Cheers with AB InBev' & Honorable Mention for the Food Insecurity Track

Getting Started with Create React App Welcome to Leftover Marketplace. This is our project for WaffleHacks 2022. Links Presentation and Demo Video Web

Geer 3 Dec 27, 2022
This is smart contact game with random winner.

Game of Chance This is smart contract that deploys on blockchain. This is the game. Winner is selected randomly. Requirements: Yarn Node.js HardHat Qu

Leonid Shaydenko 6 Sep 15, 2022
ETH NYC Winner (8 prizes)

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Stanley Zheng 39 Dec 21, 2022
Second Prize Winner of Crypto.com Hackathon 2022. An NFT Ticketing Platform built with Next.js and Ethereum.

Second Prize Winner of Crypto.com The Next Gen Hackathon 2022! How to run this project locally clone the repo and run yarn install in the root directo

Kasidis Chantharojwong 5 Oct 26, 2022