diff --git a/api/api/supply/apy.ts b/api/api/supply/apy.ts new file mode 100644 index 0000000..7523da5 --- /dev/null +++ b/api/api/supply/apy.ts @@ -0,0 +1,36 @@ +const Compound = require("@compound-finance/compound-js"); +import { VercelRequest, VercelResponse } from "@vercel/node"; + +const compound = new Compound("http://127.0.0.1:8545", { + privateKey: + "0xb8c1b5c1d81f9475fdf2e334517d29f733bdfa40682207571b12fc1142cbf329" +}); + +async function calculateApy(asset: string) { + const srpb = await Compound.eth.read( + Compound.util.getAddress("c" + asset), + "function supplyRatePerBlock() returns (uint256)", + [], + { provider: compound.provider } + ); + + const mantissa = Math.pow(10, 18); + const blocksPerDay = (60 * 60 * 24) / 13.15; // ~13.15 second block time + const daysPerYear = 365; + + const supplyApy = + (Math.pow((+srpb.toString() / mantissa) * blocksPerDay + 1, daysPerYear) - + 1) * + 100; + return supplyApy; +} + +export default async (_: VercelRequest, res: VercelResponse) => { + const asset = "USDC"; + const apy = await calculateApy(asset); + console.log(apy); + res.json({ + apy, + asset + }); +}; diff --git a/api/api/supply.ts b/api/api/supply/index.ts similarity index 100% rename from api/api/supply.ts rename to api/api/supply/index.ts diff --git a/app/eazy.xcodeproj/project.pbxproj b/app/eazy.xcodeproj/project.pbxproj index 31f6182..5db6de0 100644 --- a/app/eazy.xcodeproj/project.pbxproj +++ b/app/eazy.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 69823936268DB9C2004138FF /* OnboardingTransactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69823935268DB9C2004138FF /* OnboardingTransactionView.swift */; }; 69823939269043A1004138FF /* ObjectivePGP in Frameworks */ = {isa = PBXBuildFile; productRef = 69823938269043A1004138FF /* ObjectivePGP */; }; 69AEBD4C2689CEE70064CB78 /* LaunchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69AEBD4B2689CEE70064CB78 /* LaunchView.swift */; }; + 69CC1FDC2691A80D00DC6E46 /* ApyApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CC1FDB2691A80D00DC6E46 /* ApyApi.swift */; }; 69D43A24268C649000401501 /* OnboardingBorrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D43A23268C649000401501 /* OnboardingBorrowView.swift */; }; 69D43A26268C65BA00401501 /* OnboardingCollateralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D43A25268C65BA00401501 /* OnboardingCollateralView.swift */; }; 69D43A28268C667100401501 /* AddCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69D43A27268C667100401501 /* AddCardView.swift */; }; @@ -54,6 +55,7 @@ 6976D28D2681D60F00E50002 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69823935268DB9C2004138FF /* OnboardingTransactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTransactionView.swift; sourceTree = ""; }; 69AEBD4B2689CEE70064CB78 /* LaunchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchView.swift; sourceTree = ""; }; + 69CC1FDB2691A80D00DC6E46 /* ApyApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApyApi.swift; sourceTree = ""; }; 69D43A23268C649000401501 /* OnboardingBorrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBorrowView.swift; sourceTree = ""; }; 69D43A25268C65BA00401501 /* OnboardingCollateralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCollateralView.swift; sourceTree = ""; }; 69D43A27268C667100401501 /* AddCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCardView.swift; sourceTree = ""; }; @@ -116,6 +118,7 @@ 69A489A2268881F40096AAB3 /* data */ = { isa = PBXGroup; children = ( + 69CC1FDB2691A80D00DC6E46 /* ApyApi.swift */, 69D43A29268D090500401501 /* BorrowApi.swift */, 6926D3A8268884CB003A32CF /* CardsApi.swift */, 693442732688906400339CC3 /* PaymentsApi.swift */, @@ -220,6 +223,7 @@ buildActionMask = 2147483647; files = ( 693442762688906C00339CC3 /* PayoutsApi.swift in Sources */, + 69CC1FDC2691A80D00DC6E46 /* ApyApi.swift in Sources */, 69D43A24268C649000401501 /* OnboardingBorrowView.swift in Sources */, 6976D2872681D60E00E50002 /* ContentView.swift in Sources */, 69AEBD4C2689CEE70064CB78 /* LaunchView.swift in Sources */, diff --git a/app/eazy/data/ApyApi.swift b/app/eazy/data/ApyApi.swift new file mode 100644 index 0000000..0f3a94d --- /dev/null +++ b/app/eazy/data/ApyApi.swift @@ -0,0 +1,92 @@ +// +// ApyApi.swift +// eazy +// +// Created by Jann Driessen on 04.07.21. +// + +import Foundation + +import SwiftUI + +enum ApyApiError: Error { + case unexpected(message: String) + case missingResponse +} + +final class ApyApi: ObservableObject { + @Published var borrowApy: String = "" + @Published var supplyApy: String = "" + + func fetch() { + fetchBorrowApy() + fetchSupplyApy() + } +} + +extension ApyApi { + private func fetchBorrowApy() { + let path = "/borrow/apy" + let requestBuilder = ApiRequestBuilder() + guard let request = requestBuilder.buildRequest(for: path, method: .get) else { return } + + let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in + if let error = error { + let error = ApyApiError.unexpected(message: error.localizedDescription) + print(error) + return + } + + guard let httpResponse = response as? HTTPURLResponse, + (200...299).contains(httpResponse.statusCode) else { + let error = ApyApiError.unexpected(message: "Error with the response - unexpected status code") + print(error) + return + } + + if let data = data, let result = try? JSONDecoder().decode(ApyResponse.self, from: data) { + DispatchQueue.main.async { + print(result) + self.borrowApy = String(format: "%.2f%%", result.apy) + } + } + }) + task.resume() + } +} + +extension ApyApi { + private func fetchSupplyApy() { + let path = "/supply/apy" + let requestBuilder = ApiRequestBuilder() + guard let request = requestBuilder.buildRequest(for: path, method: .get) else { return } + + let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in + if let error = error { + let error = ApyApiError.unexpected(message: error.localizedDescription) + print(error) + return + } + + guard let httpResponse = response as? HTTPURLResponse, + (200...299).contains(httpResponse.statusCode) else { + let error = ApyApiError.unexpected(message: "Error with the response - unexpected status code") + print(error) + return + } + + if let data = data, let result = try? JSONDecoder().decode(ApyResponse.self, from: data) { + DispatchQueue.main.async { + print(result) + self.supplyApy = String(format: "%.2f%%", result.apy) + } + } + }) + task.resume() + } +} + +private struct ApyResponse: Decodable { + let apy: Double + let asset: String +} diff --git a/app/eazy/ui/DashboardView.swift b/app/eazy/ui/DashboardView.swift index acd230c..e0abca7 100644 --- a/app/eazy/ui/DashboardView.swift +++ b/app/eazy/ui/DashboardView.swift @@ -33,6 +33,7 @@ extension Transaction { } struct DashboardView: View { + @ObservedObject private var apyApi = ApyApi() @State private var notificationMessage = "Your borrowed money is ready for payout.\nTap here to send it." @State private var showPayoutMessage = false @State private var showPayoutModal = false @@ -57,8 +58,8 @@ struct DashboardView: View { } } .padding() - BorrowView(apy: "3.10%") - EarnView(apy: "2.03%") + BorrowView(apy: apyApi.borrowApy) + EarnView(apy: apyApi.supplyApy) TransactionsHeaderView() .padding() ForEach(transactions, id: \.id) { transaction in @@ -80,6 +81,7 @@ struct DashboardView: View { }) } .onAppear() { + apyApi.fetch() DispatchQueue.main.asyncAfter(deadline: .now() + 1) { togglePayoutMessage() DispatchQueue.main.asyncAfter(deadline: .now() + 2) {