Skip to content

Commit

Permalink
Merge pull request #281 from soramitsu/hotfix/crowdloans
Browse files Browse the repository at this point in the history
Private crowdloans & Bifrost flow support
  • Loading branch information
ERussel authored Jun 9, 2021
2 parents ffce2a0 + 5d7657c commit 4ba6e66
Show file tree
Hide file tree
Showing 35 changed files with 601 additions and 513 deletions.
4 changes: 2 additions & 2 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ platform :ios, '11.0'
abstract_target 'fearlessAll' do
use_frameworks!

pod 'FearlessUtils', :git => 'https://github.com/soramitsu/fearless-utils-iOS.git', :commit => '8a8bacda093463c81889a47b078503787056233e'
pod 'FearlessUtils', :git => 'https://github.com/soramitsu/fearless-utils-iOS.git', :commit => '76bd0da88c4d82f218645d78de51479e08fd9d89'
pod 'SwiftLint'
pod 'R.swift', :inhibit_warnings => true
pod 'SoraKeystore'
Expand All @@ -25,7 +25,7 @@ abstract_target 'fearlessAll' do
inherit! :search_paths

pod 'Cuckoo'
pod 'FearlessUtils', :git => 'https://github.com/soramitsu/fearless-utils-iOS.git', :commit => '8a8bacda093463c81889a47b078503787056233e'
pod 'FearlessUtils', :git => 'https://github.com/soramitsu/fearless-utils-iOS.git', :commit => '76bd0da88c4d82f218645d78de51479e08fd9d89'
pod 'SoraFoundation', '~> 0.10.0'
pod 'R.swift', :inhibit_warnings => true
pod 'FireMock', :inhibit_warnings => true
Expand Down
8 changes: 4 additions & 4 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ PODS:
DEPENDENCIES:
- CommonWallet/Core (from `https://github.com/soramitsu/Capital-iOS.git`, commit `33c7eec1db947eeae8e3b7feb217c60199599bd7`)
- Cuckoo
- FearlessUtils (from `https://github.com/soramitsu/fearless-utils-iOS.git`, commit `8a8bacda093463c81889a47b078503787056233e`)
- FearlessUtils (from `https://github.com/soramitsu/fearless-utils-iOS.git`, commit `76bd0da88c4d82f218645d78de51479e08fd9d89`)
- FireMock
- Kingfisher
- R.swift
Expand Down Expand Up @@ -161,7 +161,7 @@ EXTERNAL SOURCES:
:commit: 33c7eec1db947eeae8e3b7feb217c60199599bd7
:git: https://github.com/soramitsu/Capital-iOS.git
FearlessUtils:
:commit: 8a8bacda093463c81889a47b078503787056233e
:commit: 76bd0da88c4d82f218645d78de51479e08fd9d89
:git: https://github.com/soramitsu/fearless-utils-iOS.git
Starscream:
:branch: feature/without-origin
Expand All @@ -175,7 +175,7 @@ CHECKOUT OPTIONS:
:commit: 33c7eec1db947eeae8e3b7feb217c60199599bd7
:git: https://github.com/soramitsu/Capital-iOS.git
FearlessUtils:
:commit: 8a8bacda093463c81889a47b078503787056233e
:commit: 76bd0da88c4d82f218645d78de51479e08fd9d89
:git: https://github.com/soramitsu/fearless-utils-iOS.git
Starscream:
:commit: b9e69390d96e71427463469f47cdafb8c0db1b21
Expand Down Expand Up @@ -213,6 +213,6 @@ SPEC CHECKSUMS:
TweetNacl: 3abf4d1d2082b0114e7a67410e300892448951e6
xxHash-Swift: 30bd6a7507b3b7348a277c49b1cb6346c2905ec7

PODFILE CHECKSUM: 9bc68397b4057c1c3bfbacc87ba23bc1f636f10f
PODFILE CHECKSUM: b7eafdf52cce50483f430bcf53b281a79d34b95b

COCOAPODS: 1.10.1
102 changes: 63 additions & 39 deletions fearless.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions fearless/Common/Substrate/Calls/CrowdloanAddMemo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Foundation
import FearlessUtils

