Skip to content

Commit

Permalink
merge(#7): offramp
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszjablonski authored Nov 15, 2022
1 parent 7c6ff63 commit 4e13004
Show file tree
Hide file tree
Showing 20 changed files with 487 additions and 114 deletions.
10 changes: 5 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import PackageDescription

let package = Package(
name: "Ramp",
platforms: [
.iOS(.v11)
],
defaultLocalization: "en",
platforms: [.iOS(.v11)],
products: [
.library(name: "Ramp", targets: ["Ramp"])
],
dependencies: [
.package(name: "Passbase",
url: "https://github.com/passbase/passbase-sp.git",
.upToNextMajor(from: "2.8.0")),
from: "2.8.0"),
],
targets: [
.target(name: "Ramp", dependencies: ["Passbase"])
.target(name: "Ramp", dependencies: ["Passbase"]),
.testTarget(name: "RampTests", dependencies: ["Ramp"])
]
)
5 changes: 3 additions & 2 deletions Ramp.podspec
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
Pod::Spec.new do |spec|
spec.name = 'Ramp'
spec.version = '2.0.0'
spec.version = '3.0.0'
spec.license = 'proprietary'
spec.summary = 'Ramp SDK for iOS'
spec.homepage = 'https://ramp.network/'
spec.authors = { 'Ramp Network' => '[email protected]' }
spec.source = { :git => 'https://github.com/RampNetwork/ramp-sdk-ios', :tag => spec.version }
spec.source = { :git => 'https://github.com/RampNetwork/ramp-sdk-ios.git', :tag => spec.version }
spec.ios.deployment_target = '11.0'
spec.source_files = 'Sources/Ramp/*.swift'
spec.resource_bundles = { 'Ramp' => 'Sources/Ramp/Resources/*' }
spec.dependency 'Passbase', '~> 2.8'
end
2 changes: 1 addition & 1 deletion Sources/Ramp/Coding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let encoder = JSONEncoder()
let decoder = JSONDecoder()

