diff --git a/BlockchainSdk/Blockchains/Casper/CasperAddressService.swift b/BlockchainSdk/Blockchains/Casper/CasperAddressService.swift new file mode 100644 index 0000000000..abb3ccb0ae --- /dev/null +++ b/BlockchainSdk/Blockchains/Casper/CasperAddressService.swift @@ -0,0 +1,75 @@ +// +// CasperAddressService.swift +// BlockchainSdk +// +// Created by skibinalexander on 22.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation +import TangemSdk + +public struct CasperAddressService { + // MARK: - Private Properties + + private let curve: EllipticCurve + + // MARK: - Init + + init(curve: EllipticCurve) { + self.curve = curve + } +} + +// MARK: - AddressProvider + +extension CasperAddressService: AddressProvider { + public func makeAddress(for publicKey: Wallet.PublicKey, with addressType: AddressType) throws -> Address { + guard let prefixAddresss = CasperConstants.getAddressPrefix(curve: curve) else { + throw Error.unsupportedAddressPrefix + } + + let addressBytes = Data(hexString: prefixAddresss) + publicKey.blockchainKey + let address = try CasperAddressUtils().checksum(input: addressBytes) + return PlainAddress(value: address, publicKey: publicKey, type: addressType) + } +} + +// MARK: - AddressValidator + +extension CasperAddressService: AddressValidator { + public func validate(_ address: String) -> Bool { + let isCorrectEd25519Address = address.count == CasperConstants.lengthED25519 && address.hasPrefix(CasperConstants.prefixED25519) + let isCorrectSecp256k1Address = address.count == CasperConstants.lengthSECP256K1 && address.hasPrefix(CasperConstants.prefixSECP256K1) + + guard isCorrectEd25519Address || isCorrectSecp256k1Address else { + return false + } + + if address.isSameCase() { + return true + } + + do { + return try address == CasperAddressUtils().checksum(input: Data(hexString: address)) + } catch { + return false + } + } +} + +// MARK: - Constants + +extension CasperAddressService { + enum Error: LocalizedError { + case unsupportedAddressPrefix + } +} + +// MARK: - Helpers + +private extension String { + func isSameCase() -> Bool { + lowercased() == self || uppercased() == self + } +} diff --git a/BlockchainSdk/Blockchains/Casper/CasperAddressUtils.swift b/BlockchainSdk/Blockchains/Casper/CasperAddressUtils.swift new file mode 100644 index 0000000000..3a61942e3a --- /dev/null +++ b/BlockchainSdk/Blockchains/Casper/CasperAddressUtils.swift @@ -0,0 +1,83 @@ +// +// CasperAddressUtils.swift +// BlockchainSdk +// +// Created by skibinalexander on 22.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation + +/* + https://github.com/casper-ecosystem/casper-js-sdk/blob/dev/src/lib/ChecksummedHex.ts + */ + +struct CasperAddressUtils { + // Ed25519: encode([0x01]) + encode() + // or + // Secp256k1: encode([0x02]) + encode() + func checksum(input: Data) throws -> String { + let byteArray = input.bytes + + guard byteArray.count > 2, let first = byteArray.first else { + throw Error.failedSizeInputChecksum + } + + return try encode(input: [first]) + encode(input: Array(input.bytes[1 ..< input.count])) + } + + // MARK: - Private Implementation + + // Separate bytes inside ByteArray to nibbles + // E.g. [0x01, 0x55, 0xFF, ...] -> [0x00, 0x01, 0x50, 0x05, 0xF0, 0x0F, ...] + private func bytesToNibbles(bytes: [UInt8]) -> [UInt8] { + let result: [UInt8] = bytes.reduce(into: []) { partialResult, byte in + partialResult.append((byte & 0xFF) >> 4) + partialResult.append(byte & 0x0F) + } + + return result + } + + private func byteHash(bytes: [UInt8]) throws -> [UInt8] { + guard let hashData = Data(bytes).hashBlake2b(outputLength: 32) else { + throw Error.failedHashBlake2b + } + return hashData.bytes + } + + private func encode(input: [UInt8]) throws -> String { + let inputNibbles = bytesToNibbles(bytes: input) + let hash = try byteHash(bytes: input) + + // Separate bytes inside ByteArray to bits array + // E.g. [0x01, ...] -> [false, false, false, false, false, false, false, true, ...] + // E.g. [0xAA, ...] -> [true, false, true, false, true, false, true, false, ...] + let hashBits = hash.toBitArray().map { $0.boolValue } + + var hashBitsValues = hashBits.makeIterator() + + let result: String = inputNibbles.reduce(into: "") { partialResult, nibbleByte in + let char = String(format: "%X", nibbleByte) + + if char.range(of: Constants.regexEncodeByte, options: .regularExpression) != nil, hashBitsValues.next() ?? false { + partialResult.append(char.uppercased()) + } else { + partialResult.append(char.lowercased()) + } + } + + return result + } +} + +extension CasperAddressUtils { + enum Constants { + static let regexEncodeByte = "^[a-zA-Z()]+" + } + + enum Error: LocalizedError { + case failedSizeInputChecksum + case failedHashBlake2b + } +} diff --git a/BlockchainSdk/Blockchains/Casper/CasperExternalLinkProvider.swift b/BlockchainSdk/Blockchains/Casper/CasperExternalLinkProvider.swift new file mode 100644 index 0000000000..5765c85c0d --- /dev/null +++ b/BlockchainSdk/Blockchains/Casper/CasperExternalLinkProvider.swift @@ -0,0 +1,33 @@ +// +// CasperExternalLinkProvider.swift +// BlockchainSdk +// +// Created by skibinalexander on 22.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation + +struct CasperExternalLinkProvider: ExternalLinkProvider { + var testnetFaucetURL: URL? { + URL(string: "https://testnet.cspr.live/tools/faucet") + } + + private let isTestnet: Bool + + private var baseExplorerUrl: String { + return isTestnet ? "https://testnet.cspr.live" : "https://cspr.live" + } + + init(isTestnet: Bool) { + self.isTestnet = isTestnet + } + + func url(address: String, contractAddress: String?) -> URL? { + return URL(string: "\(baseExplorerUrl)/account/\(address)") + } + + func url(transaction hash: String) -> URL? { + return URL(string: "\(baseExplorerUrl)/deploy/\(hash)") + } +} diff --git a/BlockchainSdk/Blockchains/Casper/CasperWalletAssembly.swift b/BlockchainSdk/Blockchains/Casper/CasperWalletAssembly.swift new file mode 100644 index 0000000000..906cafac57 --- /dev/null +++ b/BlockchainSdk/Blockchains/Casper/CasperWalletAssembly.swift @@ -0,0 +1,15 @@ +// +// CasperWalletAssembly.swift +// BlockchainSdk +// +// Created by skibinalexander on 22.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation + +struct CasperWalletAssembly: WalletManagerAssembly { + func make(with input: WalletManagerAssemblyInput) throws -> WalletManager { + CasperWalletManager(wallet: input.wallet) + } +} diff --git a/BlockchainSdk/Blockchains/Casper/CasperWalletManager.swift b/BlockchainSdk/Blockchains/Casper/CasperWalletManager.swift new file mode 100644 index 0000000000..39190378fe --- /dev/null +++ b/BlockchainSdk/Blockchains/Casper/CasperWalletManager.swift @@ -0,0 +1,35 @@ +// +// CasperWalletManager.swift +// BlockchainSdk +// +// Created by skibinalexander on 22.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation +import Combine + +class CasperWalletManager: BaseManager, WalletManager { + var currentHost: String { + // TODO: - https://tangem.atlassian.net/browse/IOS-8316 + "" + } + + var allowsFeeSelection: Bool { + false + } + + override func update(completion: @escaping (Result) -> Void) { + // TODO: - https://tangem.atlassian.net/browse/IOS-8316 + } + + func getFee(amount: Amount, destination: String) -> AnyPublisher<[Fee], any Error> { + // TODO: - https://tangem.atlassian.net/browse/IOS-8316 + return .anyFail(error: WalletError.empty) + } + + func send(_ transaction: Transaction, signer: any TransactionSigner) -> AnyPublisher { + // TODO: - https://tangem.atlassian.net/browse/IOS-8316 + return .anyFail(error: SendTxError(error: WalletError.empty)) + } +} diff --git a/BlockchainSdk/Blockchains/Casper/Common/CasperConstants.swift b/BlockchainSdk/Blockchains/Casper/Common/CasperConstants.swift new file mode 100644 index 0000000000..9fb3bc21de --- /dev/null +++ b/BlockchainSdk/Blockchains/Casper/Common/CasperConstants.swift @@ -0,0 +1,32 @@ +// +// CasperConstants.swift +// BlockchainSdk +// +// Created by Alexander Skibin on 29.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation +import TangemSdk + +enum CasperConstants { + // ED25519 + static let prefixED25519 = "01" + static let lengthED25519 = 66 + + // SECP256K1 + static let prefixSECP256K1 = "02" + static let lengthSECP256K1 = 68 + + static func getAddressPrefix(curve: EllipticCurve) -> String? { + switch curve { + case .ed25519, .ed25519_slip0010: + return CasperConstants.prefixED25519 + case .secp256k1: + return CasperConstants.prefixSECP256K1 + default: + // Any curves not supported or will be added in the future + return nil + } + } +} diff --git a/BlockchainSdk/Common/API/TestnetAPINodeInfoProvider.swift b/BlockchainSdk/Common/API/TestnetAPINodeInfoProvider.swift index ab8632dfe0..bc0137b4bc 100644 --- a/BlockchainSdk/Common/API/TestnetAPINodeInfoProvider.swift +++ b/BlockchainSdk/Common/API/TestnetAPINodeInfoProvider.swift @@ -236,6 +236,10 @@ struct TestnetAPINodeInfoProvider { return [ .init(url: URL(string: "https://rpc.test.btcs.network")!), ] + case .casper: + return [ + .init(url: URL(string: "https://testnet.phantom-rpc.com/rpc")!), + ] // TODO: Refactor in IOS-6639 case .bitcoin, .litecoin, .disChain, .rsk, .bitcoinCash, .binance, .cardano, .xrp, .ducatus, .tezos, .dogecoin, .solana, .kusama, .dash, .gnosis, diff --git a/BlockchainSdk/Common/Address/AddressTypesConfig.swift b/BlockchainSdk/Common/Address/AddressTypesConfig.swift index 39e8a1a414..7aed4819ca 100644 --- a/BlockchainSdk/Common/Address/AddressTypesConfig.swift +++ b/BlockchainSdk/Common/Address/AddressTypesConfig.swift @@ -89,7 +89,8 @@ struct AddressTypesConfig { .energyWebEVM, .energyWebX, .core, - .canxium: + .canxium, + .casper: return [.default] } } diff --git a/BlockchainSdk/Common/Blockchain.swift b/BlockchainSdk/Common/Blockchain.swift index 278e7b2bf4..be60fb09c0 100644 --- a/BlockchainSdk/Common/Blockchain.swift +++ b/BlockchainSdk/Common/Blockchain.swift @@ -92,6 +92,7 @@ public indirect enum Blockchain: Equatable, Hashable { case energyWebX(curve: EllipticCurve) case core(testnet: Bool) case canxium + case casper(curve: EllipticCurve, testnet: Bool) public var isTestnet: Bool { switch self { @@ -170,7 +171,9 @@ public indirect enum Blockchain: Equatable, Hashable { .algorand(_, let testnet), .aptos(_, let testnet), .shibarium(let testnet), - .sui(_, let testnet): + .sui(_, let testnet), + .casper(_, let testnet) + : return testnet } } @@ -194,7 +197,8 @@ public indirect enum Blockchain: Equatable, Hashable { .hedera(let curve, _), .bittensor(let curve), .sui(let curve, _), - .energyWebX(let curve): + .energyWebX(let curve), + .casper(let curve, _): return curve case .chia: return .bls12381_G2_AUG @@ -307,7 +311,7 @@ public indirect enum Blockchain: Equatable, Hashable { return 6 case .stellar: return 7 - case .solana, .ton, .bittensor, .sui: + case .solana, .ton, .bittensor, .sui, .casper: return 9 case .polkadot(_, let testnet): return testnet ? 12 : 10 @@ -465,6 +469,8 @@ public indirect enum Blockchain: Equatable, Hashable { return isTestnet ? "tCORE" : "CORE" case .canxium: return "CAU" + case .casper: + return "CSPR" } } @@ -963,6 +969,7 @@ extension Blockchain: Codable { case .energyWebX: return "energyWebX" case .core: return "core" case .canxium: return "canxium" + case .casper: return "casper-network" } } @@ -1060,6 +1067,7 @@ extension Blockchain: Codable { case "energyWebX": self = .energyWebX(curve: curve) case "core": self = .core(testnet: isTestnet) case "canxium": self = .canxium + case "casper-network": self = .casper(curve: curve, testnet: isTestnet) default: throw BlockchainSdkError.decodingFailed } @@ -1302,6 +1310,8 @@ private extension Blockchain { } case .canxium: return "canxium" + case .casper: + return "casper-network" } } @@ -1417,6 +1427,8 @@ extension Blockchain { return SuiWalletAssembly() case .filecoin: return FilecoinWalletAssembly() + case .casper: + return CasperWalletAssembly() } } } diff --git a/BlockchainSdk/Common/Blockchain/Blockchain+AllCases.swift b/BlockchainSdk/Common/Blockchain/Blockchain+AllCases.swift index 580f63a65b..7b8894897d 100644 --- a/BlockchainSdk/Common/Blockchain/Blockchain+AllCases.swift +++ b/BlockchainSdk/Common/Blockchain/Blockchain+AllCases.swift @@ -87,6 +87,7 @@ public extension Blockchain { case .energyWebX: break case .core: break case .canxium: break + case .casper: break // READ BELOW: // // Did you get a compilation error here? If so, add your new blockchain to the array below @@ -167,6 +168,7 @@ public extension Blockchain { .energyWebX(curve: .ed25519_slip0010), .core(testnet: false), .canxium, + .casper(curve: .secp256k1, testnet: false), ] } } diff --git a/BlockchainSdk/Common/Derivations/DerivationConfigV1.swift b/BlockchainSdk/Common/Derivations/DerivationConfigV1.swift index 68c5aa98dd..a6f9947299 100644 --- a/BlockchainSdk/Common/Derivations/DerivationConfigV1.swift +++ b/BlockchainSdk/Common/Derivations/DerivationConfigV1.swift @@ -144,6 +144,8 @@ struct DerivationConfigV1: DerivationConfig { return "m/44'/246'/0'/0'/0'" case .core: return "m/44'/1116'/0'/0/0" + case .casper: + return "m/44'/506'/0'/0/0" } } } diff --git a/BlockchainSdk/Common/Derivations/DerivationConfigV2.swift b/BlockchainSdk/Common/Derivations/DerivationConfigV2.swift index f604b66c2c..8747ec9a02 100644 --- a/BlockchainSdk/Common/Derivations/DerivationConfigV2.swift +++ b/BlockchainSdk/Common/Derivations/DerivationConfigV2.swift @@ -129,6 +129,8 @@ struct DerivationConfigV2: DerivationConfig { return "m/44'/461'/0'/0/0" case .energyWebX: return "m/44'/246'/0'/0'/0'" + case .casper: + return "m/44'/506'/0'/0/0" } } } diff --git a/BlockchainSdk/Common/Derivations/DerivationConfigV3.swift b/BlockchainSdk/Common/Derivations/DerivationConfigV3.swift index 513bc1870f..1b603f8888 100644 --- a/BlockchainSdk/Common/Derivations/DerivationConfigV3.swift +++ b/BlockchainSdk/Common/Derivations/DerivationConfigV3.swift @@ -134,6 +134,8 @@ struct DerivationConfigV3: DerivationConfig { return "m/44'/461'/0'/0/0" case .energyWebX: return "m/44'/246'/0'/0'/0'" + case .casper: + return "m/44'/506'/0'/0/0" } } } diff --git a/BlockchainSdk/Common/Factories/AddressServiceFactory.swift b/BlockchainSdk/Common/Factories/AddressServiceFactory.swift index d348d783db..0c742d9395 100644 --- a/BlockchainSdk/Common/Factories/AddressServiceFactory.swift +++ b/BlockchainSdk/Common/Factories/AddressServiceFactory.swift @@ -140,6 +140,9 @@ public struct AddressServiceFactory { return WalletCoreAddressService(blockchain: .filecoin) case .energyWebX(let curve): return PolkadotAddressService(network: .energyWebX(curve: curve)) + case .casper(let curve, _): + // We only support this type of curve + return CasperAddressService(curve: curve) } } } diff --git a/BlockchainSdk/Common/Factories/EstimationFeeAddressFactory.swift b/BlockchainSdk/Common/Factories/EstimationFeeAddressFactory.swift index 7ef75dc54b..19e5c03c9b 100644 --- a/BlockchainSdk/Common/Factories/EstimationFeeAddressFactory.swift +++ b/BlockchainSdk/Common/Factories/EstimationFeeAddressFactory.swift @@ -105,6 +105,9 @@ struct EstimationFeeAddressFactory { return "sei1lhjvds604fvac32j4eygpr820lyc82dlfv0ea4" case .energyWebX: return "5CogUCbb5PYYbEHhDVGDN6JRRYBkd4sFRVc4wwP8oy5Su34Z" + case .casper: + // TODO: - It is necessary to clarify + return "" } } } diff --git a/BlockchainSdk/Common/Factories/ExternalLinkProviderFactory.swift b/BlockchainSdk/Common/Factories/ExternalLinkProviderFactory.swift index 27f5f441e4..7b4bc8ef53 100644 --- a/BlockchainSdk/Common/Factories/ExternalLinkProviderFactory.swift +++ b/BlockchainSdk/Common/Factories/ExternalLinkProviderFactory.swift @@ -163,6 +163,8 @@ public struct ExternalLinkProviderFactory { return CoreExternalLinkProvider(isTestnet: isTestnet) case .canxium: return CanxiumExternalLinkProvider() + case .casper: + return CasperExternalLinkProvider(isTestnet: isTestnet) } } } diff --git a/BlockchainSdk/Extensions/Byte+.swift b/BlockchainSdk/Extensions/Byte+.swift new file mode 100644 index 0000000000..cbf47a0ea3 --- /dev/null +++ b/BlockchainSdk/Extensions/Byte+.swift @@ -0,0 +1,39 @@ +// +// Byte+.swift +// BlockchainSdkTests +// +// Created by skibinalexander on 23.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation +import CryptoSwift + +extension Bit { + var boolValue: Bool { + self == .one + } +} + +extension UInt8 { + func toBits() -> [Bit] { + var byte = self + var bits = [Bit](repeating: .zero, count: 8) + for i in 0 ..< 8 { + let currentBit = byte & 0x01 + if currentBit != 0 { + bits[i] = .one + } + + byte >>= 1 + } + + return bits + } +} + +extension Array where Element == UInt8 { + func toBitArray() -> [Bit] { + flatMap { $0.toBits() } + } +} diff --git a/BlockchainSdk/Extensions/TWCoin+.swift b/BlockchainSdk/Extensions/TWCoin+.swift index ea037c77e5..89bea3e7e8 100644 --- a/BlockchainSdk/Extensions/TWCoin+.swift +++ b/BlockchainSdk/Extensions/TWCoin+.swift @@ -123,7 +123,8 @@ extension CoinType { .energyWebEVM, .energyWebX, .core, - .canxium: + .canxium, + .casper: // Blockchains that are not in WalletCore yet return nil } diff --git a/BlockchainSdkTests/Casper/CasperTests.swift b/BlockchainSdkTests/Casper/CasperTests.swift new file mode 100644 index 0000000000..34174ff6d4 --- /dev/null +++ b/BlockchainSdkTests/Casper/CasperTests.swift @@ -0,0 +1,18 @@ +// +// CasperTests.swift +// BlockchainSdkTests +// +// Created by skibinalexander on 23.10.2024. +// Copyright © 2024 Tangem AG. All rights reserved. +// + +import Foundation +import TangemSdk +import XCTest +@testable import BlockchainSdk + +final class CasperTests: XCTestCase { + private let blockchain = Blockchain.casper(curve: .secp256k1, testnet: false) + + // TODO: - https://tangem.atlassian.net/browse/IOS-8318 +} diff --git a/BlockchainSdkTests/Common/AddressTests.swift b/BlockchainSdkTests/Common/AddressTests.swift index 973f2a9970..b9b600ec5d 100644 --- a/BlockchainSdkTests/Common/AddressTests.swift +++ b/BlockchainSdkTests/Common/AddressTests.swift @@ -1310,4 +1310,32 @@ class AddressesTests: XCTestCase { XCTAssertTrue(addressService.validate("f7b1299849420e082bbdd9de92cb36e0645e7870513a6eb833d5449a88799699")) } + + func testCasperAddressGeneration() throws { + let ed25519WalletPublicKey = Data(hexString: "98C07D7E72D89A681D7227A7AF8A6FD5F22FE0105C8741D55A95DF415454B82E") + let ed25519ExpectedAddress = "0198c07D7e72D89A681d7227a7Af8A6fd5F22fe0105c8741d55A95dF415454b82E" + + let ed25519AddressService = CasperAddressService(curve: .ed25519) + + try XCTAssertEqual(ed25519AddressService.makeAddress(from: ed25519WalletPublicKey).value, ed25519ExpectedAddress) + + let secp256k1WalletPublicKey = Data(hexString: "021F997DFBBFD32817C0E110EAEE26BCBD2BB70B4640C515D9721C9664312EACD8") + let secp256k1ExpectedAddress = "02021f997DfbbFd32817C0E110EAeE26BCbD2BB70b4640C515D9721c9664312eaCd8" + + let secp256k1AddressService = CasperAddressService(curve: .secp256k1) + + try XCTAssertEqual(secp256k1AddressService.makeAddress(from: secp256k1WalletPublicKey).value, secp256k1ExpectedAddress) + } + + func testCasperAddressValidation() { + let ed25519Address = "0198c07D7e72D89A681d7227a7Af8A6fd5F22fe0105c8741d55A95dF415454b82E" + let ed25519AddressService = CasperAddressService(curve: .ed25519) + + XCTAssertTrue(ed25519AddressService.validate(ed25519Address)) + + let secp256k1Address = "02021f997DfbbFd32817C0E110EAeE26BCbD2BB70b4640C515D9721c9664312eaCd8" + let secp256k1AddressService = CasperAddressService(curve: .secp256k1) + + XCTAssertTrue(secp256k1AddressService.validate(secp256k1Address)) + } } diff --git a/BlockchainSdkTests/Extensions/TWPublicKeyType+.swift b/BlockchainSdkTests/Extensions/TWPublicKeyType+.swift index 3c896c8d4e..6f3d13cecc 100644 --- a/BlockchainSdkTests/Extensions/TWPublicKeyType+.swift +++ b/BlockchainSdkTests/Extensions/TWPublicKeyType+.swift @@ -28,7 +28,8 @@ extension PublicKeyType { .radiant, .koinos, .filecoin, - .sei: + .sei, + .casper: self = PublicKeyType.secp256k1 case .ethereum, .bsc, diff --git a/Tangem/App/Services/ExchangeService/MercuryoService.swift b/Tangem/App/Services/ExchangeService/MercuryoService.swift index 2c9c731895..2e397eea1d 100644 --- a/Tangem/App/Services/ExchangeService/MercuryoService.swift +++ b/Tangem/App/Services/ExchangeService/MercuryoService.swift @@ -284,7 +284,8 @@ private extension Blockchain { .energyWebEVM, .energyWebX, .core, - .canxium: + .canxium, + .casper: // Did you get a compilation error here? If so, check whether the network is supported at return nil } diff --git a/Tangem/App/Services/ExchangeService/MoonPayService.swift b/Tangem/App/Services/ExchangeService/MoonPayService.swift index 767d2e03e5..7a34bbc01c 100644 --- a/Tangem/App/Services/ExchangeService/MoonPayService.swift +++ b/Tangem/App/Services/ExchangeService/MoonPayService.swift @@ -391,6 +391,7 @@ private extension Blockchain { case .energyWebX: return nil case .core: return nil case .canxium: return nil + case .casper: return nil // Did you get a compilation error here? If so, check whether the network is supported at https://api.moonpay.com/v3/currencies } } @@ -472,6 +473,7 @@ private extension Blockchain { case .energyWebX: return nil case .core: return nil case .canxium: return nil + case .casper: return nil // Did you get a compilation error here? If so, check whether the network is supported at https://api.moonpay.com/v3/currencies } } diff --git a/Tangem/App/Utilities/CustomTokenContractAddressConverter.swift b/Tangem/App/Utilities/CustomTokenContractAddressConverter.swift index 81edc73d8d..02db1f13e3 100644 --- a/Tangem/App/Utilities/CustomTokenContractAddressConverter.swift +++ b/Tangem/App/Utilities/CustomTokenContractAddressConverter.swift @@ -106,7 +106,8 @@ struct CustomTokenContractAddressConverter { .energyWebEVM, .energyWebX, .core, - .canxium: + .canxium, + .casper: // Did you get a compilation error here? If so, check if the network supports multiple token contract address // formats (as Hedera does, for example) and add the appropriate conversion logic here if needed return originalAddress diff --git a/Tangem/App/Utilities/TokenInteractionAvailabilityProvider.swift b/Tangem/App/Utilities/TokenInteractionAvailabilityProvider.swift index 3226cee64d..6d36f38ae2 100644 --- a/Tangem/App/Utilities/TokenInteractionAvailabilityProvider.swift +++ b/Tangem/App/Utilities/TokenInteractionAvailabilityProvider.swift @@ -105,7 +105,8 @@ struct TokenInteractionAvailabilityProvider { .energyWebEVM, .energyWebX, .core, - .canxium: + .canxium, + .casper: // Checking that we have at least one valid (non-empty) address // // If necessary, add more specific conditions for newly added blockchains diff --git a/Tangem/Modules/Send/Services/TransactionParamsBuilder.swift b/Tangem/Modules/Send/Services/TransactionParamsBuilder.swift index d50436dae4..8b103962e2 100644 --- a/Tangem/Modules/Send/Services/TransactionParamsBuilder.swift +++ b/Tangem/Modules/Send/Services/TransactionParamsBuilder.swift @@ -110,7 +110,8 @@ struct TransactionParamsBuilder { .energyWebEVM, .energyWebX, .core, - .canxium: + .canxium, + .casper: throw TransactionParamsBuilderError.extraIdNotSupported } } diff --git a/TangemApp.xcodeproj/project.pbxproj b/TangemApp.xcodeproj/project.pbxproj index 0e2f62b0f0..e7a8353b52 100644 --- a/TangemApp.xcodeproj/project.pbxproj +++ b/TangemApp.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 2D1400CA2C43F79F00B75A42 /* MarketsPortfolioContextActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1400C82C43F4FD00B75A42 /* MarketsPortfolioContextActions.swift */; }; 2D1400CE2C44090400B75A42 /* MarketsPortfolioContainerRoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1400CD2C44090400B75A42 /* MarketsPortfolioContainerRoutable.swift */; }; 2D1400D22C4425B300B75A42 /* MarketsTokenDetailsPortfolioCoordinatorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1400D12C4425B300B75A42 /* MarketsTokenDetailsPortfolioCoordinatorFactory.swift */; }; + 2D1B27122CDA025A0066F484 /* CasperConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1B27112CDA025A0066F484 /* CasperConstants.swift */; }; 2D1BB3682C07421C008392F7 /* MarketsTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1BB3672C07421C008392F7 /* MarketsTokenModel.swift */; }; 2D1BB36A2C074534008392F7 /* MarketsDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1BB3692C074534008392F7 /* MarketsDTO.swift */; }; 2D1BB36C2C0745ED008392F7 /* MarketsListOrderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1BB36B2C0745ED008392F7 /* MarketsListOrderType.swift */; }; @@ -65,7 +66,14 @@ 2D61671E2C7632F9006FCEA6 /* MarketsTokensNetworkRoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61671D2C7632F9006FCEA6 /* MarketsTokensNetworkRoutable.swift */; }; 2D6167202C763500006FCEA6 /* MarketsTokenNetworkSelectorCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D61671F2C763500006FCEA6 /* MarketsTokenNetworkSelectorCoordinator.swift */; }; 2D6167222C763505006FCEA6 /* MarketsTokenNetworkSelectorCoordinatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D6167212C763505006FCEA6 /* MarketsTokenNetworkSelectorCoordinatorView.swift */; }; + 2D616FF42CD1421000581EA6 /* Byte+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D616FF32CD1421000581EA6 /* Byte+.swift */; }; + 2D616FF72CD142BE00581EA6 /* CasperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D616FF52CD142BE00581EA6 /* CasperTests.swift */; }; 2D70C61D2B21E90A00BD1DAB /* MarketsAddCustomItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D70C61B2B21E90A00BD1DAB /* MarketsAddCustomItemView.swift */; }; + 2DA57BBF2CD650E900C890DF /* CasperWalletAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D616FE92CD1414000581EA6 /* CasperWalletAssembly.swift */; }; + 2DA57BC02CD650E900C890DF /* CasperExternalLinkProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D616FE82CD1414000581EA6 /* CasperExternalLinkProvider.swift */; }; + 2DA57BC12CD650E900C890DF /* CasperAddressUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D616FE72CD1414000581EA6 /* CasperAddressUtils.swift */; }; + 2DA57BC22CD650E900C890DF /* CasperWalletManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D616FEA2CD1414000581EA6 /* CasperWalletManager.swift */; }; + 2DA57BC32CD650E900C890DF /* CasperAddressService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D616FE62CD1414000581EA6 /* CasperAddressService.swift */; }; 2DA8AB122A433BDB00C75D85 /* Analytics+BlockchainExceptionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA8AB112A433BDB00C75D85 /* Analytics+BlockchainExceptionHandler.swift */; }; 2DB3614B2C14FBAB00BB1D38 /* SegmentedPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB3614A2C14FBAB00BB1D38 /* SegmentedPickerView.swift */; }; 2DB3614D2C14FBF500BB1D38 /* SegmentedPickerView+EnviromentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB3614C2C14FBF500BB1D38 /* SegmentedPickerView+EnviromentValues.swift */; }; @@ -2498,12 +2506,6 @@ EF8BE6BD2CCA994F001FBA2B /* OnrampRedirectDataRequestItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6BC2CCA9941001FBA2B /* OnrampRedirectDataRequestItem.swift */; }; EF8BE6C02CCA9CC3001FBA2B /* CommonOnrampDataRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6BF2CCA9CC3001FBA2B /* CommonOnrampDataRepository.swift */; }; EF8BE6C42CCB95D7001FBA2B /* OnrampRoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6C32CCB95D7001FBA2B /* OnrampRoutable.swift */; }; - EF8BE6B02CCA71E3001FBA2B /* CommonOnrampProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6AF2CCA71E0001FBA2B /* CommonOnrampProviderManager.swift */; }; - EF8BE6B32CCA8800001FBA2B /* OnrampDataRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6B22CCA87FE001FBA2B /* OnrampDataRepository.swift */; }; - EF8BE6BB2CCA98CB001FBA2B /* OnrampPairRequestItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6BA2CCA98CA001FBA2B /* OnrampPairRequestItem.swift */; }; - EF8BE6BD2CCA994F001FBA2B /* OnrampRedirectDataRequestItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6BC2CCA9941001FBA2B /* OnrampRedirectDataRequestItem.swift */; }; - EF8BE6C02CCA9CC3001FBA2B /* CommonOnrampDataRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6BF2CCA9CC3001FBA2B /* CommonOnrampDataRepository.swift */; }; - EF8BE6C42CCB95D7001FBA2B /* OnrampRoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8BE6C32CCB95D7001FBA2B /* OnrampRoutable.swift */; }; EF8D780F2981458500B67418 /* UserTokenListManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8D780E2981458500B67418 /* UserTokenListManagerMock.swift */; }; EF8DBCDD2C060A03009072CF /* StakingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8DBCDC2C060A03009072CF /* StakingManager.swift */; }; EF8DBCE02C060CCE009072CF /* StakingDependenciesFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF8DBCDF2C060CCE009072CF /* StakingDependenciesFactory.swift */; }; @@ -3001,6 +3003,7 @@ 2D1400C82C43F4FD00B75A42 /* MarketsPortfolioContextActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsPortfolioContextActions.swift; sourceTree = ""; }; 2D1400CD2C44090400B75A42 /* MarketsPortfolioContainerRoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsPortfolioContainerRoutable.swift; sourceTree = ""; }; 2D1400D12C4425B300B75A42 /* MarketsTokenDetailsPortfolioCoordinatorFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsTokenDetailsPortfolioCoordinatorFactory.swift; sourceTree = ""; }; + 2D1B27112CDA025A0066F484 /* CasperConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CasperConstants.swift; sourceTree = ""; }; 2D1BB3672C07421C008392F7 /* MarketsTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsTokenModel.swift; sourceTree = ""; }; 2D1BB3692C074534008392F7 /* MarketsDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsDTO.swift; sourceTree = ""; }; 2D1BB36B2C0745ED008392F7 /* MarketsListOrderType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsListOrderType.swift; sourceTree = ""; }; @@ -3035,6 +3038,13 @@ 2D61671D2C7632F9006FCEA6 /* MarketsTokensNetworkRoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsTokensNetworkRoutable.swift; sourceTree = ""; }; 2D61671F2C763500006FCEA6 /* MarketsTokenNetworkSelectorCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsTokenNetworkSelectorCoordinator.swift; sourceTree = ""; }; 2D6167212C763505006FCEA6 /* MarketsTokenNetworkSelectorCoordinatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketsTokenNetworkSelectorCoordinatorView.swift; sourceTree = ""; }; + 2D616FE62CD1414000581EA6 /* CasperAddressService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CasperAddressService.swift; sourceTree = ""; }; + 2D616FE72CD1414000581EA6 /* CasperAddressUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CasperAddressUtils.swift; sourceTree = ""; }; + 2D616FE82CD1414000581EA6 /* CasperExternalLinkProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CasperExternalLinkProvider.swift; sourceTree = ""; }; + 2D616FE92CD1414000581EA6 /* CasperWalletAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CasperWalletAssembly.swift; sourceTree = ""; }; + 2D616FEA2CD1414000581EA6 /* CasperWalletManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CasperWalletManager.swift; sourceTree = ""; }; + 2D616FF32CD1421000581EA6 /* Byte+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Byte+.swift"; sourceTree = ""; }; + 2D616FF52CD142BE00581EA6 /* CasperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CasperTests.swift; sourceTree = ""; }; 2D70C61B2B21E90A00BD1DAB /* MarketsAddCustomItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarketsAddCustomItemView.swift; sourceTree = ""; }; 2D788E7F2ADD657000E0884B /* WalletSelectorViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletSelectorViewModel.swift; sourceTree = ""; }; 2D788E822ADD657000E0884B /* WalletSelectorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletSelectorView.swift; sourceTree = ""; }; @@ -5513,12 +5523,6 @@ EF8BE6BC2CCA9941001FBA2B /* OnrampRedirectDataRequestItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnrampRedirectDataRequestItem.swift; sourceTree = ""; }; EF8BE6BF2CCA9CC3001FBA2B /* CommonOnrampDataRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonOnrampDataRepository.swift; sourceTree = ""; }; EF8BE6C32CCB95D7001FBA2B /* OnrampRoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnrampRoutable.swift; sourceTree = ""; }; - EF8BE6AF2CCA71E0001FBA2B /* CommonOnrampProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonOnrampProviderManager.swift; sourceTree = ""; }; - EF8BE6B22CCA87FE001FBA2B /* OnrampDataRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnrampDataRepository.swift; sourceTree = ""; }; - EF8BE6BA2CCA98CA001FBA2B /* OnrampPairRequestItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnrampPairRequestItem.swift; sourceTree = ""; }; - EF8BE6BC2CCA9941001FBA2B /* OnrampRedirectDataRequestItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnrampRedirectDataRequestItem.swift; sourceTree = ""; }; - EF8BE6BF2CCA9CC3001FBA2B /* CommonOnrampDataRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonOnrampDataRepository.swift; sourceTree = ""; }; - EF8BE6C32CCB95D7001FBA2B /* OnrampRoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnrampRoutable.swift; sourceTree = ""; }; EF8D780E2981458500B67418 /* UserTokenListManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTokenListManagerMock.swift; sourceTree = ""; }; EF8DBCDC2C060A03009072CF /* StakingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingManager.swift; sourceTree = ""; }; EF8DBCDF2C060CCE009072CF /* StakingDependenciesFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StakingDependenciesFactory.swift; sourceTree = ""; }; @@ -5989,6 +5993,14 @@ path = Common; sourceTree = ""; }; + 2D1B27102CDA024F0066F484 /* Common */ = { + isa = PBXGroup; + children = ( + 2D1B27112CDA025A0066F484 /* CasperConstants.swift */, + ); + path = Common; + sourceTree = ""; + }; 2D1BB3662C074207008392F7 /* Markets */ = { isa = PBXGroup; children = ( @@ -6090,6 +6102,27 @@ path = WalletSelectorItemView; sourceTree = ""; }; + 2D616FEB2CD1414000581EA6 /* Casper */ = { + isa = PBXGroup; + children = ( + 2D1B27102CDA024F0066F484 /* Common */, + 2D616FE62CD1414000581EA6 /* CasperAddressService.swift */, + 2D616FE72CD1414000581EA6 /* CasperAddressUtils.swift */, + 2D616FE82CD1414000581EA6 /* CasperExternalLinkProvider.swift */, + 2D616FE92CD1414000581EA6 /* CasperWalletAssembly.swift */, + 2D616FEA2CD1414000581EA6 /* CasperWalletManager.swift */, + ); + path = Casper; + sourceTree = ""; + }; + 2D616FF62CD142BE00581EA6 /* Casper */ = { + isa = PBXGroup; + children = ( + 2D616FF52CD142BE00581EA6 /* CasperTests.swift */, + ); + path = Casper; + sourceTree = ""; + }; 2D788E7E2ADD657000E0884B /* WalletSelector */ = { isa = PBXGroup; children = ( @@ -10229,6 +10262,7 @@ B62D3C592CBC79D900C2460E /* Bitcoin */, B62D3C612CBC79D900C2460E /* BitcoinCash */, B62D3C7F2CBC79D900C2460E /* Cardano */, + 2D616FEB2CD1414000581EA6 /* Casper */, B62D3C922CBC79D900C2460E /* Chia */, B62D3CA42CBC79D900C2460E /* Cosmos */, B62D3CA92CBC79D900C2460E /* Dash */, @@ -10736,6 +10770,7 @@ B62D3F762CBC79DA00C2460E /* Bech32 */, B62D3F7A2CBC79DA00C2460E /* Moya+Combine */, B62D3F7B2CBC79DA00C2460E /* AnyPublisher+Async.swift */, + 2D616FF32CD1421000581EA6 /* Byte+.swift */, B62D3F7C2CBC79DA00C2460E /* BigUInt+.swift */, B62D3F7E2CBC79DA00C2460E /* CBOR+.swift */, B62D3F7F2CBC79DA00C2460E /* Collection+.swift */, @@ -11276,6 +11311,7 @@ B62D430D2CBC79E200C2460E /* Aptos */, B62D43172CBC79E300C2460E /* Bitcoin */, B62D432B2CBC79E300C2460E /* Cardano */, + 2D616FF62CD142BE00581EA6 /* Casper */, B62D432F2CBC79E400C2460E /* Chia */, B62D42F92CBC79E100C2460E /* Common */, B62D43132CBC79E300C2460E /* Cosmos */, @@ -17534,6 +17570,11 @@ B62D40F92CBC79DA00C2460E /* ICPProviderTarget.swift in Sources */, B604D5FF2CC2902B00CECFE2 /* Localizable+Generated.swift in Sources */, B62D42742CBC79DB00C2460E /* TransactionFeeProvider.swift in Sources */, + 2DA57BBF2CD650E900C890DF /* CasperWalletAssembly.swift in Sources */, + 2DA57BC02CD650E900C890DF /* CasperExternalLinkProvider.swift in Sources */, + 2DA57BC12CD650E900C890DF /* CasperAddressUtils.swift in Sources */, + 2DA57BC22CD650E900C890DF /* CasperWalletManager.swift in Sources */, + 2DA57BC32CD650E900C890DF /* CasperAddressService.swift in Sources */, B62D42CB2CBC79DB00C2460E /* Data+.swift in Sources */, B62D42112CBC79DB00C2460E /* SigningAlgorithm.swift in Sources */, B62D3FA32CBC79DA00C2460E /* AlgorandNetworkProvider.swift in Sources */, @@ -17618,6 +17659,7 @@ B62D408E2CBC79DA00C2460E /* TokenBalanceERC20TokenMethod.swift in Sources */, B62D40A32CBC79DA00C2460E /* AvalancheExternalLinkProvider.swift in Sources */, B62D3FFA2CBC79DA00C2460E /* OP_VER.swift in Sources */, + 2D616FF42CD1421000581EA6 /* Byte+.swift in Sources */, B62D425A2CBC79DB00C2460E /* JSONRPC.swift in Sources */, B62D42352CBC79DB00C2460E /* BlockcypherTarget.swift in Sources */, B62D418C2CBC79DA00C2460E /* SolanaFeeParameters.swift in Sources */, @@ -17681,6 +17723,7 @@ B62D42D42CBC79DB00C2460E /* Moya+.swift in Sources */, B62D40702CBC79DA00C2460E /* CosmosTransactionBuilder.swift in Sources */, B62D41972CBC79DA00C2460E /* StellarNetworkService.swift in Sources */, + 2D1B27122CDA025A0066F484 /* CasperConstants.swift in Sources */, B62D42912CBC79DB00C2460E /* TransactionHistoryProvider.swift in Sources */, B62D3FD92CBC79DA00C2460E /* OP_MUL.swift in Sources */, B62D40312CBC79DA00C2460E /* BitcoinWalletAssembly.swift in Sources */, @@ -18159,6 +18202,7 @@ B62D435E2CBC79E400C2460E /* KoinosTransactionBuilderTests.swift in Sources */, B62D43672CBC79E400C2460E /* XCTAssert+.swift in Sources */, B62D43402CBC79E400C2460E /* AddressTests.swift in Sources */, + 2D616FF72CD142BE00581EA6 /* CasperTests.swift in Sources */, B62D43482CBC79E400C2460E /* TransactionSizeTesterUtility.swift in Sources */, B62D435F2CBC79E400C2460E /* KoinosWalletManagerTests.swift in Sources */, B62D434B2CBC79E400C2460E /* BlockchainCodingKeyTests.swift in Sources */, diff --git a/TangemApp.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TangemApp.xcworkspace/xcshareddata/swiftpm/Package.resolved index dfd0b38a8a..cf56bf68e7 100644 --- a/TangemApp.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/TangemApp.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "584ca60af0fbbc527d504d4d480a556058e05f97a79e92bf44c594618d31cc9a", + "originHash" : "279320f6fd232b7bb4b2b24520941724ff8087e6be3fcae17ffa02b6129981e1", "pins" : [ { "identity" : "alamofire", @@ -40,7 +40,7 @@ { "identity" : "bigint", "kind" : "remoteSourceControl", - "location" : "https://github.com/attaswift/BigInt", + "location" : "https://github.com/attaswift/BigInt.git", "state" : { "revision" : "0ed110f7555c34ff468e72e1686e59721f2b0da6", "version" : "5.3.0"