struct CrowdloanAddMemo: Codable {
@StringCodable var index: ParaId
@BytesCodable var memo: Data
}
6 changes: 6 additions & 0 deletions fearless/Common/Substrate/Calls/SubstrateCallFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ protocol SubstrateCallFactoryProtocol {
func setController(_ controller: AccountAddress) throws -> RuntimeCall<SetControllerCall>

func contribute(to paraId: ParaId, amount: BigUInt) -> RuntimeCall<CrowdloanContributeCall>
func addMemo(to paraId: ParaId, memo: Data) -> RuntimeCall<CrowdloanAddMemo>
}

final class SubstrateCallFactory: SubstrateCallFactoryProtocol {
Expand Down Expand Up @@ -120,6 +121,11 @@ final class SubstrateCallFactory: SubstrateCallFactoryProtocol {
let args = CrowdloanContributeCall(index: paraId, value: amount, signature: nil)
return RuntimeCall(moduleName: "Crowdloan", callName: "contribute", args: args)
}

func addMemo(to paraId: ParaId, memo: Data) -> RuntimeCall<CrowdloanAddMemo> {
let args = CrowdloanAddMemo(index: paraId, memo: memo)
return RuntimeCall(moduleName: "Crowdloan", callName: "add_memo", args: args)
}
}

extension SubstrateCallFactory {
Expand Down
24 changes: 24 additions & 0 deletions fearless/Common/Substrate/Types/BytesCodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Foundation

@propertyWrapper
public struct BytesCodable: Codable, Equatable {
public var wrappedValue: Data

public init(wrappedValue: Data) {
self.wrappedValue = wrappedValue
}

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let byteArray = try container.decode([StringScaleMapper<UInt8>].self)

wrappedValue = Data(byteArray.map(\.value))
}

public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let bytes = wrappedValue.map { StringScaleMapper(value: $0) }

try container.encode(bytes)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,17 @@ class CrowdloanContributionInteractor: CrowdloanContributionInteractorInputProto
provideConstants()
}