extension JSONDecoder {
func decode<T: Decodable>(_ payload: Any) throws -> T {
func decode<T: Decodable>(_ payload: [String: Any]) throws -> T {
let data = try JSONSerialization.data(withJSONObject: payload)
let decoded = try decode(T.self, from: data)
return decoded
Expand Down
94 changes: 63 additions & 31 deletions Sources/Ramp/Configuration.swift
Original file line number Diff line number Diff line change
@@ -1,52 +1,84 @@
import Foundation

/// Parameters description and usage can be found at [Ramp Configuratoin Documentation](https://docs.ramp.network/configuration)
public struct Configuration {
public var swapAsset: String? = nil
public var swapAmount: String? = nil
public var fiatCurrency: String? = nil
public var fiatValue: String? = nil
public var userAddress: String? = nil
public var hostLogoUrl: String? = nil
public var hostAppName: String? = nil
public var userEmailAddress: String? = nil
public var selectedCountryCode: String? = nil
public var defaultAsset: String? = nil
public var url: String? = nil
public var webhookStatusUrl: String? = nil
public var finalUrl: String? = nil
public var containerNode: String? = nil
public var hostApiKey: String? = nil
public var deepLinkScheme: String? = nil
public struct Configuration: Decodable {

/// main URL
public var url: String = Constants.defaultUrl

/// query params
public var containerNode: String?
public var deepLinkScheme: String?
public var defaultAsset: String?
public var defaultFlow: Flow?
public var enabledFlows: Set<Flow>?
public var fiatCurrency: String?
public var fiatValue: String?
public var finalUrl: String?
public var hostApiKey: String?
public var hostAppName: String?
public var hostLogoUrl: String?
public var offrampAsset: String?
public var offrampWebhookV3Url: String?
public var selectedCountryCode: String?
public var swapAmount: String?
public var swapAsset: String?
public var userAddress: String?
public var userEmailAddress: String?
public var useSendCryptoCallback: Bool?
public var variant: String { Constants.sdkVariant }
public var webhookStatusUrl: String?

public init() {}
}

extension Configuration {
public enum Error: Swift.Error { case invalidUrl, invalidParameters }

func buildUrl() throws -> URL {
let url = url ?? Constants.defaultUrl
guard var urlComponents = URLComponents(string: url) else { throw Error.invalidUrl }
urlComponents.path = "/"

urlComponents.appendQueryItem(name: "containerNode", value: containerNode)
urlComponents.appendQueryItem(name: "deepLinkScheme", value: deepLinkScheme)
urlComponents.appendQueryItem(name: "defaultAsset", value: defaultAsset)
urlComponents.appendQueryItem(name: "defaultFlow", value: defaultFlow)

if let enabledFlows {
let value = enabledFlows.map(\.rawValue).joined(separator: ",")
urlComponents.appendQueryItem(name: "enabledFlows", value: value)
}

urlComponents.appendQueryItem(name: "swapAsset", value: swapAsset)
urlComponents.appendQueryItem(name: "swapAmount", value: swapAmount)
urlComponents.appendQueryItem(name: "fiatCurrency", value: fiatCurrency)
urlComponents.appendQueryItem(name: "fiatValue", value: fiatValue)
urlComponents.appendQueryItem(name: "userAddress", value: userAddress)
urlComponents.appendQueryItem(name: "hostLogoUrl", value: hostLogoUrl)
urlComponents.appendQueryItem(name: "finalUrl", value: finalUrl)
urlComponents.appendQueryItem(name: "hostApiKey", value: hostApiKey)
urlComponents.appendQueryItem(name: "hostAppName", value: hostAppName)
urlComponents.appendQueryItem(name: "userEmailAddress", value: userEmailAddress)
urlComponents.appendQueryItem(name: "hostLogoUrl", value: hostLogoUrl)
urlComponents.appendQueryItem(name: "offrampAsset", value: offrampAsset)
urlComponents.appendQueryItem(name: "offrampWebhookV3Url", value: offrampWebhookV3Url)
urlComponents.appendQueryItem(name: "selectedCountryCode", value: selectedCountryCode)
urlComponents.appendQueryItem(name: "defaultAsset", value: defaultAsset)
urlComponents.appendQueryItem(name: "swapAmount", value: swapAmount)
urlComponents.appendQueryItem(name: "swapAsset", value: swapAsset)
urlComponents.appendQueryItem(name: "userAddress", value: userAddress)
urlComponents.appendQueryItem(name: "userEmailAddress", value: userEmailAddress)

if useSendCryptoCallback == true {
urlComponents.appendQueryItem(name: "useSendCryptoCallbackVersion",
value: Constants.sendCryptoVersion)
}

urlComponents.appendQueryItem(name: "variant", value: variant)
urlComponents.appendQueryItem(name: "webhookStatusUrl", value: webhookStatusUrl)
urlComponents.appendQueryItem(name: "finalUrl", value: finalUrl)
urlComponents.appendQueryItem(name: "variant", value: Constants.sdkVariant)
urlComponents.appendQueryItem(name: "containerNode", value: containerNode)
urlComponents.appendQueryItem(name: "hostApiKey", value: hostApiKey)
urlComponents.appendQueryItem(name: "deepLinkScheme", value: deepLinkScheme)

if let url = urlComponents.url { return url }
else { throw Error.invalidParameters }
}
}

public extension Configuration {
enum Flow: String, CaseIterable, Decodable {
case onramp = "ONRAMP"
case offramp = "OFFRAMP"
}

enum Error: Swift.Error { case invalidUrl, invalidParameters }
}
13 changes: 4 additions & 9 deletions Sources/Ramp/Constants.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import Foundation
import SwiftUI

struct Constants {
static let sdkVariant: String = "sdk-mobile"
static let defaultUrl: String = "https://buy.ramp.network"
static let scriptMessageHandlerName: String = "RampInstantMobile"
static let sendCryptoVersion: Int = 1

static let rampColor: UIColor = UIColor(red: 19/255.0, green: 159/255.0, blue: 106/255.0, alpha: 1)

static let closeAlertTitle = "Do you really want to close Ramp?"
static let closeAlertMessage = "You will loose all progress and will have to start over"
static let closeAlertYesAction = "Yes, close"
static let closeAlertNoAction = "No, continue"

static func postMessageScript(_ message: String) -> String { "window.postMessage(\(message));" }
static func postMessageScript(_ message: String) -> String {
return "window.postMessage(" + message + ");"
}
}
94 changes: 74 additions & 20 deletions Sources/Ramp/IncomingEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,84 @@ import Foundation

enum IncomingEvent {
case kycInit(KycInitPayload)
case purchaseCreated(PurchaseCreatedPayload)
case purchaseFailed
case onrampPurchaseCreated(OnrampPurchaseCreatedPayload)
case widgetClose(WidgetClosePayload)
case sendCrypto(SendCryptoPayload)
case offrampSaleCreated(OfframpSaleCreatedPayload)
}

extension IncomingEvent: DictionaryDecodable {
enum Error: Swift.Error { case missingType, missingPayload, unhandledType }

init(dictionary: [String: Any]) throws {
guard let eventType = dictionary["type"] as? String else { throw Error.missingType }
let payload = dictionary["payload"]
switch eventType {
let type = dictionary[CodingKeys.type] as? String
let payload = dictionary[CodingKeys.payload] as? [String: Any]
let version = dictionary[CodingKeys.version] as? Int

case "KYC_INIT":
guard let payload = payload else { throw Error.missingPayload }
guard let type else { throw Error.missingType }

switch type {

case EventTypes.kycInit:
guard let payload else { throw Error.missingPayload(type) }
let decoded: KycInitPayload = try decoder.decode(payload)
self = .kycInit(decoded)

case "PURCHASE_CREATED":
guard let payload = payload else { throw Error.missingPayload }
let decoded: PurchaseCreatedPayload = try decoder.decode(payload)
self = .purchaseCreated(decoded)

case "PURCHASE_FAILED": self = .purchaseFailed
case EventTypes.onrampPurchaseCreated:
guard let payload else { throw Error.missingPayload(type) }
let decoded: OnrampPurchaseCreatedPayload = try decoder.decode(payload)
self = .onrampPurchaseCreated(decoded)

case "WIDGET_CLOSE":
guard let payload = payload else { throw Error.missingPayload }
case EventTypes.widgetClose:
guard let payload else { throw Error.missingPayload(type) }
let decoded: WidgetClosePayload = try decoder.decode(payload)
self = .widgetClose(decoded)

default: throw Error.unhandledType
case EventTypes.sendCrypto:
guard let payload else { throw Error.missingPayload(type) }
/// sendCrypto is loosely versioned, which means we accept no version or version 1 only
if let version {
guard version == Constants.sendCryptoVersion else {
throw Error.unhandledVersion(version, type)
}
}
let decoded: SendCryptoPayload = try decoder.decode(payload)
self = .sendCrypto(decoded)

case EventTypes.offrampSaleCreated:
guard let payload else { throw Error.missingPayload(type) }
let decoded: OfframpSaleCreatedPayload = try decoder.decode(payload)
self = .offrampSaleCreated(decoded)

default:
throw Error.unhandledType(type)
}
}
}

// MARK: Payloads
// MARK: - Types

extension IncomingEvent {
struct EventTypes {
static let kycInit = "KYC_INIT"
static let onrampPurchaseCreated = "PURCHASE_CREATED"
static let widgetClose = "WIDGET_CLOSE"
static let sendCrypto = "SEND_CRYPTO"
static let offrampSaleCreated = "OFFRAMP_SALE_CREATED"
}

struct CodingKeys {
static let payload = "payload"
static let type = "type"
static let version = "eventVersion"
}

enum Error: Swift.Error {
case missingType, missingPayload(String), missingVersion(String)
case unhandledType(String), unhandledVersion(Int, String)
}
}

// MARK: - Payloads

struct KycInitPayload: Decodable {
let email: String
Expand All @@ -48,12 +90,24 @@ struct KycInitPayload: Decodable {
let metaData: String?
}

struct PurchaseCreatedPayload: Decodable {
struct OnrampPurchaseCreatedPayload: Decodable {
let apiUrl: URL
let purchase: OnrampPurchase
let purchaseViewToken: String
let purchase: RampPurchase
}

struct WidgetClosePayload: Decodable {
let showAlert: Bool
}

public struct SendCryptoPayload: Decodable {
public let assetInfo: OfframpAssetInfo
public let amount: String
public let address: String
}

struct OfframpSaleCreatedPayload: Decodable {
let apiUrl: URL
let sale: OfframpSale
let saleViewToken: String
}
8 changes: 8 additions & 0 deletions Sources/Ramp/Localizable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation

struct Localizable {
static var closeAlertTitle: String { NSLocalizedString("Do you really want to close Ramp?", comment: "Alert title for closing Ramp") }
static var closeAlertMessage: String { NSLocalizedString("You will loose all progress and will have to start over", comment: "Alert message for closing Ramp") }
static var yes: String { NSLocalizedString("Yes", comment: "Yes") }
static var no: String { NSLocalizedString("No", comment: "No") }
}
10 changes: 10 additions & 0 deletions Sources/Ramp/OfframpAssetInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Foundation

public struct OfframpAssetInfo: Codable {
public let address: String?
public let chain: String
public let decimals: Int
public let name: String
public let symbol: String
public let type: String
}
18 changes: 18 additions & 0 deletions Sources/Ramp/OfframpSale.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

public struct OfframpSale: Codable {
public let createdAt: String
public let crypto: Crypto
public let fiat: Fiat
public let id: UUID

public struct Crypto: Codable {
public let amount: String
public let assetInfo: OfframpAssetInfo
}

public struct Fiat: Codable {
public let amount: Double
public let currencySymbol: String
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public struct RampPurchase: Decodable {
public struct OnrampPurchase: Codable {
public let id: String
public let endTime: String
public let asset: AssetInfo
Expand All @@ -20,7 +20,7 @@ public struct RampPurchase: Decodable {
public let escrowAddress: String?
public let escrowDetailsHash: String?

public struct AssetInfo: Decodable {
public struct AssetInfo: Codable {
public let address: String?
public let decimals: Int
public let name: String
Expand Down
Loading

0 comments on commit 4e13004

Please sign in to comment.