├── .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 | 
2 |
3 | # Solana-Swift
4 |
5 | 
6 | [](https://github.com/crewshin/solana-swift/blob/main/LICENSE)
7 | [](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 |
--------------------------------------------------------------------------------