func estimateFee(for amount: BigUInt) {
func estimateFee(for amount: BigUInt, bonusService: CrowdloanBonusServiceProtocol?) {
let call = callFactory.contribute(to: paraId, amount: amount)

let identifier = String(amount)

feeProxy.estimateFee(using: extrinsicService, reuseIdentifier: identifier) { builder in
try builder.adding(call: call)
let nextBuilder = try builder.adding(call: call)
return try bonusService?.applyOnchainBonusForContribution(
amount: amount,
using: nextBuilder
) ?? nextBuilder
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import BigInt

protocol CrowdloanContributionInteractorInputProtocol: AnyObject {
func setup()
func estimateFee(for amount: BigUInt)
func estimateFee(for amount: BigUInt, bonusService: CrowdloanBonusServiceProtocol?)
}

protocol CrowdloanContributionInteractorOutputProtocol: AnyObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,16 @@ final class CrowdloanContributionConfirmInteractor: CrowdloanContributionInterac
private func submitExtrinsic(for contribution: BigUInt) {
let call = callFactory.contribute(to: paraId, amount: contribution)

let builderClosure: ExtrinsicBuilderClosure = { builder in
let nextBuilder = try builder.adding(call: call)
return try self.bonusService?.applyOnchainBonusForContribution(
amount: contribution,
using: nextBuilder
) ?? nextBuilder
}

extrinsicService.submit(
{ builder in
try builder.adding(call: call)
},
builderClosure,
signer: signingWrapper,
runningIn: .main,
completion: { [weak self] result in
Expand All @@ -88,7 +94,7 @@ final class CrowdloanContributionConfirmInteractor: CrowdloanContributionInterac
extension CrowdloanContributionConfirmInteractor: CrowdloanContributionConfirmInteractorInputProtocol {
func submit(contribution: BigUInt) {
if let bonusService = bonusService {
bonusService.applyBonusForContribution(amount: contribution) { [weak self] result in
bonusService.applyOffchainBonusForContribution(amount: contribution) { [weak self] result in
DispatchQueue.main.async {
switch result {
case .success:
Expand All @@ -102,4 +108,8 @@ extension CrowdloanContributionConfirmInteractor: CrowdloanContributionConfirmIn
submitExtrinsic(for: contribution)
}
}

func estimateFee(for contribution: BigUInt) {
estimateFee(for: contribution, bonusService: bonusService)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ extension CrowdloanContributionConfirmPresenter: CrowdloanContributionConfirmPre
(fee?.toSubstrateAmount(precision: chain.addressType.precision) ?? 0)

DataValidationRunner(validators: [
dataValidatingFactory.crowdloanIsNotPrivate(crowdloan: crowdloan, locale: selectedLocale),

dataValidatingFactory.has(fee: fee, locale: selectedLocale, onError: { [weak self] in
self?.refreshFee()
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ protocol CrowdloanContributionConfirmPresenterProtocol: AnyObject {
}

protocol CrowdloanContributionConfirmInteractorInputProtocol: CrowdloanContributionInteractorInputProtocol {
func estimateFee(for contribution: BigUInt)
func submit(contribution: BigUInt)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ final class CrowdloanContributionSetupPresenter {
return
}

interactor.estimateFee(for: amount)
interactor.estimateFee(for: amount, bonusService: bonusService)
}
}

Expand Down Expand Up @@ -206,6 +206,8 @@ extension CrowdloanContributionSetupPresenter: CrowdloanContributionSetupPresent
(fee?.toSubstrateAmount(precision: chain.addressType.precision) ?? 0)

DataValidationRunner(validators: [
dataValidatingFactory.crowdloanIsNotPrivate(crowdloan: crowdloan, locale: selectedLocale),

dataValidatingFactory.has(fee: fee, locale: selectedLocale, onError: { [weak self] in
self?.refreshFee()
}),
Expand Down Expand Up @@ -244,10 +246,8 @@ extension CrowdloanContributionSetupPresenter: CrowdloanContributionSetupPresent
)

]).runValidation { [weak self] in
guard
let strongSelf = self,
let contribution = contributionDecimal,
let paraId = strongSelf.crowdloan?.paraId else { return }
guard let strongSelf = self, let contribution = contributionDecimal,
let paraId = strongSelf.crowdloan?.paraId else { return }
strongSelf.wireframe.showConfirmation(
from: strongSelf.view,
paraId: paraId,
Expand Down Expand Up @@ -410,6 +410,7 @@ extension CrowdloanContributionSetupPresenter: CustomCrowdloanDelegate {
func didReceive(bonusService: CrowdloanBonusServiceProtocol) {
self.bonusService = bonusService
provideBonusViewModel()
refreshFee()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ final class CrowdloanContributionSetupWireframe: CrowdloanContributionSetupWiref
delegate: delegate,
existingService: existingService
)
case .bifrost:
showBifrostCustomFlow(
from: view,
for: displayInfo,
inputAmount: inputAmount,
delegate: delegate,
existingService: existingService
)
}
}

Expand All @@ -48,7 +56,7 @@ final class CrowdloanContributionSetupWireframe: CrowdloanContributionSetupWiref
delegate: CustomCrowdloanDelegate,
existingService: CrowdloanBonusServiceProtocol?
) {
guard let karuraView = KaruraCrowdloanViewFactory.createView(
guard let karuraView = ReferralCrowdloanViewFactory.createKaruraView(
for: delegate,
displayInfo: displayInfo,
inputAmount: inputAmount,
Expand All @@ -63,4 +71,27 @@ final class CrowdloanContributionSetupWireframe: CrowdloanContributionSetupWiref

view?.controller.present(navigationController, animated: true, completion: nil)
}

private func showBifrostCustomFlow(
from view: CrowdloanContributionSetupViewProtocol?,
for displayInfo: CrowdloanDisplayInfo,
inputAmount: Decimal,
delegate: CustomCrowdloanDelegate,
existingService: CrowdloanBonusServiceProtocol?
) {
guard let bifrostView = ReferralCrowdloanViewFactory.createBifrostView(
for: delegate,
displayInfo: displayInfo,
inputAmount: inputAmount,
existingService: existingService
) else {
return
}

let navigationController = FearlessNavigationController(
rootViewController: bifrostView.controller
)

view?.controller.present(navigationController, animated: true, completion: nil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ final class CrowdloanListViewController: UIViewController, ViewHolder {
rootView.tableView.registerClassForCell(CompletedCrowdloanTableViewCell.self)
rootView.tableView.registerHeaderFooterView(withClass: CrowdloanStatusSectionView.self)

rootView.tableView.tableFooterView = UIView()

rootView.tableView.dataSource = self
rootView.tableView.delegate = self

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Foundation
import RobinHood
import FearlessUtils
import BigInt

final class BifrostBonusService {
static let defaultReferralCode = "FRLS69"

var bonusRate: Decimal { 0.05 }

var termsURL: URL {
URL(string: "https://docs.google.com/document/d/1PDpgHnIcAmaa7dEFusmLYgjlvAbk2VKtMd755bdEsf4/edit?usp=sharing")!
}

private(set) var referralCode: String?

let paraId: ParaId

init(paraId: ParaId) {
self.paraId = paraId
}
}

extension BifrostBonusService: CrowdloanBonusServiceProtocol {
func save(referralCode: String, completion closure: @escaping (Result<Void, Error>) -> Void) {
guard let codeData = referralCode.data(using: .utf8), codeData.count <= 32 else {
closure(.failure(CrowdloanBonusServiceError.invalidReferral))
return
}

self.referralCode = referralCode
closure(.success(()))
}

func applyOffchainBonusForContribution(
amount _: BigUInt,
with closure: @escaping (Result<Void, Error>) -> Void
) {
closure(.success(()))
}

func applyOnchainBonusForContribution(
amount _: BigUInt,
using builder: ExtrinsicBuilderProtocol
) throws -> ExtrinsicBuilderProtocol {
guard let memo = referralCode?.data(using: .utf8) else {
return builder
}

let addMemo = SubstrateCallFactory().addMemo(to: paraId, memo: memo)

return try builder.adding(call: addMemo)
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import Foundation
import BigInt
import FearlessUtils

protocol CrowdloanBonusServiceProtocol: AnyObject {
var bonusRate: Decimal { get }
var termsURL: URL { get }
var referralCode: String? { get }

func save(referralCode: String, completion closure: @escaping (Result<Void, Error>) -> Void)
func applyBonusForContribution(amount: BigUInt, with closure: @escaping (Result<Void, Error>) -> Void)
func applyOffchainBonusForContribution(
amount: BigUInt,
with closure: @escaping (Result<Void, Error>) -> Void
)

func applyOnchainBonusForContribution(
amount: BigUInt,
using builder: ExtrinsicBuilderProtocol
) throws -> ExtrinsicBuilderProtocol
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Foundation

enum CrowdloanBonusServiceError: Error, ErrorContentConvertible {
case invalidReferral
case internalError
case veficationFailed

func toErrorContent(for locale: Locale?) -> ErrorContent {
switch self {
case .invalidReferral:
return ErrorContent(
title: R.string.localizable.commonErrorGeneralTitle(preferredLanguages: locale?.rLanguages),
message: R.string.localizable.crowdloanReferralCodeInvalid(preferredLanguages: locale?.rLanguages)
)
case .internalError:
return ErrorContent(
title: R.string.localizable.commonErrorGeneralTitle(preferredLanguages: locale?.rLanguages),
message: R.string.localizable.crowdloanReferralCodeInternal(preferredLanguages: locale?.rLanguages)
)
case .veficationFailed:
return ErrorContent(
title: R.string.localizable.commonErrorGeneralTitle(preferredLanguages: locale?.rLanguages),
message: R.string.localizable.crowdloanBonusVerificationError(preferredLanguages: locale?.rLanguages)
)
}
}
}
Loading

0 comments on commit 4ba6e66

Please sign in to comment.