├── .github └── workflows │ └── build.yml ├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Resources └── Logo.png ├── SolanaDemoApp.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── SolanaDemoApp ├── AppDelegate.swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── solana_icon-1024.png │ │ ├── solana_icon-20.png │ │ ├── solana_icon-20@2x.png │ │ ├── solana_icon-20@3x.png │ │ ├── solana_icon-29.png │ │ ├── solana_icon-29@2x.png │ │ ├── solana_icon-29@3x.png │ │ ├── solana_icon-40.png │ │ ├── solana_icon-40@2x.png │ │ ├── solana_icon-40@3x.png │ │ ├── solana_icon-60@2x.png │ │ ├── solana_icon-60@3x.png │ │ ├── solana_icon-76.png │ │ ├── solana_icon-76@2x.png │ │ └── solana_icon-83.5@2x.png │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── ContentView.swift ├── Info.plist ├── Output.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── ResultView.swift └── SceneDelegate.swift ├── SolanaJsonRPC.postman_collection.json ├── Sources └── Solana │ ├── Extensions │ ├── Double+.swift │ ├── Int+.swift │ ├── JSON+.swift │ └── String+.swift │ ├── Models │ ├── Account.swift │ ├── Block.swift │ ├── Commitment.swift │ ├── Encoding.swift │ ├── Filter.swift │ ├── Hash.swift │ ├── Identity.swift │ ├── Instruction.swift │ ├── Requests and Responses │ │ ├── GetAccountInfo.swift │ │ ├── GetBalanace.swift │ │ ├── GetBlockCommitment.swift │ │ ├── GetBlockProduction.swift │ │ ├── GetBlockTime.swift │ │ ├── GetClusterNodes.swift │ │ ├── GetConfirmedBlock.swift │ │ ├── GetConfirmedBlocks.swift │ │ ├── GetConfirmedBlocksWithLimit.swift │ │ ├── GetConfirmedSignaturesForAddress.swift │ │ ├── GetConfirmedSignaturesForAddress2.swift │ │ ├── GetConfirmedTransaction.swift │ │ ├── GetEpochInfo.swift │ │ ├── GetEpochSchedule.swift │ │ ├── GetFeeCalculatorForBlockhash.swift │ │ ├── GetFeeRateGovernor.swift │ │ ├── GetFees.swift │ │ ├── RPCResponse.swift │ │ └── SimulateTransaction.swift │ ├── RewardType.swift │ ├── RpcError.swift │ ├── SlotRange.swift │ ├── TokenBalance.swift │ ├── Transaction.swift │ ├── TransactionError.swift │ └── WebSockets │ │ └── ProgramSubscribe.swift │ ├── Network.swift │ ├── NetworkRequestType.swift │ ├── Networking.swift │ ├── Solana.swift │ ├── SolanaErrors.swift │ ├── SolanaSockets.swift │ └── WebSockets.swift └── Tests ├── LinuxMain.swift └── SolanaTests ├── SolanaTests.swift └── XCTestManifests.swift /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: swift build -v 18 | - name: Run tests 19 | run: swift test -v -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | 92 | .DS_Store 93 | /.build 94 | /Packages -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dunesailer Research 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Base58Swift", 6 | "repositoryURL": "https://github.com/keefertaylor/Base58Swift", 7 | "state": { 8 | "branch": null, 9 | "revision": "1c13ea6b07f1584660526f8bde3d4cc150e91acd", 10 | "version": "2.1.14" 11 | } 12 | }, 13 | { 14 | "package": "BigInt", 15 | "repositoryURL": "https://github.com/attaswift/BigInt.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "889a1ecacd73ccc189c5cb29288048f186c44ed9", 19 | "version": "5.2.1" 20 | } 21 | }, 22 | { 23 | "package": "CryptoSwift", 24 | "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "5669f222e46c8134fb1f399c745fa6882b43532e", 28 | "version": "1.3.8" 29 | } 30 | }, 31 | { 32 | "package": "ed25519swift", 33 | "repositoryURL": "https://github.com/crewshin/ed25519swift", 34 | "state": { 35 | "branch": null, 36 | "revision": "b14719cad9fa541a5ec9b3de5aff8e9c4cc6ea07", 37 | "version": "1.2.6" 38 | } 39 | } 40 | ] 41 | }, 42 | "version": 1 43 | } 44 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Solana", 8 | platforms: [ 9 | .iOS(.v13), 10 | .macOS(.v10_15), 11 | ], 12 | products: [ 13 | // Products define the executables and libraries a package produces, and make them visible to other packages. 14 | .library( 15 | name: "Solana", 16 | targets: ["Solana"]), 17 | ], 18 | dependencies: [ 19 | .package(url: "https://github.com/keefertaylor/Base58Swift", .upToNextMajor(from: "2.1.14")), 20 | .package(url: "https://github.com/crewshin/ed25519swift", .upToNextMajor(from: "1.2.6")), 21 | ], 22 | targets: [ 23 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 24 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 25 | .target( 26 | name: "Solana", 27 | dependencies: [ 28 | "Base58Swift", 29 | "ed25519swift" 30 | ]), 31 | .testTarget( 32 | name: "SolanaTests", 33 | dependencies: ["Solana"]), 34 | ], 35 | swiftLanguageVersions: [.v5] 36 | ) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Repo image](https://github.com/crewshin/solana-swift/blob/main/Resources/Logo.png) 2 | 3 | # Solana-Swift 4 | 5 | ![Build & Test](https://github.com/crewshin/solana-swift/actions/workflows/build.yml/badge.svg) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://github.com/crewshin/solana-swift/blob/main/LICENSE) 7 | [![swift-version](https://img.shields.io/badge/swift-5.1-brightgreen.svg)](https://github.com/apple/swift) 8 | 9 | Solana Swift SDK (Based off https://docs.solana.com/developing/clients/jsonrpc-api). 10 | 11 | This is actively **WIP** at the moment. PR's welcome! [Development happening in the demo app repo](https://github.com/crewshin/solana-swift-demo-app) 12 | 13 | 14 | ## Getting Started 15 | 16 | #### Swift Package Manager (Xcode 11 and above) 17 | 1. Select `File`/`Swift Packages`/`Add Package Dependency` from the menu. 18 | 2. Paste `https://github.com/crewshin/solana-swift.git`. 19 | 20 | Why not CocoaPods, or Carthage? 21 | 22 | Supporting multiple dependency managers makes maintaining a library exponentially more complicated and time consuming. Furthermore, with the integration of the Swift Package Manager in Xcode 11 and greater, I expect the need for alternative solutions to fade quickly. 23 | 24 | 25 | ## Usage 26 | 27 | ```swift 28 | import Solana 29 | ``` 30 | 31 | 32 | Create an instance of Solana: 33 | 34 | ```swift 35 | let solana = Solana(network: .main) 36 | ``` 37 | 38 | or (not functional yet) 39 | 40 | ```swift 41 | let solana = SolanaSockets(network: .main) 42 | ``` 43 | 44 | Then: 45 | 46 | ```swift 47 | solana.getAccountInfo(pubkey: pubkey) { (result) in 48 | switch result { 49 | case .failure(let error): 50 | if case let SolanaAPIError.getAccountInfoError(message) = error { 51 | print(message) 52 | } 53 | case .success(let response): 54 | if let value = response.value { 55 | print(value) 56 | } 57 | } 58 | } 59 | 60 | ``` 61 | 62 | 63 | 64 | ## WIP Status 65 | 66 | I'm basically going down the list of methods from the [JSON RPC](https://docs.solana.com/developing/clients/jsonrpc-api#methods) 67 | 68 | Right now I've implemented the foundational stuff and am n this commit, I've done a first pass on: 69 | 70 | ```swift 71 | getAccountInfo 72 | getBalance 73 | getBlockCommitment 74 | getBlockTime 75 | getClusterNodes 76 | getConfirmedBlock 77 | getBlockProduction 78 | getConfirmedBlocks 79 | getConfirmedBlocksWithLimit 80 | getConfirmedSignaturesForAddress 81 | getConfirmedSignaturesForAddress2 82 | getConfirmedTransaction 83 | getEpochInfo 84 | getEpochSchedule 85 | getFeeCalculatorForBlockhash 86 | getFeeRateGovernor 87 | ``` 88 | 89 | 90 | Feel free to say hi on Discord: `crewshin#3286` 91 | 92 | -------------------------------------------------------------------------------- /Resources/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/Resources/Logo.png -------------------------------------------------------------------------------- /SolanaDemoApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 52; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 06967D5A264F977B0066A104 /* RPCResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06967D59264F977B0066A104 /* RPCResponse.swift */; }; 11 | 7E0CE92626238342006F0E8E /* Double+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90026238342006F0E8E /* Double+.swift */; }; 12 | 7E0CE92726238342006F0E8E /* Int+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90126238342006F0E8E /* Int+.swift */; }; 13 | 7E0CE92826238342006F0E8E /* JSON+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90226238342006F0E8E /* JSON+.swift */; }; 14 | 7E0CE92926238342006F0E8E /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90326238342006F0E8E /* String+.swift */; }; 15 | 7E0CE92A26238342006F0E8E /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90526238342006F0E8E /* Account.swift */; }; 16 | 7E0CE92B26238342006F0E8E /* Commitment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90626238342006F0E8E /* Commitment.swift */; }; 17 | 7E0CE92C26238342006F0E8E /* Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90726238342006F0E8E /* Encoding.swift */; }; 18 | 7E0CE92D26238342006F0E8E /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90826238342006F0E8E /* Filter.swift */; }; 19 | 7E0CE92E26238342006F0E8E /* Instruction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90926238342006F0E8E /* Instruction.swift */; }; 20 | 7E0CE92F26238342006F0E8E /* GetAccountInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90B26238342006F0E8E /* GetAccountInfo.swift */; }; 21 | 7E0CE93026238342006F0E8E /* GetBalanace.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90C26238342006F0E8E /* GetBalanace.swift */; }; 22 | 7E0CE93126238342006F0E8E /* GetBlockCommitment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90D26238342006F0E8E /* GetBlockCommitment.swift */; }; 23 | 7E0CE93226238342006F0E8E /* GetBlockTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90E26238342006F0E8E /* GetBlockTime.swift */; }; 24 | 7E0CE93326238342006F0E8E /* GetClusterNodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE90F26238342006F0E8E /* GetClusterNodes.swift */; }; 25 | 7E0CE93426238342006F0E8E /* GetConfirmedBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91026238342006F0E8E /* GetConfirmedBlock.swift */; }; 26 | 7E0CE93526238342006F0E8E /* GetConfirmedBlocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91126238342006F0E8E /* GetConfirmedBlocks.swift */; }; 27 | 7E0CE93626238342006F0E8E /* GetConfirmedBlocksWithLimit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91226238342006F0E8E /* GetConfirmedBlocksWithLimit.swift */; }; 28 | 7E0CE93726238342006F0E8E /* GetConfirmedSignaturesForAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91326238342006F0E8E /* GetConfirmedSignaturesForAddress.swift */; }; 29 | 7E0CE93826238342006F0E8E /* GetConfirmedSignaturesForAddress2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91426238342006F0E8E /* GetConfirmedSignaturesForAddress2.swift */; }; 30 | 7E0CE93926238342006F0E8E /* GetConfirmedTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91526238342006F0E8E /* GetConfirmedTransaction.swift */; }; 31 | 7E0CE93A26238342006F0E8E /* GetEpochInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91626238342006F0E8E /* GetEpochInfo.swift */; }; 32 | 7E0CE93B26238342006F0E8E /* SimulateTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91726238342006F0E8E /* SimulateTransaction.swift */; }; 33 | 7E0CE93C26238342006F0E8E /* RewardType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91826238342006F0E8E /* RewardType.swift */; }; 34 | 7E0CE93D26238342006F0E8E /* RpcError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91926238342006F0E8E /* RpcError.swift */; }; 35 | 7E0CE93E26238342006F0E8E /* TokenBalance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91A26238342006F0E8E /* TokenBalance.swift */; }; 36 | 7E0CE93F26238342006F0E8E /* Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91B26238342006F0E8E /* Transaction.swift */; }; 37 | 7E0CE94026238342006F0E8E /* TransactionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91C26238342006F0E8E /* TransactionError.swift */; }; 38 | 7E0CE94126238342006F0E8E /* ProgramSubscribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91E26238342006F0E8E /* ProgramSubscribe.swift */; }; 39 | 7E0CE94226238342006F0E8E /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE91F26238342006F0E8E /* Network.swift */; }; 40 | 7E0CE94326238342006F0E8E /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE92026238342006F0E8E /* Networking.swift */; }; 41 | 7E0CE94426238342006F0E8E /* NetworkRequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE92126238342006F0E8E /* NetworkRequestType.swift */; }; 42 | 7E0CE94526238342006F0E8E /* Solana.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE92226238342006F0E8E /* Solana.swift */; }; 43 | 7E0CE94626238342006F0E8E /* SolanaErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE92326238342006F0E8E /* SolanaErrors.swift */; }; 44 | 7E0CE94726238342006F0E8E /* SolanaSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE92426238342006F0E8E /* SolanaSockets.swift */; }; 45 | 7E0CE94826238342006F0E8E /* WebSockets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E0CE92526238342006F0E8E /* WebSockets.swift */; }; 46 | 7E30FC6725FBBBFB0036597A /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E30FC6625FBBBFB0036597A /* ResultView.swift */; }; 47 | 7E6DAE9B2627256700A6F118 /* Base58Swift in Frameworks */ = {isa = PBXBuildFile; productRef = 7E6DAE9A2627256700A6F118 /* Base58Swift */; }; 48 | 7E6DAE9F2627259500A6F118 /* ed25519swift in Frameworks */ = {isa = PBXBuildFile; productRef = 7E6DAE9E2627259500A6F118 /* ed25519swift */; }; 49 | 7E7E63852650A19F0044026A /* Output.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7E63842650A19F0044026A /* Output.swift */; }; 50 | 7E7E6387265168120044026A /* GetEpochSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7E6386265168120044026A /* GetEpochSchedule.swift */; }; 51 | 7E7E638926516D5D0044026A /* GetFeeCalculatorForBlockhash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7E638826516D5D0044026A /* GetFeeCalculatorForBlockhash.swift */; }; 52 | 7E7E638B2652A5CB0044026A /* GetFeeRateGovernor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7E638A2652A5CB0044026A /* GetFeeRateGovernor.swift */; }; 53 | 7E7E638D2652A7FF0044026A /* GetBlockProduction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7E638C2652A7FF0044026A /* GetBlockProduction.swift */; }; 54 | 7E7E638F2652B1CF0044026A /* SlotRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7E638E2652B1CF0044026A /* SlotRange.swift */; }; 55 | 7E937B2125F9A0330066CA84 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E937B2025F9A0330066CA84 /* AppDelegate.swift */; }; 56 | 7E937B2325F9A0330066CA84 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E937B2225F9A0330066CA84 /* SceneDelegate.swift */; }; 57 | 7E937B2525F9A0330066CA84 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E937B2425F9A0330066CA84 /* ContentView.swift */; }; 58 | 7E937B2725F9A0360066CA84 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7E937B2625F9A0360066CA84 /* Assets.xcassets */; }; 59 | 7E937B2A25F9A0360066CA84 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7E937B2925F9A0360066CA84 /* Preview Assets.xcassets */; }; 60 | 7E937B2D25F9A0360066CA84 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E937B2B25F9A0360066CA84 /* LaunchScreen.storyboard */; }; 61 | 7E94A187272F3A52003852B0 /* Hash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E94A185272F3A52003852B0 /* Hash.swift */; }; 62 | 7E94A188272F3A52003852B0 /* Block.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E94A186272F3A52003852B0 /* Block.swift */; }; 63 | 7E94A18A272F3C33003852B0 /* Identity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E94A189272F3C33003852B0 /* Identity.swift */; }; 64 | 7EA423EB2669B0430011E098 /* GetFees.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EA423EA2669B0430011E098 /* GetFees.swift */; }; 65 | 7EF9255A266BE068008E46D1 /* CodeEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 7EF92559266BE068008E46D1 /* CodeEditor */; }; 66 | /* End PBXBuildFile section */ 67 | 68 | /* Begin PBXFileReference section */ 69 | 06967D59264F977B0066A104 /* RPCResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RPCResponse.swift; sourceTree = ""; }; 70 | 7E0CE90026238342006F0E8E /* Double+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+.swift"; sourceTree = ""; }; 71 | 7E0CE90126238342006F0E8E /* Int+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Int+.swift"; sourceTree = ""; }; 72 | 7E0CE90226238342006F0E8E /* JSON+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSON+.swift"; sourceTree = ""; }; 73 | 7E0CE90326238342006F0E8E /* String+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; 74 | 7E0CE90526238342006F0E8E /* Account.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = ""; }; 75 | 7E0CE90626238342006F0E8E /* Commitment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Commitment.swift; sourceTree = ""; }; 76 | 7E0CE90726238342006F0E8E /* Encoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Encoding.swift; sourceTree = ""; }; 77 | 7E0CE90826238342006F0E8E /* Filter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = ""; }; 78 | 7E0CE90926238342006F0E8E /* Instruction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Instruction.swift; sourceTree = ""; }; 79 | 7E0CE90B26238342006F0E8E /* GetAccountInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetAccountInfo.swift; sourceTree = ""; }; 80 | 7E0CE90C26238342006F0E8E /* GetBalanace.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetBalanace.swift; sourceTree = ""; }; 81 | 7E0CE90D26238342006F0E8E /* GetBlockCommitment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetBlockCommitment.swift; sourceTree = ""; }; 82 | 7E0CE90E26238342006F0E8E /* GetBlockTime.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetBlockTime.swift; sourceTree = ""; }; 83 | 7E0CE90F26238342006F0E8E /* GetClusterNodes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetClusterNodes.swift; sourceTree = ""; }; 84 | 7E0CE91026238342006F0E8E /* GetConfirmedBlock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfirmedBlock.swift; sourceTree = ""; }; 85 | 7E0CE91126238342006F0E8E /* GetConfirmedBlocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfirmedBlocks.swift; sourceTree = ""; }; 86 | 7E0CE91226238342006F0E8E /* GetConfirmedBlocksWithLimit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfirmedBlocksWithLimit.swift; sourceTree = ""; }; 87 | 7E0CE91326238342006F0E8E /* GetConfirmedSignaturesForAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfirmedSignaturesForAddress.swift; sourceTree = ""; }; 88 | 7E0CE91426238342006F0E8E /* GetConfirmedSignaturesForAddress2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfirmedSignaturesForAddress2.swift; sourceTree = ""; }; 89 | 7E0CE91526238342006F0E8E /* GetConfirmedTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetConfirmedTransaction.swift; sourceTree = ""; }; 90 | 7E0CE91626238342006F0E8E /* GetEpochInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetEpochInfo.swift; sourceTree = ""; }; 91 | 7E0CE91726238342006F0E8E /* SimulateTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SimulateTransaction.swift; sourceTree = ""; }; 92 | 7E0CE91826238342006F0E8E /* RewardType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RewardType.swift; sourceTree = ""; }; 93 | 7E0CE91926238342006F0E8E /* RpcError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RpcError.swift; sourceTree = ""; }; 94 | 7E0CE91A26238342006F0E8E /* TokenBalance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenBalance.swift; sourceTree = ""; }; 95 | 7E0CE91B26238342006F0E8E /* Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transaction.swift; sourceTree = ""; }; 96 | 7E0CE91C26238342006F0E8E /* TransactionError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionError.swift; sourceTree = ""; }; 97 | 7E0CE91E26238342006F0E8E /* ProgramSubscribe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgramSubscribe.swift; sourceTree = ""; }; 98 | 7E0CE91F26238342006F0E8E /* Network.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; 99 | 7E0CE92026238342006F0E8E /* Networking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = ""; }; 100 | 7E0CE92126238342006F0E8E /* NetworkRequestType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRequestType.swift; sourceTree = ""; }; 101 | 7E0CE92226238342006F0E8E /* Solana.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Solana.swift; sourceTree = ""; }; 102 | 7E0CE92326238342006F0E8E /* SolanaErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SolanaErrors.swift; sourceTree = ""; }; 103 | 7E0CE92426238342006F0E8E /* SolanaSockets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SolanaSockets.swift; sourceTree = ""; }; 104 | 7E0CE92526238342006F0E8E /* WebSockets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSockets.swift; sourceTree = ""; }; 105 | 7E30FC6625FBBBFB0036597A /* ResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; 106 | 7E7E63842650A19F0044026A /* Output.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Output.swift; sourceTree = ""; }; 107 | 7E7E6386265168120044026A /* GetEpochSchedule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetEpochSchedule.swift; sourceTree = ""; }; 108 | 7E7E638826516D5D0044026A /* GetFeeCalculatorForBlockhash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetFeeCalculatorForBlockhash.swift; sourceTree = ""; }; 109 | 7E7E638A2652A5CB0044026A /* GetFeeRateGovernor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetFeeRateGovernor.swift; sourceTree = ""; }; 110 | 7E7E638C2652A7FF0044026A /* GetBlockProduction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetBlockProduction.swift; sourceTree = ""; }; 111 | 7E7E638E2652B1CF0044026A /* SlotRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SlotRange.swift; sourceTree = ""; }; 112 | 7E937B1D25F9A0330066CA84 /* SolanaDemoApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SolanaDemoApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 113 | 7E937B2025F9A0330066CA84 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 114 | 7E937B2225F9A0330066CA84 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 115 | 7E937B2425F9A0330066CA84 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 116 | 7E937B2625F9A0360066CA84 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 117 | 7E937B2925F9A0360066CA84 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 118 | 7E937B2C25F9A0360066CA84 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 119 | 7E937B2E25F9A0360066CA84 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 120 | 7E94A185272F3A52003852B0 /* Hash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hash.swift; sourceTree = ""; }; 121 | 7E94A186272F3A52003852B0 /* Block.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Block.swift; sourceTree = ""; }; 122 | 7E94A189272F3C33003852B0 /* Identity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Identity.swift; sourceTree = ""; }; 123 | 7EA423EA2669B0430011E098 /* GetFees.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetFees.swift; sourceTree = ""; }; 124 | /* End PBXFileReference section */ 125 | 126 | /* Begin PBXFrameworksBuildPhase section */ 127 | 7E937B1A25F9A0330066CA84 /* Frameworks */ = { 128 | isa = PBXFrameworksBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | 7E6DAE9F2627259500A6F118 /* ed25519swift in Frameworks */, 132 | 7EF9255A266BE068008E46D1 /* CodeEditor in Frameworks */, 133 | 7E6DAE9B2627256700A6F118 /* Base58Swift in Frameworks */, 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXFrameworksBuildPhase section */ 138 | 139 | /* Begin PBXGroup section */ 140 | 7E0CE8FD26238342006F0E8E /* Sources */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 7E0CE8FE26238342006F0E8E /* Solana */, 144 | ); 145 | path = Sources; 146 | sourceTree = ""; 147 | }; 148 | 7E0CE8FE26238342006F0E8E /* Solana */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 7E0CE8FF26238342006F0E8E /* Extensions */, 152 | 7E0CE90426238342006F0E8E /* Models */, 153 | 7E0CE91F26238342006F0E8E /* Network.swift */, 154 | 7E0CE92026238342006F0E8E /* Networking.swift */, 155 | 7E0CE92126238342006F0E8E /* NetworkRequestType.swift */, 156 | 7E0CE92226238342006F0E8E /* Solana.swift */, 157 | 7E0CE92326238342006F0E8E /* SolanaErrors.swift */, 158 | 7E0CE92426238342006F0E8E /* SolanaSockets.swift */, 159 | 7E0CE92526238342006F0E8E /* WebSockets.swift */, 160 | ); 161 | path = Solana; 162 | sourceTree = ""; 163 | }; 164 | 7E0CE8FF26238342006F0E8E /* Extensions */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 7E0CE90026238342006F0E8E /* Double+.swift */, 168 | 7E0CE90126238342006F0E8E /* Int+.swift */, 169 | 7E0CE90226238342006F0E8E /* JSON+.swift */, 170 | 7E0CE90326238342006F0E8E /* String+.swift */, 171 | ); 172 | path = Extensions; 173 | sourceTree = ""; 174 | }; 175 | 7E0CE90426238342006F0E8E /* Models */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 7E0CE90A26238342006F0E8E /* Requests and Responses */, 179 | 7E0CE91D26238342006F0E8E /* WebSockets */, 180 | 7E0CE90526238342006F0E8E /* Account.swift */, 181 | 7E94A186272F3A52003852B0 /* Block.swift */, 182 | 7E0CE90626238342006F0E8E /* Commitment.swift */, 183 | 7E0CE90726238342006F0E8E /* Encoding.swift */, 184 | 7E0CE90826238342006F0E8E /* Filter.swift */, 185 | 7E94A185272F3A52003852B0 /* Hash.swift */, 186 | 7E0CE90926238342006F0E8E /* Instruction.swift */, 187 | 7E0CE91826238342006F0E8E /* RewardType.swift */, 188 | 7E0CE91926238342006F0E8E /* RpcError.swift */, 189 | 7E0CE91A26238342006F0E8E /* TokenBalance.swift */, 190 | 7E0CE91B26238342006F0E8E /* Transaction.swift */, 191 | 7E0CE91C26238342006F0E8E /* TransactionError.swift */, 192 | 7E7E638E2652B1CF0044026A /* SlotRange.swift */, 193 | 7E94A189272F3C33003852B0 /* Identity.swift */, 194 | ); 195 | path = Models; 196 | sourceTree = ""; 197 | }; 198 | 7E0CE90A26238342006F0E8E /* Requests and Responses */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | 06967D59264F977B0066A104 /* RPCResponse.swift */, 202 | 7E0CE90B26238342006F0E8E /* GetAccountInfo.swift */, 203 | 7E0CE90C26238342006F0E8E /* GetBalanace.swift */, 204 | 7E0CE90D26238342006F0E8E /* GetBlockCommitment.swift */, 205 | 7E0CE90E26238342006F0E8E /* GetBlockTime.swift */, 206 | 7E0CE90F26238342006F0E8E /* GetClusterNodes.swift */, 207 | 7E0CE91026238342006F0E8E /* GetConfirmedBlock.swift */, 208 | 7E7E638C2652A7FF0044026A /* GetBlockProduction.swift */, 209 | 7E0CE91126238342006F0E8E /* GetConfirmedBlocks.swift */, 210 | 7E0CE91226238342006F0E8E /* GetConfirmedBlocksWithLimit.swift */, 211 | 7E0CE91326238342006F0E8E /* GetConfirmedSignaturesForAddress.swift */, 212 | 7E0CE91426238342006F0E8E /* GetConfirmedSignaturesForAddress2.swift */, 213 | 7E0CE91526238342006F0E8E /* GetConfirmedTransaction.swift */, 214 | 7E0CE91626238342006F0E8E /* GetEpochInfo.swift */, 215 | 7E7E6386265168120044026A /* GetEpochSchedule.swift */, 216 | 7E7E638826516D5D0044026A /* GetFeeCalculatorForBlockhash.swift */, 217 | 7E7E638A2652A5CB0044026A /* GetFeeRateGovernor.swift */, 218 | 7EA423EA2669B0430011E098 /* GetFees.swift */, 219 | 7E0CE91726238342006F0E8E /* SimulateTransaction.swift */, 220 | ); 221 | path = "Requests and Responses"; 222 | sourceTree = ""; 223 | }; 224 | 7E0CE91D26238342006F0E8E /* WebSockets */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | 7E0CE91E26238342006F0E8E /* ProgramSubscribe.swift */, 228 | ); 229 | path = WebSockets; 230 | sourceTree = ""; 231 | }; 232 | 7E937B1425F9A0330066CA84 = { 233 | isa = PBXGroup; 234 | children = ( 235 | 7E0CE8FD26238342006F0E8E /* Sources */, 236 | 7E937B1F25F9A0330066CA84 /* SolanaDemoApp */, 237 | 7E937B1E25F9A0330066CA84 /* Products */, 238 | ); 239 | sourceTree = ""; 240 | }; 241 | 7E937B1E25F9A0330066CA84 /* Products */ = { 242 | isa = PBXGroup; 243 | children = ( 244 | 7E937B1D25F9A0330066CA84 /* SolanaDemoApp.app */, 245 | ); 246 | name = Products; 247 | sourceTree = ""; 248 | }; 249 | 7E937B1F25F9A0330066CA84 /* SolanaDemoApp */ = { 250 | isa = PBXGroup; 251 | children = ( 252 | 7E937B2025F9A0330066CA84 /* AppDelegate.swift */, 253 | 7E937B2225F9A0330066CA84 /* SceneDelegate.swift */, 254 | 7E937B2425F9A0330066CA84 /* ContentView.swift */, 255 | 7E7E63842650A19F0044026A /* Output.swift */, 256 | 7E30FC6625FBBBFB0036597A /* ResultView.swift */, 257 | 7E937B2625F9A0360066CA84 /* Assets.xcassets */, 258 | 7E937B2B25F9A0360066CA84 /* LaunchScreen.storyboard */, 259 | 7E937B2E25F9A0360066CA84 /* Info.plist */, 260 | 7E937B2825F9A0360066CA84 /* Preview Content */, 261 | ); 262 | path = SolanaDemoApp; 263 | sourceTree = ""; 264 | }; 265 | 7E937B2825F9A0360066CA84 /* Preview Content */ = { 266 | isa = PBXGroup; 267 | children = ( 268 | 7E937B2925F9A0360066CA84 /* Preview Assets.xcassets */, 269 | ); 270 | path = "Preview Content"; 271 | sourceTree = ""; 272 | }; 273 | /* End PBXGroup section */ 274 | 275 | /* Begin PBXNativeTarget section */ 276 | 7E937B1C25F9A0330066CA84 /* SolanaDemoApp */ = { 277 | isa = PBXNativeTarget; 278 | buildConfigurationList = 7E937B3125F9A0360066CA84 /* Build configuration list for PBXNativeTarget "SolanaDemoApp" */; 279 | buildPhases = ( 280 | 7E937B1925F9A0330066CA84 /* Sources */, 281 | 7E937B1A25F9A0330066CA84 /* Frameworks */, 282 | 7E937B1B25F9A0330066CA84 /* Resources */, 283 | ); 284 | buildRules = ( 285 | ); 286 | dependencies = ( 287 | ); 288 | name = SolanaDemoApp; 289 | packageProductDependencies = ( 290 | 7E6DAE9A2627256700A6F118 /* Base58Swift */, 291 | 7E6DAE9E2627259500A6F118 /* ed25519swift */, 292 | 7EF92559266BE068008E46D1 /* CodeEditor */, 293 | ); 294 | productName = SolanaDemoApp; 295 | productReference = 7E937B1D25F9A0330066CA84 /* SolanaDemoApp.app */; 296 | productType = "com.apple.product-type.application"; 297 | }; 298 | /* End PBXNativeTarget section */ 299 | 300 | /* Begin PBXProject section */ 301 | 7E937B1525F9A0330066CA84 /* Project object */ = { 302 | isa = PBXProject; 303 | attributes = { 304 | LastSwiftUpdateCheck = 1240; 305 | LastUpgradeCheck = 1240; 306 | TargetAttributes = { 307 | 7E937B1C25F9A0330066CA84 = { 308 | CreatedOnToolsVersion = 12.4; 309 | }; 310 | }; 311 | }; 312 | buildConfigurationList = 7E937B1825F9A0330066CA84 /* Build configuration list for PBXProject "SolanaDemoApp" */; 313 | compatibilityVersion = "Xcode 9.3"; 314 | developmentRegion = en; 315 | hasScannedForEncodings = 0; 316 | knownRegions = ( 317 | en, 318 | Base, 319 | ); 320 | mainGroup = 7E937B1425F9A0330066CA84; 321 | packageReferences = ( 322 | 7E6DAE992627256700A6F118 /* XCRemoteSwiftPackageReference "Base58Swift" */, 323 | 7E6DAE9D2627259500A6F118 /* XCRemoteSwiftPackageReference "ed25519swift" */, 324 | 7EF92558266BE068008E46D1 /* XCRemoteSwiftPackageReference "CodeEditor" */, 325 | ); 326 | productRefGroup = 7E937B1E25F9A0330066CA84 /* Products */; 327 | projectDirPath = ""; 328 | projectRoot = ""; 329 | targets = ( 330 | 7E937B1C25F9A0330066CA84 /* SolanaDemoApp */, 331 | ); 332 | }; 333 | /* End PBXProject section */ 334 | 335 | /* Begin PBXResourcesBuildPhase section */ 336 | 7E937B1B25F9A0330066CA84 /* Resources */ = { 337 | isa = PBXResourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 7E937B2D25F9A0360066CA84 /* LaunchScreen.storyboard in Resources */, 341 | 7E937B2A25F9A0360066CA84 /* Preview Assets.xcassets in Resources */, 342 | 7E937B2725F9A0360066CA84 /* Assets.xcassets in Resources */, 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | }; 346 | /* End PBXResourcesBuildPhase section */ 347 | 348 | /* Begin PBXSourcesBuildPhase section */ 349 | 7E937B1925F9A0330066CA84 /* Sources */ = { 350 | isa = PBXSourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | 7E0CE93826238342006F0E8E /* GetConfirmedSignaturesForAddress2.swift in Sources */, 354 | 7E0CE93626238342006F0E8E /* GetConfirmedBlocksWithLimit.swift in Sources */, 355 | 7E937B2125F9A0330066CA84 /* AppDelegate.swift in Sources */, 356 | 7E7E638F2652B1CF0044026A /* SlotRange.swift in Sources */, 357 | 7E0CE92D26238342006F0E8E /* Filter.swift in Sources */, 358 | 7E0CE94826238342006F0E8E /* WebSockets.swift in Sources */, 359 | 7E7E6387265168120044026A /* GetEpochSchedule.swift in Sources */, 360 | 7E94A18A272F3C33003852B0 /* Identity.swift in Sources */, 361 | 7E937B2325F9A0330066CA84 /* SceneDelegate.swift in Sources */, 362 | 7E0CE94226238342006F0E8E /* Network.swift in Sources */, 363 | 7E0CE92926238342006F0E8E /* String+.swift in Sources */, 364 | 7E0CE93B26238342006F0E8E /* SimulateTransaction.swift in Sources */, 365 | 7E7E638D2652A7FF0044026A /* GetBlockProduction.swift in Sources */, 366 | 7E0CE93526238342006F0E8E /* GetConfirmedBlocks.swift in Sources */, 367 | 7E0CE94526238342006F0E8E /* Solana.swift in Sources */, 368 | 7E0CE93D26238342006F0E8E /* RpcError.swift in Sources */, 369 | 7E0CE93726238342006F0E8E /* GetConfirmedSignaturesForAddress.swift in Sources */, 370 | 7E30FC6725FBBBFB0036597A /* ResultView.swift in Sources */, 371 | 7E0CE94626238342006F0E8E /* SolanaErrors.swift in Sources */, 372 | 7E0CE94126238342006F0E8E /* ProgramSubscribe.swift in Sources */, 373 | 7E0CE92B26238342006F0E8E /* Commitment.swift in Sources */, 374 | 7E0CE92F26238342006F0E8E /* GetAccountInfo.swift in Sources */, 375 | 7EA423EB2669B0430011E098 /* GetFees.swift in Sources */, 376 | 7E937B2525F9A0330066CA84 /* ContentView.swift in Sources */, 377 | 7E0CE93F26238342006F0E8E /* Transaction.swift in Sources */, 378 | 7E0CE94026238342006F0E8E /* TransactionError.swift in Sources */, 379 | 7E94A187272F3A52003852B0 /* Hash.swift in Sources */, 380 | 7E0CE93026238342006F0E8E /* GetBalanace.swift in Sources */, 381 | 7E0CE94326238342006F0E8E /* Networking.swift in Sources */, 382 | 7E0CE93326238342006F0E8E /* GetClusterNodes.swift in Sources */, 383 | 7E94A188272F3A52003852B0 /* Block.swift in Sources */, 384 | 06967D5A264F977B0066A104 /* RPCResponse.swift in Sources */, 385 | 7E0CE92A26238342006F0E8E /* Account.swift in Sources */, 386 | 7E0CE92C26238342006F0E8E /* Encoding.swift in Sources */, 387 | 7E0CE93126238342006F0E8E /* GetBlockCommitment.swift in Sources */, 388 | 7E7E638B2652A5CB0044026A /* GetFeeRateGovernor.swift in Sources */, 389 | 7E0CE93E26238342006F0E8E /* TokenBalance.swift in Sources */, 390 | 7E0CE93426238342006F0E8E /* GetConfirmedBlock.swift in Sources */, 391 | 7E7E63852650A19F0044026A /* Output.swift in Sources */, 392 | 7E0CE93226238342006F0E8E /* GetBlockTime.swift in Sources */, 393 | 7E0CE92E26238342006F0E8E /* Instruction.swift in Sources */, 394 | 7E0CE93A26238342006F0E8E /* GetEpochInfo.swift in Sources */, 395 | 7E0CE92626238342006F0E8E /* Double+.swift in Sources */, 396 | 7E0CE94726238342006F0E8E /* SolanaSockets.swift in Sources */, 397 | 7E0CE93926238342006F0E8E /* GetConfirmedTransaction.swift in Sources */, 398 | 7E7E638926516D5D0044026A /* GetFeeCalculatorForBlockhash.swift in Sources */, 399 | 7E0CE92826238342006F0E8E /* JSON+.swift in Sources */, 400 | 7E0CE94426238342006F0E8E /* NetworkRequestType.swift in Sources */, 401 | 7E0CE92726238342006F0E8E /* Int+.swift in Sources */, 402 | 7E0CE93C26238342006F0E8E /* RewardType.swift in Sources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | /* End PBXSourcesBuildPhase section */ 407 | 408 | /* Begin PBXVariantGroup section */ 409 | 7E937B2B25F9A0360066CA84 /* LaunchScreen.storyboard */ = { 410 | isa = PBXVariantGroup; 411 | children = ( 412 | 7E937B2C25F9A0360066CA84 /* Base */, 413 | ); 414 | name = LaunchScreen.storyboard; 415 | sourceTree = ""; 416 | }; 417 | /* End PBXVariantGroup section */ 418 | 419 | /* Begin XCBuildConfiguration section */ 420 | 7E937B2F25F9A0360066CA84 /* Debug */ = { 421 | isa = XCBuildConfiguration; 422 | buildSettings = { 423 | ALWAYS_SEARCH_USER_PATHS = NO; 424 | CLANG_ANALYZER_NONNULL = YES; 425 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 426 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 427 | CLANG_CXX_LIBRARY = "libc++"; 428 | CLANG_ENABLE_MODULES = YES; 429 | CLANG_ENABLE_OBJC_ARC = YES; 430 | CLANG_ENABLE_OBJC_WEAK = YES; 431 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 432 | CLANG_WARN_BOOL_CONVERSION = YES; 433 | CLANG_WARN_COMMA = YES; 434 | CLANG_WARN_CONSTANT_CONVERSION = YES; 435 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 436 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 437 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 438 | CLANG_WARN_EMPTY_BODY = YES; 439 | CLANG_WARN_ENUM_CONVERSION = YES; 440 | CLANG_WARN_INFINITE_RECURSION = YES; 441 | CLANG_WARN_INT_CONVERSION = YES; 442 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 443 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 444 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 445 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 446 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 447 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 448 | CLANG_WARN_STRICT_PROTOTYPES = YES; 449 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 450 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 451 | CLANG_WARN_UNREACHABLE_CODE = YES; 452 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 453 | COPY_PHASE_STRIP = NO; 454 | DEBUG_INFORMATION_FORMAT = dwarf; 455 | ENABLE_STRICT_OBJC_MSGSEND = YES; 456 | ENABLE_TESTABILITY = YES; 457 | GCC_C_LANGUAGE_STANDARD = gnu11; 458 | GCC_DYNAMIC_NO_PIC = NO; 459 | GCC_NO_COMMON_BLOCKS = YES; 460 | GCC_OPTIMIZATION_LEVEL = 0; 461 | GCC_PREPROCESSOR_DEFINITIONS = ( 462 | "DEBUG=1", 463 | "$(inherited)", 464 | ); 465 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 466 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 467 | GCC_WARN_UNDECLARED_SELECTOR = YES; 468 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 469 | GCC_WARN_UNUSED_FUNCTION = YES; 470 | GCC_WARN_UNUSED_VARIABLE = YES; 471 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 472 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 473 | MTL_FAST_MATH = YES; 474 | ONLY_ACTIVE_ARCH = YES; 475 | SDKROOT = iphoneos; 476 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 477 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 478 | }; 479 | name = Debug; 480 | }; 481 | 7E937B3025F9A0360066CA84 /* Release */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ALWAYS_SEARCH_USER_PATHS = NO; 485 | CLANG_ANALYZER_NONNULL = YES; 486 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 487 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 488 | CLANG_CXX_LIBRARY = "libc++"; 489 | CLANG_ENABLE_MODULES = YES; 490 | CLANG_ENABLE_OBJC_ARC = YES; 491 | CLANG_ENABLE_OBJC_WEAK = YES; 492 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 493 | CLANG_WARN_BOOL_CONVERSION = YES; 494 | CLANG_WARN_COMMA = YES; 495 | CLANG_WARN_CONSTANT_CONVERSION = YES; 496 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 497 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 498 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 499 | CLANG_WARN_EMPTY_BODY = YES; 500 | CLANG_WARN_ENUM_CONVERSION = YES; 501 | CLANG_WARN_INFINITE_RECURSION = YES; 502 | CLANG_WARN_INT_CONVERSION = YES; 503 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 504 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 505 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 506 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 507 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 508 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 509 | CLANG_WARN_STRICT_PROTOTYPES = YES; 510 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 511 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 512 | CLANG_WARN_UNREACHABLE_CODE = YES; 513 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 514 | COPY_PHASE_STRIP = NO; 515 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 516 | ENABLE_NS_ASSERTIONS = NO; 517 | ENABLE_STRICT_OBJC_MSGSEND = YES; 518 | GCC_C_LANGUAGE_STANDARD = gnu11; 519 | GCC_NO_COMMON_BLOCKS = YES; 520 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 521 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 522 | GCC_WARN_UNDECLARED_SELECTOR = YES; 523 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 524 | GCC_WARN_UNUSED_FUNCTION = YES; 525 | GCC_WARN_UNUSED_VARIABLE = YES; 526 | IPHONEOS_DEPLOYMENT_TARGET = 14.4; 527 | MTL_ENABLE_DEBUG_INFO = NO; 528 | MTL_FAST_MATH = YES; 529 | SDKROOT = iphoneos; 530 | SWIFT_COMPILATION_MODE = wholemodule; 531 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 532 | VALIDATE_PRODUCT = YES; 533 | }; 534 | name = Release; 535 | }; 536 | 7E937B3225F9A0360066CA84 /* Debug */ = { 537 | isa = XCBuildConfiguration; 538 | buildSettings = { 539 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 540 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 541 | CODE_SIGN_STYLE = Automatic; 542 | DEVELOPMENT_ASSET_PATHS = "\"SolanaDemoApp/Preview Content\""; 543 | DEVELOPMENT_TEAM = 634BMM2SZM; 544 | ENABLE_PREVIEWS = YES; 545 | INFOPLIST_FILE = SolanaDemoApp/Info.plist; 546 | LD_RUNPATH_SEARCH_PATHS = ( 547 | "$(inherited)", 548 | "@executable_path/Frameworks", 549 | ); 550 | PRODUCT_BUNDLE_IDENTIFIER = com.genecrucean.SolanaDemoApp; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | SWIFT_VERSION = 5.0; 553 | TARGETED_DEVICE_FAMILY = "1,2"; 554 | }; 555 | name = Debug; 556 | }; 557 | 7E937B3325F9A0360066CA84 /* Release */ = { 558 | isa = XCBuildConfiguration; 559 | buildSettings = { 560 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 561 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 562 | CODE_SIGN_STYLE = Automatic; 563 | DEVELOPMENT_ASSET_PATHS = "\"SolanaDemoApp/Preview Content\""; 564 | DEVELOPMENT_TEAM = 634BMM2SZM; 565 | ENABLE_PREVIEWS = YES; 566 | INFOPLIST_FILE = SolanaDemoApp/Info.plist; 567 | LD_RUNPATH_SEARCH_PATHS = ( 568 | "$(inherited)", 569 | "@executable_path/Frameworks", 570 | ); 571 | PRODUCT_BUNDLE_IDENTIFIER = com.genecrucean.SolanaDemoApp; 572 | PRODUCT_NAME = "$(TARGET_NAME)"; 573 | SWIFT_VERSION = 5.0; 574 | TARGETED_DEVICE_FAMILY = "1,2"; 575 | }; 576 | name = Release; 577 | }; 578 | /* End XCBuildConfiguration section */ 579 | 580 | /* Begin XCConfigurationList section */ 581 | 7E937B1825F9A0330066CA84 /* Build configuration list for PBXProject "SolanaDemoApp" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 7E937B2F25F9A0360066CA84 /* Debug */, 585 | 7E937B3025F9A0360066CA84 /* Release */, 586 | ); 587 | defaultConfigurationIsVisible = 0; 588 | defaultConfigurationName = Release; 589 | }; 590 | 7E937B3125F9A0360066CA84 /* Build configuration list for PBXNativeTarget "SolanaDemoApp" */ = { 591 | isa = XCConfigurationList; 592 | buildConfigurations = ( 593 | 7E937B3225F9A0360066CA84 /* Debug */, 594 | 7E937B3325F9A0360066CA84 /* Release */, 595 | ); 596 | defaultConfigurationIsVisible = 0; 597 | defaultConfigurationName = Release; 598 | }; 599 | /* End XCConfigurationList section */ 600 | 601 | /* Begin XCRemoteSwiftPackageReference section */ 602 | 7E6DAE992627256700A6F118 /* XCRemoteSwiftPackageReference "Base58Swift" */ = { 603 | isa = XCRemoteSwiftPackageReference; 604 | repositoryURL = "https://github.com/keefertaylor/Base58Swift"; 605 | requirement = { 606 | kind = upToNextMajorVersion; 607 | minimumVersion = 2.1.14; 608 | }; 609 | }; 610 | 7E6DAE9D2627259500A6F118 /* XCRemoteSwiftPackageReference "ed25519swift" */ = { 611 | isa = XCRemoteSwiftPackageReference; 612 | repositoryURL = "https://github.com/crewshin/ed25519swift"; 613 | requirement = { 614 | kind = upToNextMajorVersion; 615 | minimumVersion = 1.2.6; 616 | }; 617 | }; 618 | 7EF92558266BE068008E46D1 /* XCRemoteSwiftPackageReference "CodeEditor" */ = { 619 | isa = XCRemoteSwiftPackageReference; 620 | repositoryURL = "https://github.com/ZeeZide/CodeEditor.git"; 621 | requirement = { 622 | kind = upToNextMajorVersion; 623 | minimumVersion = 1.2.0; 624 | }; 625 | }; 626 | /* End XCRemoteSwiftPackageReference section */ 627 | 628 | /* Begin XCSwiftPackageProductDependency section */ 629 | 7E6DAE9A2627256700A6F118 /* Base58Swift */ = { 630 | isa = XCSwiftPackageProductDependency; 631 | package = 7E6DAE992627256700A6F118 /* XCRemoteSwiftPackageReference "Base58Swift" */; 632 | productName = Base58Swift; 633 | }; 634 | 7E6DAE9E2627259500A6F118 /* ed25519swift */ = { 635 | isa = XCSwiftPackageProductDependency; 636 | package = 7E6DAE9D2627259500A6F118 /* XCRemoteSwiftPackageReference "ed25519swift" */; 637 | productName = ed25519swift; 638 | }; 639 | 7EF92559266BE068008E46D1 /* CodeEditor */ = { 640 | isa = XCSwiftPackageProductDependency; 641 | package = 7EF92558266BE068008E46D1 /* XCRemoteSwiftPackageReference "CodeEditor" */; 642 | productName = CodeEditor; 643 | }; 644 | /* End XCSwiftPackageProductDependency section */ 645 | }; 646 | rootObject = 7E937B1525F9A0330066CA84 /* Project object */; 647 | } 648 | -------------------------------------------------------------------------------- /SolanaDemoApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SolanaDemoApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SolanaDemoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Base58Swift", 6 | "repositoryURL": "https://github.com/keefertaylor/Base58Swift", 7 | "state": { 8 | "branch": null, 9 | "revision": "1c13ea6b07f1584660526f8bde3d4cc150e91acd", 10 | "version": "2.1.14" 11 | } 12 | }, 13 | { 14 | "package": "BigInt", 15 | "repositoryURL": "https://github.com/attaswift/BigInt.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "889a1ecacd73ccc189c5cb29288048f186c44ed9", 19 | "version": "5.2.1" 20 | } 21 | }, 22 | { 23 | "package": "CodeEditor", 24 | "repositoryURL": "https://github.com/ZeeZide/CodeEditor.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "5856fac22b0a2174dbdea212784567c8c9cd1129", 28 | "version": "1.2.0" 29 | } 30 | }, 31 | { 32 | "package": "CryptoSwift", 33 | "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "5669f222e46c8134fb1f399c745fa6882b43532e", 37 | "version": "1.3.8" 38 | } 39 | }, 40 | { 41 | "package": "ed25519swift", 42 | "repositoryURL": "https://github.com/crewshin/ed25519swift", 43 | "state": { 44 | "branch": null, 45 | "revision": "b14719cad9fa541a5ec9b3de5aff8e9c4cc6ea07", 46 | "version": "1.2.6" 47 | } 48 | }, 49 | { 50 | "package": "Highlightr", 51 | "repositoryURL": "https://github.com/raspu/Highlightr", 52 | "state": { 53 | "branch": null, 54 | "revision": "93199b9e434f04bda956a613af8f571933f9f037", 55 | "version": "2.1.2" 56 | } 57 | } 58 | ] 59 | }, 60 | "version": 1 61 | } 62 | -------------------------------------------------------------------------------- /SolanaDemoApp.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /SolanaDemoApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | // Override point for customization after application launch. 15 | return true 16 | } 17 | 18 | // MARK: UISceneSession Lifecycle 19 | 20 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 21 | // Called when a new scene session is being created. 22 | // Use this method to select a configuration to create the new scene with. 23 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 24 | } 25 | 26 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 27 | // Called when the user discards a scene session. 28 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 29 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 30 | } 31 | 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "solana_icon-20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "solana_icon-20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "solana_icon-29@2x.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "solana_icon-29@3x.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "solana_icon-40@2x.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "solana_icon-40@3x.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "solana_icon-60@2x.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "solana_icon-60@3x.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "solana_icon-20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "solana_icon-20@2x.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "solana_icon-29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "solana_icon-29@2x.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "solana_icon-40.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "solana_icon-40@2x.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "solana_icon-76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "solana_icon-76@2x.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "solana_icon-83.5@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "solana_icon-1024.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | } 111 | ], 112 | "info" : { 113 | "author" : "xcode", 114 | "version" : 1 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-1024.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-20.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-20@2x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-20@3x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-29.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-29@2x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-29@3x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-40.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-40@2x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-40@3x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-60@2x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-60@3x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-76.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-76@2x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewshin/solana-swift/61a53dee2a371127bb1998a62a5ec9aa9c419acb/SolanaDemoApp/Assets.xcassets/AppIcon.appiconset/solana_icon-83.5@2x.png -------------------------------------------------------------------------------- /SolanaDemoApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SolanaDemoApp/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SolanaDemoApp/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | 12 | @State var solana = Solana(network: .dev) 13 | @State var solanaSocket = SolanaSockets(network: .dev) 14 | @State private var networkSelection: Int = 1 15 | @State private var customNetworkIsSelected = false 16 | @State private var currentNetwork = Network.dev.url?.absoluteString ?? "" 17 | 18 | @EnvironmentObject var output: Output 19 | 20 | @State var pubkey: String = "9B5XszUGdMaxCZ7uSQhPzdks5ZQSmWxrmzCSvtJ6Ns6g" 21 | @State var toggleModal = false 22 | @State var toggleModalHidden = false 23 | @State var isViewingSocketData = false 24 | 25 | var body: some View { 26 | Form { 27 | 28 | Section(header: Text("Network")) { 29 | Picker(selection: $networkSelection, label: Text("Picker"), content: { 30 | Text("Dev").tag(1) 31 | Text("Main").tag(2) 32 | Text("Test").tag(3) 33 | Text("Custom").tag(4) 34 | }) 35 | .pickerStyle(SegmentedPickerStyle()) 36 | .onChange(of: networkSelection, perform: { value in 37 | setNetwork() 38 | }) 39 | 40 | TextField("http://localhost:8899", text: $currentNetwork) 41 | .onChange(of: currentNetwork, perform: { value in 42 | setNetwork() 43 | }) 44 | .disabled(!customNetworkIsSelected) 45 | .foregroundColor(customNetworkIsSelected ? .white : .gray) 46 | 47 | } 48 | 49 | Section(header: Text("Pubkey")) { 50 | TextField("PubKey", 51 | text: $pubkey 52 | ) 53 | .frame(minWidth: 0, idealWidth: 375, maxWidth: .infinity, minHeight: 40, idealHeight: 40, maxHeight: 200, alignment: .leading) 54 | } 55 | 56 | Section(header: Text("Network Calls")) { 57 | 58 | Group { 59 | Button("Get Account Info") { 60 | getAccountInfo() 61 | } 62 | 63 | Button("Get Balance") { 64 | getBalance() 65 | } 66 | 67 | Button("Get Block Commitment") { 68 | getBlockCommitment() 69 | } 70 | 71 | Button("Get Block Time") { 72 | getBlockTime() 73 | } 74 | 75 | Button("Get Cluster Nodes") { 76 | getClusterNodes() 77 | } 78 | 79 | Button("Get Confirmed Block") { 80 | getConfirmedBlock() 81 | } 82 | 83 | Button("Get Block Production") { 84 | getBlockProduction() 85 | } 86 | 87 | Button("Get Confirmed Blocks") { 88 | getConfirmedBlocks() 89 | } 90 | 91 | Button("Get Confirmed Blocks With Limit") { 92 | getConfirmedBlocksWithLimit() 93 | } 94 | 95 | Button("Get Confirmed Signatures For Address 2") { 96 | getConfirmedSignaturesForAddress2() 97 | } 98 | } 99 | 100 | Group { 101 | Button("Get Confirmed Transaction") { 102 | getConfirmedTransaction() 103 | } 104 | 105 | Button("Get Epoch Info") { 106 | getEpochInfo() 107 | } 108 | 109 | Button("Get Epoch Schedule") { 110 | getEpochSchedule() 111 | } 112 | 113 | Button("Get Fee Calculator For Blockhash") { 114 | getFeeCalculatorForBlockhash() 115 | } 116 | 117 | Button("Get Fee Rate Governor") { 118 | getFeeRateGovernor() 119 | } 120 | 121 | Button("Get Fees") { 122 | getFees() 123 | } 124 | } 125 | } 126 | 127 | Section(header: Text("Web Sockets")) { 128 | Group { 129 | Button("Start") { 130 | testWebSocket() 131 | } 132 | 133 | Button("Stop") { 134 | solanaSocket.testWebSocketDisconnect() 135 | } 136 | } 137 | } 138 | 139 | if isViewingSocketData { 140 | Section(header: Text("Output")) { 141 | TextEditor(text: $output.value) 142 | .font(.caption) 143 | .foregroundColor(.pink) 144 | } 145 | } 146 | } 147 | .sheet(isPresented: isViewingSocketData ? $toggleModalHidden : $toggleModal, 148 | onDismiss: dismissSheet, 149 | content: { 150 | ResultView(input: output.value) 151 | } 152 | ) 153 | } 154 | 155 | // MARK: - Result Sheet 156 | 157 | func dismissSheet() { 158 | toggleModal = false 159 | solanaSocket.testWebSocketDisconnect() 160 | } 161 | 162 | // MARK: - Calls 163 | 164 | func getAccountInfo() { 165 | solana.getAccountInfo(pubkey: pubkey) { (result) in 166 | isViewingSocketData = false 167 | 168 | switch result { 169 | case .failure(let error): 170 | if case let SolanaAPIError.getAccountInfoError(message) = error { 171 | DispatchQueue.main.async { 172 | output.value = message 173 | } 174 | toggleModal.toggle() 175 | } 176 | case .success(let response): 177 | DispatchQueue.main.async { 178 | output.value = "\(response.value.asDictionary ?? [:])" 179 | } 180 | toggleModal.toggle() 181 | } 182 | } 183 | } 184 | 185 | func getBalance() { 186 | solana.getBalance(pubkeys: [pubkey]) { (result) in 187 | isViewingSocketData = false 188 | 189 | switch result { 190 | case .failure(let error): 191 | if case let SolanaAPIError.getBalanceError(message) = error { 192 | DispatchQueue.main.async { 193 | output.value = message 194 | } 195 | toggleModal.toggle() 196 | } 197 | case .success(let response): 198 | DispatchQueue.main.async { 199 | output.value = "\(response.value.asDictionary ?? [:])" 200 | } 201 | toggleModal.toggle() 202 | } 203 | } 204 | } 205 | 206 | func getBlockCommitment() { 207 | solana.getBlockCommitment(blocks: [41901216]) { (result) in 208 | isViewingSocketData = false 209 | 210 | switch result { 211 | case .failure(let error): 212 | if case let SolanaAPIError.getBlockCommitmentError(message) = error { 213 | DispatchQueue.main.async { 214 | output.value = message 215 | } 216 | toggleModal.toggle() 217 | } 218 | case .success(let response): 219 | DispatchQueue.main.async { 220 | output.value = "\(response.value.asDictionary ?? [:])" 221 | } 222 | toggleModal.toggle() 223 | } 224 | } 225 | } 226 | 227 | func getBlockTime() { 228 | solana.getBlockTime(blocks: [41901216]) { (result) in 229 | isViewingSocketData = false 230 | 231 | switch result { 232 | case .failure(let error): 233 | if case let SolanaAPIError.getBlockTimeError(message) = error { 234 | DispatchQueue.main.async { 235 | output.value = message 236 | } 237 | toggleModal.toggle() 238 | } 239 | case .success(let response): 240 | DispatchQueue.main.async { 241 | output.value = "\(response.value.asDictionary ?? [:])" 242 | } 243 | toggleModal.toggle() 244 | } 245 | } 246 | } 247 | 248 | func getClusterNodes() { 249 | solana.getClusterNodes() { (result) in 250 | isViewingSocketData = false 251 | 252 | switch result { 253 | case .failure(let error): 254 | if case let SolanaAPIError.getClusterNodesError(message) = error { 255 | DispatchQueue.main.async { 256 | output.value = message 257 | } 258 | toggleModal.toggle() 259 | } 260 | case .success(let response): 261 | DispatchQueue.main.async { 262 | output.value = "\(response.value.asDictionary ?? [:])" 263 | } 264 | toggleModal.toggle() 265 | } 266 | } 267 | } 268 | 269 | func getConfirmedBlock() { 270 | solana.getConfirmedBlock(slot: 48441492) { (result) in 271 | isViewingSocketData = false 272 | 273 | switch result { 274 | case .failure(let error): 275 | if case let SolanaAPIError.getConfirmedBlockError(message) = error { 276 | DispatchQueue.main.async { 277 | output.value = message 278 | } 279 | toggleModal.toggle() 280 | } 281 | case .success(let response): 282 | DispatchQueue.main.async { 283 | output.value = "\(response.value.asDictionary ?? [:])" 284 | } 285 | toggleModal.toggle() 286 | } 287 | } 288 | } 289 | 290 | func getBlockProduction() { 291 | solana.getBlockProduction(commitment: .finalized, slotRange: SlotRange(firstSlot: 55579597, lastSlot: 55579600), identity: nil) { (result) in 292 | isViewingSocketData = false 293 | 294 | switch result { 295 | case .failure(let error): 296 | if case let SolanaAPIError.getBlockProductionError(message) = error { 297 | DispatchQueue.main.async { 298 | output.value = message 299 | } 300 | toggleModal.toggle() 301 | } 302 | case .success(let response): 303 | DispatchQueue.main.async { 304 | output.value = "\(response.value.asDictionary ?? [:])" 305 | } 306 | toggleModal.toggle() 307 | } 308 | } 309 | } 310 | 311 | func getConfirmedBlocks() { 312 | solana.getConfirmedBlocks(startSlot: 5, endSlot: 20) { (result) in 313 | isViewingSocketData = false 314 | 315 | switch result { 316 | case .failure(let error): 317 | if case let SolanaAPIError.getConfirmedBlocksError(message) = error { 318 | DispatchQueue.main.async { 319 | output.value = message 320 | } 321 | toggleModal.toggle() 322 | } 323 | case .success(let response): 324 | DispatchQueue.main.async { 325 | output.value = "\(response.value.asDictionary ?? [:])" 326 | } 327 | toggleModal.toggle() 328 | } 329 | } 330 | } 331 | 332 | func getConfirmedBlocksWithLimit() { 333 | solana.getConfirmedBlocksWithLimit(startSlot: 1, limit: 5) { (result) in 334 | isViewingSocketData = false 335 | 336 | switch result { 337 | case .failure(let error): 338 | if case let SolanaAPIError.getConfirmedBlocksWithLimitError(message) = error { 339 | DispatchQueue.main.async { 340 | output.value = message 341 | } 342 | toggleModal.toggle() 343 | } 344 | case .success(let response): 345 | DispatchQueue.main.async { 346 | output.value = "\(response.value.asDictionary ?? [:])" 347 | } 348 | toggleModal.toggle() 349 | } 350 | } 351 | } 352 | 353 | // Need to confirm success response. 354 | func getConfirmedSignaturesForAddress2() { 355 | solana.getConfirmedSignaturesForAddress2(address: pubkey, config: nil) { (result) in 356 | isViewingSocketData = false 357 | 358 | switch result { 359 | case .failure(let error): 360 | if case let SolanaAPIError.getConfirmedSignaturesForAddress2Error(message) = error { 361 | DispatchQueue.main.async { 362 | output.value = message 363 | } 364 | toggleModal.toggle() 365 | } 366 | case .success(let response): 367 | DispatchQueue.main.async { 368 | output.value = "\(response.value.asDictionary ?? [:])" 369 | } 370 | toggleModal.toggle() 371 | } 372 | } 373 | } 374 | 375 | func getConfirmedTransaction() { 376 | solana.getConfirmedTransaction(transaction: "5oxHqnr7cNn9kZGfnV7mvZWNB4hp5vgcmPi8jtcaEY9PjohueEpNB5xC14eqVzLUoooE1rctSc91mFhnigpvDt5V") { (result) in 377 | isViewingSocketData = false 378 | 379 | switch result { 380 | case .failure(let error): 381 | if case let SolanaAPIError.getConfirmedTransactionError(message) = error { 382 | DispatchQueue.main.async { 383 | output.value = message 384 | } 385 | toggleModal.toggle() 386 | } 387 | case .success(let response): 388 | DispatchQueue.main.async { 389 | output.value = "\(response.value.asDictionary ?? [:])" 390 | } 391 | toggleModal.toggle() 392 | } 393 | } 394 | } 395 | 396 | func getEpochInfo() { 397 | solana.getEpochInfo(commitment: .finalized) { (result) in 398 | isViewingSocketData = false 399 | 400 | switch result { 401 | case .failure(let error): 402 | if case let SolanaAPIError.getEpochInfoError(message) = error { 403 | DispatchQueue.main.async { 404 | output.value = message 405 | } 406 | toggleModal.toggle() 407 | } 408 | case .success(let response): 409 | DispatchQueue.main.async { 410 | output.value = "\(response.value.asDictionary ?? [:])" 411 | } 412 | toggleModal.toggle() 413 | } 414 | } 415 | } 416 | 417 | func getEpochSchedule() { 418 | solana.getEpochSchedule() { (result) in 419 | isViewingSocketData = false 420 | 421 | switch result { 422 | case .failure(let error): 423 | if case let SolanaAPIError.getEpochScheduleError(message) = error { 424 | DispatchQueue.main.async { 425 | output.value = message 426 | } 427 | toggleModal.toggle() 428 | } 429 | case .success(let response): 430 | DispatchQueue.main.async { 431 | output.value = "\(response.value.asDictionary ?? [:])" 432 | } 433 | toggleModal.toggle() 434 | } 435 | } 436 | } 437 | 438 | func getFeeCalculatorForBlockhash() { 439 | solana.getFeeCalculatorFor(blockhash: "6bERxmpFyJrdKWneWMyUWTvZx563qssYjGQn4S2sXWMY") { (result) in 440 | isViewingSocketData = false 441 | 442 | switch result { 443 | case .failure(let error): 444 | if case let SolanaAPIError.getFeeCalculatorForBlockhashError(message) = error { 445 | DispatchQueue.main.async { 446 | output.value = message 447 | } 448 | toggleModal.toggle() 449 | } 450 | case .success(let response): 451 | DispatchQueue.main.async { 452 | output.value = "\(response.value.asDictionary ?? [:])" 453 | } 454 | toggleModal.toggle() 455 | } 456 | } 457 | } 458 | 459 | func getFeeRateGovernor() { 460 | solana.getFeeRateGovernor() { (result) in 461 | isViewingSocketData = false 462 | 463 | switch result { 464 | case .failure(let error): 465 | if case let SolanaAPIError.getFeeRateGovernorError(message) = error { 466 | DispatchQueue.main.async { 467 | output.value = message 468 | } 469 | toggleModal.toggle() 470 | } 471 | case .success(let response): 472 | DispatchQueue.main.async { 473 | output.value = "\(response.value.asDictionary ?? [:])" 474 | } 475 | toggleModal.toggle() 476 | } 477 | } 478 | } 479 | 480 | func getFees() { 481 | solana.getFees(commitment: .finalized) { (result) in 482 | isViewingSocketData = false 483 | 484 | switch result { 485 | case .failure(let error): 486 | if case let SolanaAPIError.getFeesError(message) = error { 487 | DispatchQueue.main.async { 488 | output.value = message 489 | } 490 | toggleModal.toggle() 491 | } 492 | case .success(let response): 493 | DispatchQueue.main.async { 494 | output.value = "\(response.value.asDictionary ?? [:])" 495 | } 496 | toggleModal.toggle() 497 | } 498 | } 499 | } 500 | 501 | 502 | // MARK: - WebSockets 503 | 504 | func testWebSocket() { 505 | solanaSocket.delegate = self 506 | isViewingSocketData.toggle() 507 | solanaSocket.testWebSocket(request: nil) 508 | } 509 | 510 | // MARK: - Misc demo app helpers 511 | 512 | func setNetwork() { 513 | switch networkSelection { 514 | case 1: 515 | solana = Solana(network: .dev) 516 | solanaSocket = SolanaSockets(network: .dev) 517 | customNetworkIsSelected = false 518 | currentNetwork = Network.dev.url?.absoluteString ?? "" 519 | case 2: 520 | solana = Solana(network: .main) 521 | solanaSocket = SolanaSockets(network: .main) 522 | customNetworkIsSelected = false 523 | currentNetwork = Network.main.url?.absoluteString ?? "" 524 | case 3: 525 | solana = Solana(network: .test) 526 | solanaSocket = SolanaSockets(network: .test) 527 | customNetworkIsSelected = false 528 | currentNetwork = Network.test.url?.absoluteString ?? "" 529 | case 4: 530 | solana = Solana(network: .custom(network: currentNetwork)) 531 | solanaSocket = SolanaSockets(network: .custom(network: currentNetwork)) 532 | customNetworkIsSelected = true 533 | currentNetwork = "http://{ip-to-solana-cluster}:8899" 534 | default: 535 | solana = Solana(network: .dev) 536 | solanaSocket = SolanaSockets(network: .dev) 537 | customNetworkIsSelected = false 538 | currentNetwork = Network.dev.url?.absoluteString ?? "" 539 | } 540 | } 541 | } 542 | 543 | struct ContentView_Previews: PreviewProvider { 544 | static var previews: some View { 545 | ContentView( 546 | pubkey: "9B5XszUGdMaxCZ7uSQhPzdks5ZQSmWxrmzCSvtJ6Ns6g" 547 | ) 548 | } 549 | } 550 | 551 | extension ContentView: SolanaDelegate { 552 | func receivedMessage(message: T) where T : Decodable, T : Encodable { 553 | if let message = message as? String { 554 | DispatchQueue.main.async { 555 | output.value = message 556 | } 557 | } 558 | } 559 | 560 | func disconnected() {} 561 | } 562 | -------------------------------------------------------------------------------- /SolanaDemoApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UIApplicationSupportsIndirectInputEvents 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /SolanaDemoApp/Output.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | class Output: ObservableObject { 8 | @Published var value: String = "" 9 | } 10 | -------------------------------------------------------------------------------- /SolanaDemoApp/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /SolanaDemoApp/ResultView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResultView.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/12/21. 6 | // 7 | 8 | import SwiftUI 9 | import CodeEditor 10 | 11 | struct ResultView: View { 12 | 13 | @State var input: String 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | var body: some View { 17 | CodeEditor(source: $input, language: .javascript, theme: colorScheme == .dark ? .agate : .atelierSavannaDark) 18 | .font(.caption) 19 | .padding(EdgeInsets(top: 4, leading: 8, bottom: 4, trailing:8)) 20 | } 21 | } 22 | 23 | struct ResultView_Previews: PreviewProvider { 24 | 25 | static let inputText = """ 26 | ["result": { 27 | context = { 28 | slot = 60671161; 29 | }; 30 | value = { 31 | blockhash = 8fvJiYGR4WCfBW2BhoeV2sE6hwDwmjiDRqMQWtdmm4fe; 32 | feeCalculator = { 33 | lamportsPerSignature = 5000; 34 | }; 35 | lastValidBlockHeight = 60128530; 36 | lastValidSlot = 60671461; 37 | }; 38 | }, "jsonrpc": 2.0, "id": 1] 39 | """ 40 | 41 | static var previews: some View { 42 | ResultView(input: inputText) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SolanaDemoApp/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var window: UIWindow? 14 | var output = Output() 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 18 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 19 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 20 | 21 | // Create the SwiftUI view that provides the window contents. 22 | let contentView = ContentView( 23 | pubkey: "" 24 | ) 25 | 26 | // Use a UIHostingController as window root view controller. 27 | if let windowScene = scene as? UIWindowScene { 28 | let window = UIWindow(windowScene: windowScene) 29 | window.rootViewController = UIHostingController( 30 | rootView: contentView.environmentObject(output) 31 | ) 32 | 33 | self.window = window 34 | window.makeKeyAndVisible() 35 | } 36 | } 37 | 38 | func sceneDidDisconnect(_ scene: UIScene) { 39 | // Called as the scene is being released by the system. 40 | // This occurs shortly after the scene enters the background, or when its session is discarded. 41 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 42 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 43 | } 44 | 45 | func sceneDidBecomeActive(_ scene: UIScene) { 46 | // Called when the scene has moved from an inactive state to an active state. 47 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 48 | } 49 | 50 | func sceneWillResignActive(_ scene: UIScene) { 51 | // Called when the scene will move from an active state to an inactive state. 52 | // This may occur due to temporary interruptions (ex. an incoming phone call). 53 | } 54 | 55 | func sceneWillEnterForeground(_ scene: UIScene) { 56 | // Called as the scene transitions from the background to the foreground. 57 | // Use this method to undo the changes made on entering the background. 58 | } 59 | 60 | func sceneDidEnterBackground(_ scene: UIScene) { 61 | // Called as the scene transitions from the foreground to the background. 62 | // Use this method to save data, release shared resources, and store enough scene-specific state information 63 | // to restore the scene back to its current state. 64 | } 65 | 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /SolanaJsonRPC.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "ba4f358b-9fcf-4713-8635-125a35de5218", 4 | "name": "Solana", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "WebSockets", 10 | "item": [ 11 | { 12 | "name": "Logs Subscribe", 13 | "request": { 14 | "method": "POST", 15 | "header": [ 16 | { 17 | "key": "Content-Type", 18 | "value": "application/json", 19 | "type": "text" 20 | } 21 | ], 22 | "body": { 23 | "mode": "raw", 24 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"logsSubscribe\",\n \"params\": [\n {\n \"mentions\": [ \"11111111111111111111111111111111\" ]\n },\n {\n \"commitment\": \"finalized\"\n }\n ]\n}" 25 | }, 26 | "url": { 27 | "raw": "http://api.mainnet-beta.solana.com", 28 | "protocol": "http", 29 | "host": [ 30 | "api", 31 | "mainnet-beta", 32 | "solana", 33 | "com" 34 | ] 35 | } 36 | }, 37 | "response": [] 38 | } 39 | ] 40 | }, 41 | { 42 | "name": "Get Account Info", 43 | "request": { 44 | "method": "POST", 45 | "header": [ 46 | { 47 | "key": "Content-Type", 48 | "value": "application/json", 49 | "type": "text" 50 | } 51 | ], 52 | "body": { 53 | "mode": "raw", 54 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getAccountInfo\",\n \"params\": [\n \"{{pubkey}}\",\n {\n \"encoding\": \"base58\"\n }\n ]\n}" 55 | }, 56 | "url": { 57 | "raw": "{{hostname}}", 58 | "host": [ 59 | "{{hostname}}" 60 | ] 61 | } 62 | }, 63 | "response": [] 64 | }, 65 | { 66 | "name": "Get Balance", 67 | "request": { 68 | "method": "POST", 69 | "header": [ 70 | { 71 | "key": "Content-Type", 72 | "value": "application/json", 73 | "type": "text" 74 | } 75 | ], 76 | "body": { 77 | "mode": "raw", 78 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getBalance\",\n \"params\": [\"{{pubkey}}\"]\n}" 79 | }, 80 | "url": { 81 | "raw": "{{hostname}}", 82 | "host": [ 83 | "{{hostname}}" 84 | ] 85 | } 86 | }, 87 | "response": [] 88 | }, 89 | { 90 | "name": "Get Block Commitment", 91 | "request": { 92 | "method": "POST", 93 | "header": [ 94 | { 95 | "key": "Content-Type", 96 | "value": "application/json", 97 | "type": "text" 98 | } 99 | ], 100 | "body": { 101 | "mode": "raw", 102 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getBlockCommitment\",\n \"params\": [41901216]\n}" 103 | }, 104 | "url": { 105 | "raw": "{{hostname}}", 106 | "host": [ 107 | "{{hostname}}" 108 | ] 109 | } 110 | }, 111 | "response": [] 112 | }, 113 | { 114 | "name": "Get Block Time", 115 | "request": { 116 | "method": "POST", 117 | "header": [ 118 | { 119 | "key": "Content-Type", 120 | "value": "application/json", 121 | "type": "text" 122 | } 123 | ], 124 | "body": { 125 | "mode": "raw", 126 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getBlockTime\",\n \"params\": [41901216]\n}" 127 | }, 128 | "url": { 129 | "raw": "{{hostname}}", 130 | "host": [ 131 | "{{hostname}}" 132 | ] 133 | } 134 | }, 135 | "response": [] 136 | }, 137 | { 138 | "name": "Get Cluster Nodes", 139 | "request": { 140 | "method": "POST", 141 | "header": [ 142 | { 143 | "key": "Content-Type", 144 | "value": "application/json", 145 | "type": "text" 146 | } 147 | ], 148 | "body": { 149 | "mode": "raw", 150 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getClusterNodes\"\n}" 151 | }, 152 | "url": { 153 | "raw": "{{hostname}}", 154 | "host": [ 155 | "{{hostname}}" 156 | ] 157 | } 158 | }, 159 | "response": [] 160 | }, 161 | { 162 | "name": "Get Confirmed Block - mainnet", 163 | "request": { 164 | "method": "POST", 165 | "header": [ 166 | { 167 | "key": "Content-Type", 168 | "value": "application/json", 169 | "type": "text" 170 | } 171 | ], 172 | "body": { 173 | "mode": "raw", 174 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getConfirmedBlock\",\n \"params\": [430, \"json\"]\n}" 175 | }, 176 | "url": { 177 | "raw": "{{hostname}}", 178 | "host": [ 179 | "{{hostname}}" 180 | ] 181 | } 182 | }, 183 | "response": [] 184 | }, 185 | { 186 | "name": "Get Confirmed Blocks - mainnet", 187 | "request": { 188 | "method": "POST", 189 | "header": [ 190 | { 191 | "key": "Content-Type", 192 | "value": "application/json", 193 | "type": "text" 194 | } 195 | ], 196 | "body": { 197 | "mode": "raw", 198 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getConfirmedBlocks\",\n \"params\": [5, 50]\n}" 199 | }, 200 | "url": { 201 | "raw": "{{hostname}}", 202 | "host": [ 203 | "{{hostname}}" 204 | ] 205 | } 206 | }, 207 | "response": [] 208 | }, 209 | { 210 | "name": "Get Confirmed Blocks With Limit - mainnet", 211 | "request": { 212 | "method": "POST", 213 | "header": [ 214 | { 215 | "key": "Content-Type", 216 | "value": "application/json", 217 | "type": "text" 218 | } 219 | ], 220 | "body": { 221 | "mode": "raw", 222 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getConfirmedBlocks\",\n \"params\": [1, 10]\n}" 223 | }, 224 | "url": { 225 | "raw": "{{hostname}}", 226 | "host": [ 227 | "{{hostname}}" 228 | ] 229 | } 230 | }, 231 | "response": [] 232 | }, 233 | { 234 | "name": "Get Confirmed Blocks With Limit Copy - mainnet", 235 | "request": { 236 | "method": "POST", 237 | "header": [ 238 | { 239 | "key": "Content-Type", 240 | "value": "application/json", 241 | "type": "text" 242 | } 243 | ], 244 | "body": { 245 | "mode": "raw", 246 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getConfirmedBlocks\",\n \"params\": [1, 10]\n}" 247 | }, 248 | "url": { 249 | "raw": "{{hostname}}", 250 | "host": [ 251 | "{{hostname}}" 252 | ] 253 | } 254 | }, 255 | "response": [] 256 | }, 257 | { 258 | "name": "Get Confirmed Signatures For Address - mainnet", 259 | "request": { 260 | "method": "POST", 261 | "header": [ 262 | { 263 | "key": "Content-Type", 264 | "value": "application/json", 265 | "type": "text" 266 | } 267 | ], 268 | "body": { 269 | "mode": "raw", 270 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getConfirmedSignaturesForAddress\",\n \"params\": [\n \"{{pubkey}}\",\n 1,\n 5\n ]\n}" 271 | }, 272 | "url": { 273 | "raw": "{{hostname}}", 274 | "host": [ 275 | "{{hostname}}" 276 | ] 277 | } 278 | }, 279 | "response": [] 280 | }, 281 | { 282 | "name": "Get Confirmed Signatures For Address 2 - mainnet", 283 | "request": { 284 | "method": "POST", 285 | "header": [ 286 | { 287 | "key": "Content-Type", 288 | "value": "application/json", 289 | "type": "text" 290 | } 291 | ], 292 | "body": { 293 | "mode": "raw", 294 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getConfirmedSignaturesForAddress2\",\n \"params\": [\n \"{{pubkey}}\",\n {\n \"limit\": 5\n }\n ]\n}" 295 | }, 296 | "url": { 297 | "raw": "{{hostname}}", 298 | "host": [ 299 | "{{hostname}}" 300 | ] 301 | } 302 | }, 303 | "response": [] 304 | }, 305 | { 306 | "name": "Get Confirmed Transaction", 307 | "request": { 308 | "method": "POST", 309 | "header": [ 310 | { 311 | "key": "Content-Type", 312 | "value": "application/json", 313 | "type": "text" 314 | } 315 | ], 316 | "body": { 317 | "mode": "raw", 318 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getConfirmedTransaction\",\n \"params\": [\n \"3gsewyaWbZAPCgJoa8En6pWs2V7XrW8gn8TixLzJz8QMFtu8uPyuppGdg6G3d53LaxLcayfUpf44bmc1qiaqLjL2\",\n \"json\"\n ]\n}" 319 | }, 320 | "url": { 321 | "raw": "{{hostname}}", 322 | "host": [ 323 | "{{hostname}}" 324 | ] 325 | } 326 | }, 327 | "response": [] 328 | }, 329 | { 330 | "name": "Get Epoch Schedule", 331 | "request": { 332 | "method": "POST", 333 | "header": [ 334 | { 335 | "key": "Content-Type", 336 | "value": "application/json", 337 | "type": "text" 338 | } 339 | ], 340 | "body": { 341 | "mode": "raw", 342 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getEpochSchedule\",\n \"params\": []\n}" 343 | }, 344 | "url": { 345 | "raw": "{{hostname}}", 346 | "host": [ 347 | "{{hostname}}" 348 | ] 349 | } 350 | }, 351 | "response": [] 352 | }, 353 | { 354 | "name": "Get Fee Calculator For Blockhash", 355 | "request": { 356 | "method": "POST", 357 | "header": [ 358 | { 359 | "key": "Content-Type", 360 | "value": "application/json", 361 | "type": "text" 362 | } 363 | ], 364 | "body": { 365 | "mode": "raw", 366 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getFeeCalculatorForBlockhash\",\n \"params\": [\n \"6bERxmpFyJrdKWneWMyUWTvZx563qssYjGQn4S2sXWMY\"\n ]\n}" 367 | }, 368 | "url": { 369 | "raw": "{{hostname}}", 370 | "host": [ 371 | "{{hostname}}" 372 | ] 373 | } 374 | }, 375 | "response": [] 376 | }, 377 | { 378 | "name": "Get Fee Rate Governor", 379 | "request": { 380 | "method": "POST", 381 | "header": [ 382 | { 383 | "key": "Content-Type", 384 | "value": "application/json", 385 | "type": "text" 386 | } 387 | ], 388 | "body": { 389 | "mode": "raw", 390 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"getFeeRateGovernor\",\n \"params\": [\n \n ]\n}" 391 | }, 392 | "url": { 393 | "raw": "{{hostname}}", 394 | "host": [ 395 | "{{hostname}}" 396 | ] 397 | } 398 | }, 399 | "response": [] 400 | }, 401 | { 402 | "name": "Simulate Transaction", 403 | "request": { 404 | "method": "POST", 405 | "header": [ 406 | { 407 | "key": "Content-Type", 408 | "value": "application/json", 409 | "type": "text" 410 | } 411 | ], 412 | "body": { 413 | "mode": "raw", 414 | "raw": "{\n \"jsonrpc\": \"2.0\",\n \"id\": 1,\n \"method\": \"simulateTransaction\",\n \"params\": [\n \"21Utv9ZpbdGWmtWPprg5uU2WRTV4sYLr38XA32qLu46D\"\n ]\n}" 415 | }, 416 | "url": { 417 | "raw": "{{hostname}}", 418 | "host": [ 419 | "{{hostname}}" 420 | ] 421 | } 422 | }, 423 | "response": [] 424 | } 425 | ] 426 | } -------------------------------------------------------------------------------- /Sources/Solana/Extensions/Double+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double+.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Double { 11 | var fromLamport: Double { 12 | return self * 0.000000001 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Solana/Extensions/Int+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int+.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Int { 11 | var fromLamport: Double { 12 | return Double(self) * 0.000000001 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Solana/Extensions/JSON+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSON+.swift 3 | // 4 | 5 | import Foundation 6 | 7 | extension String { 8 | public func convertJsonStringToDict() -> [String: Any] { 9 | guard let stringData = self.data(using: .utf8) else { return [:] } 10 | 11 | do { 12 | guard let json = try JSONSerialization.jsonObject(with: stringData, options: []) as? [String: Any] else { return [:] } 13 | return json 14 | } catch { 15 | return [:] 16 | } 17 | } 18 | } 19 | 20 | extension Encodable { 21 | public var asDictionary: [String: Any]? { 22 | guard let data = try? JSONEncoder().encode(self) else { return nil } 23 | return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] } 24 | } 25 | } 26 | 27 | //extension Dictionary where Key == String, Value == String { 28 | extension Dictionary { 29 | public func convertDictToJsonString() -> String { 30 | do { 31 | let stringData = try JSONSerialization.data(withJSONObject: self, options: []) 32 | return String(data: stringData, encoding: .utf8) ?? "" 33 | } catch { 34 | return "" 35 | } 36 | } 37 | 38 | public func convertDictToJsonData() -> Data { 39 | do { 40 | return try JSONSerialization.data(withJSONObject: self, options: []) 41 | } catch { 42 | return Data() 43 | } 44 | } 45 | } 46 | 47 | extension Array { 48 | public func convertArrayToJsonString() -> String { 49 | do { 50 | let stringData = try JSONSerialization.data(withJSONObject: self, options: []) 51 | return String(data: stringData, encoding: .utf8) ?? "" 52 | } catch { 53 | return "" 54 | } 55 | } 56 | 57 | public func convertArrayToJsonData() -> Data { 58 | do { 59 | return try JSONSerialization.data(withJSONObject: self, options: []) 60 | } catch { 61 | return Data() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Solana/Extensions/String+.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+.swift 3 | // NetworkStackExamples 4 | // 5 | // Created by Gene Crucean on 1/9/21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension String { 11 | func toObject() throws -> T { 12 | let data = Data(self.utf8) 13 | return try JSONDecoder().decode(T.self, from: data) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Account.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Account.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | import ed25519swift 10 | 11 | public struct Account: Codable { 12 | public let publicKey: [UInt8] 13 | public let privateKey: [UInt8] 14 | 15 | public static func newAccount() -> Account { 16 | let keys = Ed25519.generateKeyPair() 17 | return Account(publicKey: keys.publicKey, privateKey: keys.secretKey) 18 | } 19 | 20 | public static func accountFromPrivateKey(privateKey: [UInt8]) -> Account { 21 | return Account( 22 | publicKey: Ed25519.calcPublicKey(secretKey: privateKey), 23 | privateKey: privateKey 24 | ) 25 | } 26 | } 27 | 28 | public struct AccountMeta: Codable { 29 | public let publicKey: [UInt8] 30 | public let isSigner: Bool 31 | public let isWritable: Bool 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Block.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | public typealias Block = UInt 8 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Commitment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Commitment.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Commitment: String, RawRepresentable, Codable { 11 | case processed 12 | case confirmed 13 | case finalized 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Encoding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Encoding.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Encoding: String, RawRepresentable, Codable { 11 | case base58 12 | case base64 13 | case json 14 | case jsonParsed 15 | case base64Zstd = "base64+zstd" 16 | } 17 | 18 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Filter.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct Filter: Codable { 11 | let memcmp: Memcmp 12 | let dataSize: UInt64 13 | } 14 | 15 | public struct Memcmp: Codable { 16 | let offset: Int 17 | let bytes: String 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Hash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | public typealias Hash = String 8 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Identity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | public struct Identity: Codable { 8 | let foundation: Double 9 | let foundationTerm: Double 10 | let initial: Double 11 | let taper: Double 12 | let terminal: Double 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Instruction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Instruction.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct CompiledInstruction: Codable { 11 | let programIDIndex: Int 12 | let accounts: [Int] 13 | let data: [UInt8] 14 | } 15 | 16 | // TODO: Unify "Instruction" across calls. 17 | //public struct Instruction: Codable { 18 | // let programID: [UInt8] 19 | // let accounts: [AccountMeta] 20 | // let data: [UInt8] 21 | //} 22 | 23 | public struct Instruction: Codable { 24 | let accounts: [Int] 25 | let data: String 26 | let programIDIndex: Int 27 | 28 | enum CodingKeys: String, CodingKey { 29 | case accounts 30 | case data 31 | case programIDIndex = "programIdIndex" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetAccountInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetAccountInfo.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetAccountInfoResponse = RPCResponse 12 | 13 | public struct GetAccountInfoResult: Codable { 14 | let context: GetAccountInfoContext 15 | let value: GetAccountInfoValue? 16 | } 17 | 18 | public struct GetAccountInfoContext: Codable { 19 | let slot: Int 20 | } 21 | 22 | public struct GetAccountInfoValue: Codable { 23 | let data: [String] 24 | let executable: Bool 25 | let lamports: UInt64 26 | let owner: String 27 | let rentEpoch: UInt64 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetBalanace.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetBalanace.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetBalanceResponse = RPCResponse 12 | 13 | public struct GetBalanceResult: Codable { 14 | let context: GetBalanceContext 15 | let value: UInt64 16 | } 17 | 18 | public struct GetBalanceContext: Codable { 19 | let slot: UInt64 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetBlockCommitment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetBlockCommitment.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetBlockCommitmentResponse = RPCResponse 12 | 13 | public struct GetBlockCommitmentResult: Codable { 14 | let commitment: [Int]? 15 | let totalStake: Double 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetBlockProduction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | // MARK: - Response 8 | public typealias GetBlockProductionResponse = RPCResponse 9 | 10 | public struct GetBlockProductionResult: Codable { 11 | public let context: GetBlockProductionContext 12 | public let value: GetBlockProductionValue 13 | } 14 | 15 | public struct GetBlockProductionContext: Codable { 16 | public let slot: Int 17 | } 18 | 19 | public struct GetBlockProductionValue: Codable { 20 | public let byIdentity: [String: [Int]] 21 | public let range: SlotRange 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetBlockTime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetBlockTime.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetBlockTimeResponse = RPCResponse 12 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetClusterNodes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetClusterNodes.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetClusterNodesResponse = RPCResponse<[GetClusterNodesResult]> 12 | 13 | public struct GetClusterNodesResult: Codable { 14 | let featureSet: UInt64? 15 | let gossip: String 16 | let pubkey: String 17 | let rpc: String? 18 | let tpu: String? 19 | let version: String? 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetConfirmedBlock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetConfirmedBlock.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetConfirmedBlockResponse = RPCResponse 12 | 13 | public struct GetConfirmedBlockResult: Codable { 14 | let blockTime: Int64? 15 | let blockhash: String 16 | let parentSlot: UInt64 17 | let previousBlockhash: String 18 | let rewards: [GetConfirmedBlockReward] 19 | let transactions: [GetConfirmedBlockTransactionElement] 20 | } 21 | 22 | public struct GetConfirmedBlockReward: Codable { 23 | let pubkey: String 24 | let lamports: Int64 25 | let postBalance: UInt64 26 | let rewardType: RewardType? 27 | } 28 | 29 | public struct GetConfirmedBlockTransactionElement: Codable { 30 | let meta: GetConfirmedBlockMeta? 31 | let transaction: Transaction 32 | } 33 | 34 | public struct GetConfirmedBlockMeta: Codable { 35 | let err: String? // ToDo 36 | let fee: UInt64 37 | let innerInstructions: [BlockInstruction]? 38 | let logMessages: [String]? 39 | let preBalances: [UInt64]? 40 | let postBalances: [UInt64]? 41 | let postTokenBalances: [String] 42 | let preTokenBalances: [String] 43 | } 44 | 45 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetConfirmedBlocks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetConfirmedBlocks.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetConfirmedBlocksResponse = RPCResponse<[UInt64]> 12 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetConfirmedBlocksWithLimit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetConfirmedBlocksWithLimit.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetConfirmedBlocksWithLimitResponse = RPCResponse<[UInt64]> 12 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetConfirmedSignaturesForAddress.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetConfirmedSignaturesForAddress.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetConfirmedSignaturesForAddressResponse = RPCResponse<[String]> 12 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetConfirmedSignaturesForAddress2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetConfirmedSignaturesForAddress2.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct GetConfirmedSignaturesForAddress2Config: Codable { 11 | let limit: UInt64? 12 | let before: String? 13 | let until: String? 14 | } 15 | 16 | // MARK: - Response 17 | public typealias GetConfirmedSignaturesForAddress2Response = RPCResponse<[GetConfirmedSignaturesForAddress2Result]> 18 | 19 | public struct GetConfirmedSignaturesForAddress2Result: Codable { 20 | let blockTime: Int64? 21 | let err: TransactionError? 22 | let memo: String? 23 | let signature: String 24 | let slot: UInt64 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetConfirmedTransaction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetConfirmedTransaction.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetConfirmedTransactionResponse = RPCResponse 12 | 13 | public struct GetConfirmedTransactionResult: Codable { 14 | let slot: UInt64 15 | let transaction: Transaction 16 | let blockTime: Int64? 17 | let meta: GetConfirmedTransactionMeta? 18 | } 19 | 20 | public struct GetConfirmedTransactionMeta: Codable { 21 | let err: TransactionError? 22 | let fee: UInt64 23 | let preBalances: [UInt64] 24 | let postBalances: [UInt64] 25 | let innerInstructions: [BlockInstruction]? 26 | let preTokenBalances: [UInt64]? 27 | let postTokenBalances: [UInt64]? 28 | let logMessages: [String]? 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetEpochInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetEpochInfo.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetEpochInfoResponse = RPCResponse 12 | 13 | public struct GetEpochInfoResult: Codable { 14 | let absoluteSlot: UInt64 15 | let blockHeight: UInt64 16 | let epoch: UInt64 17 | let slotIndex: UInt64 18 | let slotsInEpoch: UInt64 19 | let transactionCount: UInt64 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetEpochSchedule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GetEpochSchedule.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Arturo Jamaica on 2021/05/15. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Response 11 | public typealias GetEpochScheduleResponse = RPCResponse 12 | 13 | public struct GetEpochScheduleResult: Codable { 14 | public let firstNormalEpoch: Int 15 | public let firstNormalSlot: Int 16 | public let leaderScheduleSlotOffset: Int 17 | public let slotsPerEpoch: Int 18 | public let warmup: Bool 19 | } 20 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetFeeCalculatorForBlockhash.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | // MARK: - Response 8 | public typealias GetFeeCalculatorForBlockhashResponse = RPCResponse 9 | 10 | public struct GetFeeCalculatorForBlockhashResult: Codable { 11 | public let context: GetFeeCalculatorForBlockhashContext 12 | public let value: GetFeeCalculatorForBlockhashValue? 13 | } 14 | 15 | public struct GetFeeCalculatorForBlockhashContext: Codable { 16 | public let slot: Int 17 | } 18 | 19 | public struct GetFeeCalculatorForBlockhashValue: Codable { 20 | public let feeCalculator: FeeCalculator 21 | } 22 | 23 | public struct FeeCalculator: Codable { 24 | public let lamportsPerSignature: Int 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetFeeRateGovernor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | // MARK: - Response 8 | public typealias GetFeeRateGovernorResponse = RPCResponse 9 | 10 | public struct GetFeeRateGovernorResult: Codable { 11 | public let context: GetFeeRateGovernorContext 12 | public let value: GetFeeRateGovernorValue 13 | 14 | enum CodingKeys: String, CodingKey { 15 | case context = "context" 16 | case value = "value" 17 | } 18 | } 19 | 20 | public struct GetFeeRateGovernorContext: Codable { 21 | public let slot: Int 22 | 23 | enum CodingKeys: String, CodingKey { 24 | case slot = "slot" 25 | } 26 | } 27 | 28 | public struct GetFeeRateGovernorValue: Codable { 29 | public let feeRateGovernor: FeeRateGovernor 30 | 31 | enum CodingKeys: String, CodingKey { 32 | case feeRateGovernor = "feeRateGovernor" 33 | } 34 | } 35 | 36 | public struct FeeRateGovernor: Codable { 37 | public let burnPercent: Int 38 | public let maxLamportsPerSignature: Int 39 | public let minLamportsPerSignature: Int 40 | public let targetLamportsPerSignature: Int 41 | public let targetSignaturesPerSlot: Int 42 | 43 | enum CodingKeys: String, CodingKey { 44 | case burnPercent = "burnPercent" 45 | case maxLamportsPerSignature = "maxLamportsPerSignature" 46 | case minLamportsPerSignature = "minLamportsPerSignature" 47 | case targetLamportsPerSignature = "targetLamportsPerSignature" 48 | case targetSignaturesPerSlot = "targetSignaturesPerSlot" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/GetFees.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | // MARK: - Response 8 | public typealias GetFeesResponse = RPCResponse 9 | 10 | public struct GetFeesResult: Codable { 11 | public let context: GetFeesContext 12 | public let value: GetFeesValue 13 | } 14 | 15 | public struct GetFeesContext: Codable { 16 | public let slot: Int 17 | } 18 | 19 | public struct GetFeesValue: Codable { 20 | public let blockhash: String 21 | public let feeCalculator: FeeCalculator 22 | public let lastValidSlot: Int 23 | public let lastValidBlockHeight: Int 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/RPCResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RPCResponse.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Arturo Jamaica on 2021/05/15. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct RPCResponse: Codable { 11 | let jsonrpc: String 12 | let result: T 13 | let id: Int 14 | let error: RpcError? 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Requests and Responses/SimulateTransaction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimulateTransaction.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Request 11 | public struct SimulateTransactionRequest: Codable { 12 | let transaction: String 13 | let config: SimulateTransactionConfig? 14 | } 15 | 16 | public struct SimulateTransactionConfig: Codable { 17 | let sigVerify: Bool 18 | let commitment: Commitment? 19 | let encoding: Encoding? 20 | } 21 | 22 | // MARK: - Response 23 | public typealias SimulateTransactionResponse = RPCResponse 24 | 25 | public struct SimulateTransactionResult: Codable { 26 | let context: SimulateTransactionContext 27 | let value: SimulateTransactionValue 28 | } 29 | 30 | public struct SimulateTransactionContext: Codable { 31 | let slot: Int 32 | } 33 | 34 | public struct SimulateTransactionValue: Codable { 35 | let err: TransactionError? 36 | let logs: [String]? 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Solana/Models/RewardType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RewardType.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 4/8/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum RewardType: String, Codable { 11 | case Fee 12 | case Rent 13 | case Voting 14 | case Staking 15 | 16 | // Ugh. Wanted lowercase cases but this coding keys wasn't working. Will look into later. 17 | // enum CodingKeys: String, CodingKey { 18 | // case fee = "Fee" 19 | // case rent = "Rent" 20 | // case voting = "Voting" 21 | // case staking = "Staking" 22 | // } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Solana/Models/RpcError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RpcError.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct RpcError: Codable { 11 | let code: Int 12 | let message: String 13 | } 14 | -------------------------------------------------------------------------------- /Sources/Solana/Models/SlotRange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneCrucean.swift 3 | // 4 | 5 | import Foundation 6 | 7 | public struct SlotRange: Codable { 8 | public let firstSlot: UInt64 9 | public let lastSlot: UInt64 10 | } 11 | -------------------------------------------------------------------------------- /Sources/Solana/Models/TokenBalance.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenBalance.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct TokenBalance: Codable { 11 | let accountIndex: Int 12 | let mint: String 13 | let uiTokenAmount: UITokenAmount 14 | } 15 | 16 | public struct UITokenAmount: Codable { 17 | let amount: String 18 | let decimals: Int 19 | let uiAmount: Int? 20 | let uiAmountString: String 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Solana/Models/Transaction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Transaction.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Transaction 11 | public struct Transaction: Codable { 12 | let signatures: [String] 13 | let message: Message 14 | } 15 | 16 | // MARK: - Message 17 | public struct Message: Codable { 18 | let accountKeys: [String] 19 | let header: Header 20 | let instructions: [BlockInstruction] 21 | let recentBlockhash: String 22 | } 23 | 24 | // MARK: - Header 25 | public struct Header: Codable { 26 | let numReadonlySignedAccounts: Int 27 | let numReadonlyUnsignedAccounts: Int 28 | let numRequiredSignatures: Int 29 | } 30 | 31 | // MARK: - Instruction 32 | public struct BlockInstruction: Codable { 33 | let accounts: [Int] 34 | let data: String 35 | let programIDIndex: Int 36 | 37 | enum CodingKeys: String, CodingKey { 38 | case accounts 39 | case data 40 | case programIDIndex = "programIdIndex" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Solana/Models/TransactionError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransactionError.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum TransactionError: String, Codable { 11 | case AccountInUse 12 | case AccountLoadedTwice 13 | case AccountNotFound 14 | case ProgramAccountNotFound 15 | case InsufficientFundsForFee 16 | case InvalidAccountForFee 17 | case DuplicateSignature 18 | case BlockhashNotFound 19 | case InstructionError 20 | case CallChainTooDeep 21 | case MissingSignatureForFee 22 | case InvalidAccountIndex 23 | case SignatureFailure 24 | case InvalidProgramForExecution 25 | case SanitizeFailure 26 | case ClusterMaintenance 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Solana/Models/WebSockets/ProgramSubscribe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgramSubscribe.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | //// MARK: - Request 11 | //public struct ProgramSubscribeRequest: Codable { 12 | // let pubkey: String 13 | // let config: ProgramSubscribeConfig? 14 | //} 15 | // 16 | //public struct ProgramSubscribeConfig: Codable { 17 | // let commitment: Commitment? 18 | // var encoding: String = "jsonParsed" 19 | // let filters: Filter? 20 | //} 21 | // 22 | //// MARK: - Response 23 | //public struct ProgramSubscribeResponse: Codable { 24 | // 25 | //} 26 | // 27 | 28 | 29 | 30 | public struct TestRequest: Codable { 31 | let test: String 32 | } 33 | 34 | // MARK: - Response 35 | public struct TestResponse: Codable { 36 | let jsonrpc: String 37 | let result: TestResult 38 | let id: Int 39 | } 40 | 41 | public struct TestResult: Codable { 42 | let parent: Int 43 | let root: Int 44 | let slot: Int 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Solana/Network.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Network.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Network 11 | public enum Network { 12 | case custom(network: String) 13 | case dev 14 | case test 15 | case main 16 | 17 | var url: URL? { 18 | switch self { 19 | case .custom(let network): 20 | return URL(string: network) 21 | case .dev: 22 | return URL(string: "https://api.devnet.solana.com") 23 | case .test: 24 | return URL(string: "https://testnet.solana.com") 25 | case .main: 26 | return URL(string: "https://api.mainnet-beta.solana.com") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Solana/NetworkRequestType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkRequestType.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum HTTPRequestType: String { 11 | case get = "GET" 12 | case post = "POST" 13 | case put = "PUT" 14 | case delete = "DELETE" 15 | case head = "HEAD" 16 | case connect = "CONNECT" 17 | case options = "OPTIONS" 18 | case trace = "TRACE" 19 | case patch = "PATCH" 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Solana/Networking.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Networking.swift 3 | // Solana 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Networking 11 | public struct Networking { 12 | 13 | var globalHeaders: [String: String] = { 14 | var headers = [String: String]() 15 | headers["Content-Type"] = "application/json" 16 | return headers 17 | }() 18 | 19 | public init() {} 20 | 21 | public struct Response { 22 | public let value: T 23 | public let response: URLResponse 24 | } 25 | 26 | public func decodableTask(request: URLRequest, decoder: JSONDecoder = JSONDecoder(), completion: @escaping (Result, Error>) -> Void) { 27 | 28 | var tmpRequest = request 29 | 30 | globalHeaders.forEach { (key, value) in 31 | tmpRequest.addValue(value, forHTTPHeaderField: key) 32 | } 33 | 34 | URLSession.shared.dataTask(with: tmpRequest) { (data, response, error) in 35 | guard let data = data, error == nil, let response = response else { 36 | completion(.failure(SolanaAPIError.generic(message: "API Call guard failed."))) 37 | return 38 | } 39 | 40 | do { 41 | let objectOutput = try JSONDecoder().decode(T.self, from: data) 42 | completion(.success(Response(value: objectOutput, response: response))) 43 | } catch { 44 | completion(.failure(SolanaAPIError.generic(message: "There was an issue decoding the response."))) 45 | } 46 | 47 | 48 | 49 | }.resume() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Solana/Solana.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solana.swift 3 | // Solana 4 | // 5 | // Created by Gene Crucean on 3/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol SolanaDelegate { 11 | func receivedMessage(message: T) 12 | func disconnected() 13 | } 14 | 15 | extension SolanaDelegate { 16 | func disconnected() {} 17 | } 18 | 19 | public class Solana { 20 | private let networking: Networking 21 | private var networkURL: URL 22 | private let jsonrpc = "2.0" 23 | 24 | public var delegate: SolanaDelegate? 25 | 26 | public init(network: Network, networking: Networking = Networking()) { 27 | self.networkURL = network.url ?? URL(string: "https://devnet.solana.com")! 28 | self.networking = networking 29 | } 30 | 31 | // MARK: - Calls 32 | 33 | /// Returns all account information associated with the account of provided pubkey. 34 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getaccountinfo 35 | public func getAccountInfo(pubkey: String, completion: @escaping (Result, SolanaAPIError>) -> Void) { 36 | 37 | // Simple sanity checking. 38 | guard pubkey.count > 0 else { 39 | completion(.failure(.getAccountInfoError(message: "Please enter a valid pubkey"))) 40 | return 41 | } 42 | 43 | let body: [String: Any] = [ 44 | "jsonrpc": jsonrpc, 45 | "id": 1, 46 | "method": "getAccountInfo", 47 | "params": [ 48 | pubkey, 49 | ["encoding": Encoding.base58.rawValue] 50 | ] 51 | ] 52 | 53 | var request = URLRequest(url: networkURL) 54 | request.httpBody = body.convertDictToJsonData() 55 | request.httpMethod = HTTPRequestType.post.rawValue 56 | 57 | networking.decodableTask(request: request) { (result: Result, Error>) in 58 | switch result { 59 | case .failure(let error): 60 | if case let SolanaAPIError.generic(message) = error { 61 | completion(.failure(.getAccountInfoError(message: message))) 62 | } else { 63 | completion(.failure(.getAccountInfoError(message: error.localizedDescription))) 64 | } 65 | case .success(let res): 66 | completion(.success(res)) 67 | } 68 | } 69 | } 70 | 71 | /// Returns the balance of the provided pubkeys. 72 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getbalance 73 | public func getBalance(pubkeys: [String], completion: @escaping (Result, SolanaAPIError>) -> Void) { 74 | 75 | // Simple sanity checking. 76 | guard let data = pubkeys.first, data.count > 0 else { 77 | completion(.failure(.getBalanceError(message: "Please enter a valid pubkey(s). Comma separated."))) 78 | return 79 | } 80 | 81 | let body: [String: Any] = [ 82 | "jsonrpc": jsonrpc, 83 | "id": 1, 84 | "method": "getBalance", 85 | "params": pubkeys 86 | ] 87 | 88 | var request = URLRequest(url: networkURL) 89 | request.httpBody = body.convertDictToJsonData() 90 | request.httpMethod = HTTPRequestType.post.rawValue 91 | 92 | networking.decodableTask(request: request) { (result: Result, Error>) in 93 | switch result { 94 | case .failure(let error): 95 | if case let SolanaAPIError.generic(message) = error { 96 | completion(.failure(.getBalanceError(message: message))) 97 | } else { 98 | completion(.failure(.getBalanceError(message: error.localizedDescription))) 99 | } 100 | case .success(let res): 101 | completion(.success(res)) 102 | } 103 | } 104 | } 105 | 106 | /// Returns the block commitment for the provided blocks. 107 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getblockcommitment 108 | public func getBlockCommitment(blocks: [UInt64], completion: @escaping (Result, SolanaAPIError>) -> Void) { 109 | 110 | let body: [String: Any] = [ 111 | "jsonrpc": jsonrpc, 112 | "id": 1, 113 | "method": "getBlockCommitment", 114 | "params": blocks 115 | ] 116 | 117 | var request = URLRequest(url: networkURL) 118 | request.httpBody = body.convertDictToJsonData() 119 | request.httpMethod = HTTPRequestType.post.rawValue 120 | 121 | networking.decodableTask(request: request) { (result: Result, Error>) in 122 | switch result { 123 | case .failure(let error): 124 | if case let SolanaAPIError.generic(message) = error { 125 | completion(.failure(.getBlockCommitmentError(message: message))) 126 | } else { 127 | completion(.failure(.getBlockCommitmentError(message: error.localizedDescription))) 128 | } 129 | case .success(let res): 130 | completion(.success(res)) 131 | } 132 | } 133 | } 134 | 135 | /// Returns the block time for the provided blocks. 136 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getblocktime 137 | public func getBlockTime(blocks: [UInt64], completion: @escaping (Result, SolanaAPIError>) -> Void) { 138 | 139 | let body: [String: Any] = [ 140 | "jsonrpc": jsonrpc, 141 | "id": 1, 142 | "method": "getBlockTime", 143 | "params": blocks 144 | ] 145 | 146 | var request = URLRequest(url: networkURL) 147 | request.httpBody = body.convertDictToJsonData() 148 | request.httpMethod = HTTPRequestType.post.rawValue 149 | 150 | networking.decodableTask(request: request) { (result: Result, Error>) in 151 | switch result { 152 | case .failure(let error): 153 | if case let SolanaAPIError.generic(message) = error { 154 | completion(.failure(.getBlockTimeError(message: message))) 155 | } else { 156 | completion(.failure(.getBlockTimeError(message: error.localizedDescription))) 157 | } 158 | case .success(let res): 159 | completion(.success(res)) 160 | } 161 | } 162 | } 163 | 164 | /// Returns the cluster nodes. 165 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getclusternodes 166 | public func getClusterNodes(completion: @escaping (Result, SolanaAPIError>) -> Void) { 167 | 168 | let body: [String: Any] = [ 169 | "jsonrpc": jsonrpc, 170 | "id": 1, 171 | "method": "getClusterNodes" 172 | ] 173 | 174 | var request = URLRequest(url: networkURL) 175 | request.httpBody = body.convertDictToJsonData() 176 | request.httpMethod = HTTPRequestType.post.rawValue 177 | 178 | networking.decodableTask(request: request) { (result: Result, Error>) in 179 | switch result { 180 | case .failure(let error): 181 | if case let SolanaAPIError.generic(message) = error { 182 | completion(.failure(.getClusterNodesError(message: message))) 183 | } else { 184 | completion(.failure(.getClusterNodesError(message: error.localizedDescription))) 185 | } 186 | case .success(let res): 187 | completion(.success(res)) 188 | } 189 | } 190 | } 191 | 192 | /// Returns the block for the provided slot. 193 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getconfirmedblock 194 | /// NOTE: Only json encoding is supported at this time. 195 | public func getConfirmedBlock(slot: UInt64, encoding: Encoding = .json, completion: @escaping (Result, SolanaAPIError>) -> Void) { 196 | 197 | let body: [String: Any] = [ 198 | "jsonrpc": jsonrpc, 199 | "id": 1, 200 | "method": "getConfirmedBlock", 201 | "params": [ 202 | slot, 203 | encoding.rawValue 204 | ] 205 | ] 206 | 207 | var request = URLRequest(url: networkURL) 208 | request.httpBody = body.convertDictToJsonData() 209 | request.httpMethod = HTTPRequestType.post.rawValue 210 | 211 | networking.decodableTask(request: request) { (result: Result, Error>) in 212 | switch result { 213 | case .failure(let error): 214 | if case let SolanaAPIError.generic(message) = error { 215 | completion(.failure(.getConfirmedBlockError(message: message))) 216 | } else { 217 | completion(.failure(.getConfirmedBlockError(message: error.localizedDescription))) 218 | } 219 | case .success(let res): 220 | completion(.success(res)) 221 | } 222 | } 223 | } 224 | 225 | /// Returns recent block production information from the current or previous epoch. 226 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getblockproduction 227 | public func getBlockProduction(commitment: Commitment? = nil, slotRange: SlotRange? = nil, identity: String? = nil, completion: @escaping (Result, SolanaAPIError>) -> Void) { 228 | 229 | var params: [String: Any] = [:] 230 | 231 | if let commitment = commitment { 232 | params["commitment"] = commitment.rawValue 233 | } 234 | 235 | if let range = slotRange { 236 | params["range"] = [ 237 | "firstSlot": range.firstSlot, 238 | "lastSlot": range.lastSlot 239 | ] 240 | } 241 | 242 | if let identity = identity { 243 | params["identity"] = identity 244 | } 245 | 246 | let body: [String: Any] = [ 247 | "jsonrpc": jsonrpc, 248 | "id": 1, 249 | "method": "getBlockProduction", 250 | "params": [ 251 | params 252 | ] 253 | ] 254 | 255 | var request = URLRequest(url: networkURL) 256 | request.httpBody = body.convertDictToJsonData() 257 | request.httpMethod = HTTPRequestType.post.rawValue 258 | 259 | networking.decodableTask(request: request) { (result: Result, Error>) in 260 | switch result { 261 | case .failure(let error): 262 | if case let SolanaAPIError.generic(message) = error { 263 | completion(.failure(.getBlockProductionError(message: message))) 264 | } else { 265 | completion(.failure(.getBlockProductionError(message: error.localizedDescription))) 266 | } 267 | case .success(let res): 268 | completion(.success(res)) 269 | } 270 | } 271 | } 272 | 273 | /// Returns a list of confirmed blocks between two slots. 274 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getconfirmedblocks 275 | public func getConfirmedBlocks(startSlot: UInt64, endSlot: UInt64? = nil, completion: @escaping (Result, SolanaAPIError>) -> Void) { 276 | 277 | let body: [String: Any] = [ 278 | "jsonrpc": jsonrpc, 279 | "id": 1, 280 | "method": "getConfirmedBlocks", 281 | "params": [ 282 | startSlot, 283 | endSlot 284 | ] 285 | ] 286 | 287 | var request = URLRequest(url: networkURL) 288 | request.httpBody = body.convertDictToJsonData() 289 | request.httpMethod = HTTPRequestType.post.rawValue 290 | 291 | networking.decodableTask(request: request) { (result: Result, Error>) in 292 | switch result { 293 | case .failure(let error): 294 | if case let SolanaAPIError.generic(message) = error { 295 | completion(.failure(.getConfirmedBlocksError(message: message))) 296 | } else { 297 | completion(.failure(.getConfirmedBlocksError(message: error.localizedDescription))) 298 | } 299 | case .success(let res): 300 | completion(.success(res)) 301 | } 302 | } 303 | } 304 | 305 | /// Returns a list of confirmed blocks starting at the given slot. 306 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getconfirmedblockswithlimit 307 | public func getConfirmedBlocksWithLimit(startSlot: UInt64, limit: UInt64, completion: @escaping (Result, SolanaAPIError>) -> Void) { 308 | 309 | let body: [String: Any] = [ 310 | "jsonrpc": jsonrpc, 311 | "id": 1, 312 | "method": "getConfirmedBlocksWithLimit", 313 | "params": [ 314 | startSlot, 315 | limit 316 | ] 317 | ] 318 | 319 | var request = URLRequest(url: networkURL) 320 | request.httpBody = body.convertDictToJsonData() 321 | request.httpMethod = HTTPRequestType.post.rawValue 322 | 323 | networking.decodableTask(request: request) { (result: Result, Error>) in 324 | switch result { 325 | case .failure(let error): 326 | if case let SolanaAPIError.generic(message) = error { 327 | completion(.failure(.getConfirmedBlocksWithLimitError(message: message))) 328 | } else { 329 | completion(.failure(.getConfirmedBlocksWithLimitError(message: error.localizedDescription))) 330 | } 331 | case .success(let res): 332 | completion(.success(res)) 333 | } 334 | } 335 | } 336 | 337 | /// Returns confirmed signatures for transactions involving an address backwards in time from the provided signature or most recent confirmed block. 338 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getconfirmedsignaturesforaddress2 339 | public func getConfirmedSignaturesForAddress2(address: String, config: GetConfirmedSignaturesForAddress2Config? = nil, completion: @escaping (Result, SolanaAPIError>) -> Void) { 340 | 341 | // Simple sanity checking. 342 | guard address.count > 0 else { 343 | completion(.failure(.getConfirmedSignaturesForAddress2Error(message: "Please enter a valid pubkey"))) 344 | return 345 | } 346 | 347 | let body: [String: Any] = [ 348 | "jsonrpc": jsonrpc, 349 | "id": 1, 350 | "method": "getConfirmedSignaturesForAddress2", 351 | "params": [ 352 | address, 353 | [ 354 | "limit": config?.limit as Any, 355 | "before": config?.before as Any, 356 | "until": config?.until as Any 357 | ] 358 | ] 359 | ] 360 | 361 | var request = URLRequest(url: networkURL) 362 | request.httpBody = body.convertDictToJsonData() 363 | request.httpMethod = HTTPRequestType.post.rawValue 364 | 365 | networking.decodableTask(request: request) { (result: Result, Error>) in 366 | switch result { 367 | case .failure(let error): 368 | if case let SolanaAPIError.generic(message) = error { 369 | completion(.failure(.getConfirmedSignaturesForAddress2Error(message: message))) 370 | } else { 371 | completion(.failure(.getConfirmedSignaturesForAddress2Error(message: error.localizedDescription))) 372 | } 373 | case .success(let res): 374 | completion(.success(res)) 375 | } 376 | } 377 | } 378 | 379 | /// Returns transaction details for a confirmed transaction. 380 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getconfirmedtransaction 381 | public func getConfirmedTransaction(transaction: String, encoding: Encoding? = .json, completion: @escaping (Result, SolanaAPIError>) -> Void) { 382 | 383 | // Simple sanity checking. 384 | guard transaction.count > 0 else { 385 | completion(.failure(.getConfirmedTransactionError(message: "Please enter a valid pubkey"))) 386 | return 387 | } 388 | 389 | let body: [String: Any] = [ 390 | "jsonrpc": jsonrpc, 391 | "id": 1, 392 | "method": "getConfirmedTransaction", 393 | "params": [ 394 | transaction, 395 | encoding?.rawValue 396 | ] 397 | ] 398 | 399 | var request = URLRequest(url: networkURL) 400 | request.httpBody = body.convertDictToJsonData() 401 | request.httpMethod = HTTPRequestType.post.rawValue 402 | 403 | networking.decodableTask(request: request) { (result: Result, Error>) in 404 | switch result { 405 | case .failure(let error): 406 | if case let SolanaAPIError.generic(message) = error { 407 | completion(.failure(.getConfirmedTransactionError(message: message))) 408 | } else { 409 | completion(.failure(.getConfirmedTransactionError(message: error.localizedDescription))) 410 | } 411 | case .success(let res): 412 | completion(.success(res)) 413 | } 414 | } 415 | } 416 | 417 | /// Returns information about the current epoch. 418 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getepochinfo 419 | public func getEpochInfo(commitment: Commitment? = nil, completion: @escaping (Result, SolanaAPIError>) -> Void) { 420 | 421 | let body: [String: Any] = [ 422 | "jsonrpc": jsonrpc, 423 | "id": 1, 424 | "method": "getEpochInfo", 425 | "params": [ 426 | [ 427 | "commitment": commitment?.rawValue 428 | ] 429 | ] 430 | ] 431 | 432 | var request = URLRequest(url: networkURL) 433 | request.httpBody = body.convertDictToJsonData() 434 | request.httpMethod = HTTPRequestType.post.rawValue 435 | 436 | networking.decodableTask(request: request) { (result: Result, Error>) in 437 | switch result { 438 | case .failure(let error): 439 | if case let SolanaAPIError.generic(message) = error { 440 | completion(.failure(.getEpochInfoError(message: message))) 441 | } else { 442 | completion(.failure(.getEpochInfoError(message: error.localizedDescription))) 443 | } 444 | case .success(let res): 445 | completion(.success(res)) 446 | } 447 | } 448 | } 449 | 450 | /// Returns epoch schedule information from this cluster's genesis config. 451 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getepochschedule 452 | public func getEpochSchedule(completion: @escaping (Result, SolanaAPIError>) -> Void) { 453 | 454 | let body: [String: Any] = [ 455 | "jsonrpc": jsonrpc, 456 | "id": 1, 457 | "method": "getEpochSchedule", 458 | "params": [] 459 | ] 460 | 461 | var request = URLRequest(url: networkURL) 462 | request.httpBody = body.convertDictToJsonData() 463 | request.httpMethod = HTTPRequestType.post.rawValue 464 | 465 | networking.decodableTask(request: request) { (result: Result, Error>) in 466 | switch result { 467 | case .failure(let error): 468 | if case let SolanaAPIError.generic(message) = error { 469 | completion(.failure(.getEpochScheduleError(message: message))) 470 | } else { 471 | completion(.failure(.getEpochScheduleError(message: error.localizedDescription))) 472 | } 473 | case .success(let res): 474 | completion(.success(res)) 475 | } 476 | } 477 | } 478 | 479 | /// Returns the fee calculator associated with the query blockhash, or null if the blockhash has expired. 480 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getfeecalculatorforblockhash 481 | public func getFeeCalculatorFor(blockhash: String, completion: @escaping (Result, SolanaAPIError>) -> Void) { 482 | 483 | let body: [String: Any] = [ 484 | "jsonrpc": jsonrpc, 485 | "id": 1, 486 | "method": "getFeeCalculatorForBlockhash", 487 | "params": [ 488 | blockhash 489 | ] 490 | ] 491 | 492 | var request = URLRequest(url: networkURL) 493 | request.httpBody = body.convertDictToJsonData() 494 | request.httpMethod = HTTPRequestType.post.rawValue 495 | 496 | networking.decodableTask(request: request) { (result: Result, Error>) in 497 | switch result { 498 | case .failure(let error): 499 | if case let SolanaAPIError.generic(message) = error { 500 | completion(.failure(.getFeeCalculatorForBlockhashError(message: message))) 501 | } else { 502 | completion(.failure(.getFeeCalculatorForBlockhashError(message: error.localizedDescription))) 503 | } 504 | case .success(let res): 505 | completion(.success(res)) 506 | } 507 | } 508 | } 509 | 510 | /// Returns the fee rate governor information from the root bank. 511 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getfeerategovernor 512 | public func getFeeRateGovernor(completion: @escaping (Result, SolanaAPIError>) -> Void) { 513 | 514 | let body: [String: Any] = [ 515 | "jsonrpc": jsonrpc, 516 | "id": 1, 517 | "method": "getFeeRateGovernor", 518 | "params": [ 519 | 520 | ] 521 | ] 522 | 523 | var request = URLRequest(url: networkURL) 524 | request.httpBody = body.convertDictToJsonData() 525 | request.httpMethod = HTTPRequestType.post.rawValue 526 | 527 | networking.decodableTask(request: request) { (result: Result, Error>) in 528 | switch result { 529 | case .failure(let error): 530 | if case let SolanaAPIError.generic(message) = error { 531 | completion(.failure(.getFeeRateGovernorError(message: message))) 532 | } else { 533 | completion(.failure(.getFeeRateGovernorError(message: error.localizedDescription))) 534 | } 535 | case .success(let res): 536 | completion(.success(res)) 537 | } 538 | } 539 | } 540 | 541 | /// Returns a recent block hash from the ledger, a fee schedule that can be used to compute the cost of submitting a transaction using it, and the last slot in which the blockhash will be valid. 542 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getfees 543 | public func getFees(commitment: Commitment? = nil, completion: @escaping (Result, SolanaAPIError>) -> Void) { 544 | 545 | var params: [String: Any] = [:] 546 | 547 | if let commitment = commitment { 548 | params["commitment"] = commitment.rawValue 549 | } 550 | 551 | let body: [String: Any] = [ 552 | "jsonrpc": jsonrpc, 553 | "id": 1, 554 | "method": "getFees", 555 | "params": [ 556 | params 557 | ] 558 | ] 559 | 560 | var request = URLRequest(url: networkURL) 561 | request.httpBody = body.convertDictToJsonData() 562 | request.httpMethod = HTTPRequestType.post.rawValue 563 | 564 | networking.decodableTask(request: request) { (result: Result, Error>) in 565 | switch result { 566 | case .failure(let error): 567 | if case let SolanaAPIError.generic(message) = error { 568 | completion(.failure(.getFeesError(message: message))) 569 | } else { 570 | completion(.failure(.getFeesError(message: error.localizedDescription))) 571 | } 572 | case .success(let res): 573 | completion(.success(res)) 574 | } 575 | } 576 | } 577 | 578 | /// Returns the slot of the lowest confirmed block that has not been purged from the ledger. 579 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getfirstavailableblock 580 | public func getFirstAvailableBlock(completion: @escaping (Result, SolanaAPIError>) -> Void) { 581 | 582 | let body: [String: Any] = [ 583 | "jsonrpc": jsonrpc, 584 | "id": 1, 585 | "method": "getFirstAvailableBlock" 586 | ] 587 | 588 | var request = URLRequest(url: networkURL) 589 | request.httpBody = body.convertDictToJsonData() 590 | request.httpMethod = HTTPRequestType.post.rawValue 591 | 592 | networking.decodableTask(request: request) { (result: Result, Error>) in 593 | switch result { 594 | case .failure(let error): 595 | if case let SolanaAPIError.generic(message) = error { 596 | completion(.failure(.getFirstAvailableBlockError(message: message))) 597 | } else { 598 | completion(.failure(.getFirstAvailableBlockError(message: error.localizedDescription))) 599 | } 600 | case .success(let res): 601 | completion(.success(res)) 602 | } 603 | } 604 | } 605 | 606 | /// Returns the genesis hash. 607 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getgenesishash 608 | public func getGenesisHash(completion: @escaping (Result, SolanaAPIError>) -> Void) { 609 | 610 | let body: [String: Any] = [ 611 | "jsonrpc": jsonrpc, 612 | "id": 1, 613 | "method": "getGenesisHash" 614 | ] 615 | 616 | var request = URLRequest(url: networkURL) 617 | request.httpBody = body.convertDictToJsonData() 618 | request.httpMethod = HTTPRequestType.post.rawValue 619 | 620 | networking.decodableTask(request: request) { (result: Result, Error>) in 621 | switch result { 622 | case .failure(let error): 623 | if case let SolanaAPIError.generic(message) = error { 624 | completion(.failure(.getGenesisHashError(message: message))) 625 | } else { 626 | completion(.failure(.getGenesisHashError(message: error.localizedDescription))) 627 | } 628 | case .success(let res): 629 | completion(.success(res)) 630 | } 631 | } 632 | } 633 | 634 | /// Returns the current health of the node. 635 | /// https://docs.solana.com/developing/clients/jsonrpc-api#gethealth 636 | public func getHealth(completion: @escaping (Result, SolanaAPIError>) -> Void) { 637 | 638 | let body: [String: Any] = [ 639 | "jsonrpc": jsonrpc, 640 | "id": 1, 641 | "method": "getHealth" 642 | ] 643 | 644 | var request = URLRequest(url: networkURL) 645 | request.httpBody = body.convertDictToJsonData() 646 | request.httpMethod = HTTPRequestType.post.rawValue 647 | 648 | networking.decodableTask(request: request) { (result: Result, Error>) in 649 | switch result { 650 | case .failure(let error): 651 | if case let SolanaAPIError.generic(message) = error { 652 | completion(.failure(.getHealthError(message: message))) 653 | } else { 654 | completion(.failure(.getHealthError(message: error.localizedDescription))) 655 | } 656 | case .success(let res): 657 | completion(.success(res)) 658 | } 659 | } 660 | } 661 | 662 | /// Returns the identity pubkey for the current node. 663 | /// https://docs.solana.com/developing/clients/jsonrpc-api#getidentity 664 | public func getIdentity(completion: @escaping (Result, SolanaAPIError>) -> Void) { 665 | 666 | let body: [String: Any] = [ 667 | "jsonrpc": jsonrpc, 668 | "id": 1, 669 | "method": "getIdentity" 670 | ] 671 | 672 | var request = URLRequest(url: networkURL) 673 | request.httpBody = body.convertDictToJsonData() 674 | request.httpMethod = HTTPRequestType.post.rawValue 675 | 676 | networking.decodableTask(request: request) { (result: Result, Error>) in 677 | switch result { 678 | case .failure(let error): 679 | if case let SolanaAPIError.generic(message) = error { 680 | completion(.failure(.getIdentityError(message: message))) 681 | } else { 682 | completion(.failure(.getIdentityError(message: error.localizedDescription))) 683 | } 684 | case .success(let res): 685 | completion(.success(res)) 686 | } 687 | } 688 | } 689 | } 690 | -------------------------------------------------------------------------------- /Sources/Solana/SolanaErrors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SolanaErrors.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum SolanaAPIError: Error { 11 | case generic(message: String) 12 | case getAccountInfoError(message: String) 13 | case getBalanceError(message: String) 14 | case getBlockCommitmentError(message: String) 15 | case getBlockTimeError(message: String) 16 | case getClusterNodesError(message: String) 17 | case getConfirmedBlockError(message: String) 18 | case getBlockProductionError(message: String) 19 | case getConfirmedBlocksError(message: String) 20 | case getConfirmedBlocksWithLimitError(message: String) 21 | case getConfirmedSignaturesForAddressError(message: String) 22 | case getConfirmedSignaturesForAddress2Error(message: String) 23 | case getConfirmedTransactionError(message: String) 24 | case getEpochInfoError(message: String) 25 | case getEpochScheduleError(message: String) 26 | case getFeeCalculatorForBlockhashError(message: String) 27 | case getFeeRateGovernorError(message: String) 28 | case getFeesError(message: String) 29 | case getFirstAvailableBlockError(message: String) 30 | case getGenesisHashError(message: String) 31 | case getHealthError(message: String) 32 | case getIdentityError(message: String) 33 | 34 | case simulateTransactionError(message: String) 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Solana/SolanaSockets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SolanaSockets.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | public class SolanaSockets { 11 | private let websockets: WebSockets 12 | private var networkURL: URL 13 | private let jsonrpc = "2.0" 14 | 15 | public var delegate: SolanaDelegate? 16 | 17 | public init(network: Network, websockets: WebSockets = WebSockets()) { 18 | self.networkURL = network.url ?? URL(string: "https://devnet.solana.com")! 19 | self.websockets = websockets 20 | } 21 | 22 | public func testWebSocket(request: TestRequest?) -> Void { 23 | 24 | let body: [String: Any] = [ 25 | "jsonrpc": jsonrpc, 26 | "id": 1, 27 | "method": "slotSubscribe" 28 | ] 29 | 30 | var request = URLRequest(url: networkURL.appendingPathComponent(":8900")) 31 | request.httpBody = body.convertDictToJsonData() 32 | request.httpMethod = HTTPRequestType.post.rawValue 33 | 34 | let payload = TestRequest(test: "FOO") 35 | 36 | websockets.delegate = self 37 | websockets.webSocketTask(request: request, payload: payload) 38 | } 39 | 40 | public func testWebSocketDisconnect() { 41 | websockets.disconnect() 42 | } 43 | } 44 | 45 | extension SolanaSockets: WebSocketsDelegate { 46 | func recievedMessage(message: T) where T : Decodable, T : Encodable { 47 | delegate?.receivedMessage(message: message) 48 | } 49 | 50 | func disconnected() { 51 | delegate?.disconnected() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Solana/WebSockets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebSockets.swift 3 | // SolanaDemoApp 4 | // 5 | // Created by Gene Crucean on 3/14/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol WebSocketsDelegate: AnyObject { 11 | func recievedMessage(message: T) 12 | func disconnected() 13 | } 14 | 15 | public class WebSockets { 16 | 17 | var task: URLSessionWebSocketTask? 18 | var delegate: WebSocketsDelegate? 19 | 20 | public init() {} 21 | 22 | public func webSocketTask(request: URLRequest, payload: Req, decoder: JSONDecoder = JSONDecoder()) -> Void { 23 | let task = URLSession.shared.webSocketTask(with: request) 24 | self.task = task 25 | task.resume() 26 | 27 | if let jsonData = try? JSONEncoder().encode(payload) { 28 | task.send(.data(jsonData)) { (error) in 29 | if let error = error { 30 | print(error) 31 | } 32 | } 33 | 34 | receiveMessage() 35 | } 36 | } 37 | 38 | private func receiveMessage() { 39 | guard let task = self.task else { return } 40 | 41 | task.receive { (result) in 42 | switch result { 43 | case .failure: 44 | self.disconnect() 45 | self.delegate?.disconnected() 46 | case .success(let message): 47 | self.processMessage(message: message) 48 | self.receiveMessage() 49 | } 50 | } 51 | } 52 | 53 | private func processMessage(message: URLSessionWebSocketTask.Message) { 54 | switch message { 55 | case .string(let jsonString): 56 | self.delegate?.recievedMessage(message: jsonString) 57 | case .data(let data): 58 | if let _data = try? JSONDecoder().decode(TestResponse.self, from: data) { // ToDo: Need to make this generic. 59 | self.delegate?.recievedMessage(message: _data) 60 | } 61 | default: 62 | break 63 | } 64 | } 65 | 66 | public func disconnect() { 67 | task?.cancel() 68 | task = nil 69 | delegate?.disconnected() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import SolanaTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += SolanaTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /Tests/SolanaTests/SolanaTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Solana 3 | 4 | final class SolanaTests: XCTestCase { 5 | 6 | static var allTests = [ 7 | ("testExample", testExample), 8 | ] 9 | 10 | func testExample() { 11 | // This is an example of a functional test case. 12 | // Use XCTAssert and related functions to verify your tests produce the correct 13 | // results. 14 | XCTAssertEqual("Tests coming soon", "Tests coming soon") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/SolanaTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(SolanaTests.allTests), 7 | ] 8 | } 9 | #endif 10 | --------------------------------------------------------------------------------