Skip to content

Commit 4c84c75

Browse files
committed
Merge branch 'refactor/pwn-3297' into refactor/fix-protection-level
2 parents ffc6286 + 33df007 commit 4c84c75

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1159
-48
lines changed

.github/workflows/docs.yml

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Swift
2+
3+
on:
4+
push:
5+
branches: [ refactor/pwn-3297 ]
6+
paths:
7+
- 'Sources/**/*'
8+
9+
jobs:
10+
docs:
11+
runs-on: macos-12
12+
steps:
13+
- uses: actions/checkout@v3
14+
15+
- uses: fwcd/swift-docc-action@v1
16+
with:
17+
target: SolanaSwift
18+
output: ./docs
19+
hosting-base-path: solana-swift
20+
disable-indexing: 'true'
21+
transform-for-static-hosting: 'true'
22+
23+
- name: Init new repo for docs
24+
run: |
25+
cd docs
26+
git init
27+
git add -A
28+
git config --local user.email "[email protected]"
29+
git config --local user.name "GitHub Action"
30+
git commit -m 'deploy'
31+
32+
- name: Force push to destination branch
33+
uses: ad-m/[email protected]
34+
with:
35+
github_token: ${{ secrets.GITHUB_TOKEN }}
36+
branch: docs
37+
force: true
38+
directory: ./docs

.github/workflows/unit-test.yml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: On pull request
2+
3+
on: [ pull_request ]
4+
5+
jobs:
6+
unit-test:
7+
runs-on: macos-12
8+
steps:
9+
- uses: actions/checkout@v2
10+
- name: Build source code
11+
run: swift build
12+
- name: Run tests
13+
run: swift test --filter "SolanaSwiftUnitTests"

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@ Carthage/Build
4242
OrcaSwapTransitiveTests-Secret.swift
4343

4444
.build
45-
.idea
45+
.idea

Package.resolved

+13-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"repositoryURL": "https://github.com/bigearsenal/loggerswift.git",
77
"state": {
88
"branch": null,
9-
"revision": "bbb62757aca270f8bb6489de50b48ae424daf913",
10-
"version": "1.0.0"
9+
"revision": "779dbd11a9a2fea3c82943f69a30ca75f4596fc8",
10+
"version": "1.0.2"
1111
}
1212
},
1313
{
@@ -19,13 +19,22 @@
1919
"version": "0.1.6"
2020
}
2121
},
22+
{
23+
"package": "SwiftDocCPlugin",
24+
"repositoryURL": "https://github.com/apple/swift-docc-plugin",
25+
"state": {
26+
"branch": null,
27+
"revision": "3303b164430d9a7055ba484c8ead67a52f7b74f6",
28+
"version": "1.0.0"
29+
}
30+
},
2231
{
2332
"package": "Task_retrying",
2433
"repositoryURL": "https://github.com/bigearsenal/task-retrying-swift.git",
2534
"state": {
2635
"branch": null,
27-
"revision": "1e83e99e412b27a5b1f2123493ed7ae4f2df7413",
28-
"version": "1.0.1"
36+
"revision": "c93fef795874cf31b7b0f8f6e9c23f4f15188dec",
37+
"version": "1.0.2"
2938
}
3039
},
3140
{

Package.swift

+11-3
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ let package = Package(
2121
.package(name: "secp256k1", url: "https://github.com/Boilertalk/secp256k1.swift.git", from: "0.1.0"),
2222
.package(name: "TweetNacl", url: "https://github.com/bitmark-inc/tweetnacl-swiftwrap.git", from: "1.0.2"),
2323

24-
.package(name: "Task_retrying", url: "https://github.com/bigearsenal/task-retrying-swift.git", from: "1.0.1"),
25-
.package(name: "LoggerSwift", url: "https://github.com/bigearsenal/loggerswift.git", from: "1.0.0")
24+
.package(name: "Task_retrying", url: "https://github.com/bigearsenal/task-retrying-swift.git", from: "1.0.2"),
25+
.package(name: "LoggerSwift", url: "https://github.com/bigearsenal/loggerswift.git", from: "1.0.2"),
26+
27+
// Docs generator
28+
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
2629
],
2730
targets: [
2831
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
@@ -38,9 +41,14 @@ let package = Package(
3841
// resources: [ .process("Resources") ]
3942
),
4043
.testTarget(
41-
name: "SolanaSwiftTests",
44+
name: "SolanaSwiftUnitTests",
4245
dependencies: ["SolanaSwift"]
4346
// resources: [ .process("Resources") ]
4447
),
48+
.testTarget(
49+
name: "SolanaSwiftIntegrationTests",
50+
dependencies: ["SolanaSwift"]
51+
// resources: [ .process("Resources") ]
52+
)
4553
]
4654
)

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Solana-blockchain client, written in pure swift.
55
[![Version](https://img.shields.io/cocoapods/v/SolanaSwift.svg?style=flat)](https://cocoapods.org/pods/SolanaSwift)
66
[![License](https://img.shields.io/cocoapods/l/SolanaSwift.svg?style=flat)](https://www.apache.org/licenses/LICENSE-2.0.html)
77
[![Platform](https://img.shields.io/cocoapods/p/SolanaSwift.svg?style=flat)](https://cocoapods.org/pods/SolanaSwift)
8+
[![Documentation Status](https://readthedocs.org/projects/ansicolortags/badge/?version=latest)](https://p2p-org.github.io/solana-swift/documentation/solanaswift)
89

910
## Features
1011
- [x] Key pairs generation

Sources/SolanaSwift/APIClient/APIClient+Extension.swift

+24
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,28 @@ extension SolanaAPIClient {
183183
}
184184
return knownWallets + wallets
185185
}
186+
187+
/// Wait until transaction is confirmed, return even when there is one or more confirmations and request timed out
188+
/// - Parameters:
189+
/// - signature: signature of the transaction
190+
/// - ignoreStatus: ignore status and return true even when observation is timed out
191+
public func waitForConfirmation(signature: String, ignoreStatus: Bool, timeout: Int = 60, delay: Int = 2) async throws {
192+
var statuses = [TransactionStatus]()
193+
for try await status in observeSignatureStatus(signature: signature, timeout: timeout, delay: delay) {
194+
statuses.append(status)
195+
}
196+
197+
// if the status is important
198+
if !ignoreStatus {
199+
guard let lastStatus = statuses.last else {
200+
throw SolanaError.transactionHasNotBeenConfirmed
201+
}
202+
switch lastStatus {
203+
case .confirmed, .finalized:
204+
return
205+
default:
206+
throw SolanaError.transactionHasNotBeenConfirmed
207+
}
208+
}
209+
}
186210
}

Sources/SolanaSwift/APIClient/APIClient.swift

+16
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,23 @@ public protocol SolanaAPIClient {
243243
///
244244
func observeSignatureStatus(signature: String, timeout: Int, delay: Int) -> AsyncStream<TransactionStatus>
245245

246+
/// Returns a recent block hash from the ledger, and a fee schedule that can be used to compute the cost of submitting a transaction using it.
247+
/// - Parameters:
248+
/// - commitment: (optional) Commitment
249+
/// - Throws: APIClientError
250+
/// - SeeAlso https://docs.solana.com/developing/clients/jsonrpc-api#getrecentblockhash
251+
///
246252
func getRecentBlockhash(commitment: Commitment?) async throws -> String
253+
254+
/// Returns signatures for confirmed transactions that include the given address in their accountKeys list.
255+
/// Returns signatures backwards in time from the provided signature or most recent confirmed block
256+
/// - Parameters:
257+
/// - address: account address as base-58 encoded string
258+
/// - configs: (optional) Configuration object
259+
/// - Throws: APIClientError
260+
/// - SeeAlso https://docs.solana.com/developing/clients/jsonrpc-api#getrecentblockhash
261+
///
262+
func getSignaturesForAddress(address: String, configs: RequestConfiguration?) async throws -> [SignatureInfo]
247263

248264
}
249265

Sources/SolanaSwift/APIClient/Networking/JSONRPCAPIClient.swift

+4
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ public class JSONRPCAPIClient: SolanaAPIClient {
178178
}
179179
}
180180

181+
public func getSignaturesForAddress(address: String, configs: RequestConfiguration? = nil) async throws -> [SignatureInfo] {
182+
try await self.get(method: "getSignaturesForAddress", params: [address, configs])
183+
}
184+
181185
public func simulateTransaction(transaction: String, configs: RequestConfiguration = RequestConfiguration(encoding: "base64")!) async throws -> SimulationResult {
182186
let result: Rpc<SimulationResult> = try await self.get(method: "simulateTransaction", params: [transaction, configs])
183187
if let err = result.value.err {

Sources/SolanaSwift/BlockchainClient/Helpers/AskCoin-HD/SwiftBigInteger/SMP Bignum Extensions.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public typealias Bignum = BInt
1515
extension Bignum
1616
{
1717
/// Representation as Data
18-
var data: Data {
18+
public var data: Data {
1919
let n = limbs.count
2020
var data = Data(count: n * 8)
2121
data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer<UInt8>) -> Void in

Sources/SolanaSwift/BlockchainClient/Helpers/BIP39/Mnemonic.swift

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
//
99

1010
import Foundation
11-
import CryptoKit
1211
import CommonCrypto
1312

1413
public class Mnemonic {

Sources/SolanaSwift/BlockchainClient/Helpers/Borsh/BinaryReader.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ public struct BinaryReader {
1111
}
1212

1313
extension BinaryReader {
14-
mutating func read(count: UInt32) -> [UInt8] {
14+
mutating public func read(count: UInt32) throws -> [UInt8] {
1515
let newPosition = cursor + Int(count)
16+
guard bytes.count >= newPosition else {
17+
throw SolanaError.couldNotRetrieveAccountInfo
18+
}
1619
let result = bytes[cursor..<newPosition]
1720
cursor = newPosition
1821
return Array(result)

Sources/SolanaSwift/BlockchainClient/Helpers/Borsh/BorshDeserialize.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ enum DeserializationError: Error {
1111
public extension FixedWidthInteger {
1212
init(from reader: inout BinaryReader) throws {
1313
var value: Self = .zero
14-
let bytes = reader.read(count: UInt32(MemoryLayout<Self>.size))
14+
let bytes = try reader.read(count: UInt32(MemoryLayout<Self>.size))
1515
let size = withUnsafeMutableBytes(of: &value, { bytes.copyBytes(to: $0) })
1616
assert(size == MemoryLayout<Self>.size)
1717
self = Self(littleEndian: value)
@@ -32,7 +32,7 @@ extension Int128: BorshDeserializable {}
3232
extension Float32: BorshDeserializable {
3333
public init(from reader: inout BinaryReader) throws {
3434
var value: Self = .zero
35-
let bytes = reader.read(count: UInt32(MemoryLayout<Self>.size))
35+
let bytes = try reader.read(count: UInt32(MemoryLayout<Self>.size))
3636
let size = withUnsafeMutableBytes(of: &value, { bytes.copyBytes(to: $0) })
3737
assert(size == MemoryLayout<Self>.size)
3838
assert(!value.isNaN, "For portability reasons we do not allow to deserialize NaNs.")
@@ -43,7 +43,7 @@ extension Float32: BorshDeserializable {
4343
extension Float64: BorshDeserializable {
4444
public init(from reader: inout BinaryReader) throws {
4545
var value: Self = .zero
46-
let bytes = reader.read(count: UInt32(MemoryLayout<Self>.size))
46+
let bytes = try reader.read(count: UInt32(MemoryLayout<Self>.size))
4747
let size = withUnsafeMutableBytes(of: &value, { bytes.copyBytes(to: $0) })
4848
assert(size == MemoryLayout<Self>.size)
4949
assert(!value.isNaN, "For portability reasons we do not allow to deserialize NaNs.")
@@ -54,7 +54,7 @@ extension Float64: BorshDeserializable {
5454
extension Bool: BorshDeserializable {
5555
public init(from reader: inout BinaryReader) throws {
5656
var value: Self = false
57-
let bytes = reader.read(count: UInt32(MemoryLayout<Self>.size))
57+
let bytes = try reader.read(count: UInt32(MemoryLayout<Self>.size))
5858
let size = withUnsafeMutableBytes(of: &value, { bytes.copyBytes(to: $0) })
5959
assert(size == MemoryLayout<Self>.size)
6060
self = value
@@ -74,7 +74,7 @@ extension Optional where Wrapped: BorshDeserializable {
7474
extension String: BorshDeserializable {
7575
public init(from reader: inout BinaryReader) throws {
7676
let count: UInt32 = try .init(from: &reader)
77-
let bytes = reader.read(count: count)
77+
let bytes = try reader.read(count: count)
7878
guard let value = String(bytes: bytes, encoding: .utf8) else {throw DeserializationError.noData}
7979
self = value
8080
}

Sources/SolanaSwift/Helpers/URLSession+Async.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import Foundation
22

33
@available(iOS, deprecated: 15.0, message: "Use the built-in API instead")
4+
@available(macOS, deprecated: 12.0, message: "Use the built-in API instead")
45
extension URLSession {
5-
func data(from url: URL) async throws -> (Data, URLResponse) {
6+
func data(for url: URL) async throws -> (Data, URLResponse) {
67
try await withCheckedThrowingContinuation { continuation in
78
let task = self.dataTask(with: url) { data, response, error in
89
guard let data = data, let response = response else {
@@ -17,7 +18,7 @@ extension URLSession {
1718
}
1819
}
1920

20-
func data(from urlRequest: URLRequest) async throws -> (Data, URLResponse) {
21+
func data(for urlRequest: URLRequest) async throws -> (Data, URLResponse) {
2122
try await withCheckedThrowingContinuation { continuation in
2223
let task = self.dataTask(with: urlRequest) { data, response, error in
2324
guard let data = data, let response = response else {

Sources/SolanaSwift/Helpers/URLSession+NetworkManager.swift

+1-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ extension URLSession: NetworkManager {
44

55
public func requestData(request: URLRequest) async throws -> Data {
66
let (data, _): (Data, URLResponse)
7-
if #available(iOS 15.0, macOS 12.0, *) {
8-
(data, _) = try await self.data(for: request)
9-
} else {
10-
(data, _) = try await self.data(from: request)
11-
}
7+
(data, _) = try await self.data(for: request)
128
return data
139
}
1410
}

Sources/SolanaSwift/Models/BufferLayout/BufferLayout.swift

+6-12
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import Foundation
33
public enum BufferLayoutError: Error {
44
case NotImplemented
55
}
6-
public protocol BufferLayout: Codable, BorshCodable {
7-
static var BUFFER_LENGTH: UInt64 { get }
8-
}
6+
public protocol BufferLayout: Codable, BorshCodable {}
97

108
extension BufferLayout {
119
public init(from decoder: Decoder) throws {
@@ -19,21 +17,17 @@ extension BufferLayout {
1917

2018
// Unable to get parsed data, fallback to decoding base64
2119
let stringData = (try? container.decode([String].self).first) ?? (try? container.decode(String.self))
22-
guard let string = stringData,
23-
let data = Data(base64Encoded: string)
24-
else {
20+
guard let string = stringData else {
2521
throw SolanaError.couldNotRetrieveAccountInfo
2622
}
2723

2824
if string.isEmpty && !(Self.self == EmptyInfo.self) {
2925
throw SolanaError.couldNotRetrieveAccountInfo
3026
}
3127

32-
do {
33-
var reader = BinaryReader(bytes: data.bytes)
34-
try self.init(from: &reader)
35-
} catch {
36-
throw SolanaError.couldNotRetrieveAccountInfo
37-
}
28+
let data = Data(base64Encoded: string) ?? Data(Base58.decode(string))
29+
30+
var reader = BinaryReader(bytes: data.bytes)
31+
try self.init(from: &reader)
3832
}
3933
}

Sources/SolanaSwift/Models/Models.swift

+16
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ public struct Response<T: Decodable>: Decodable {
1111
public let result: T?
1212
public let error: ResponseError?
1313
public let method: String?
14+
15+
// socket
16+
public let params: SocketParams<T>?
17+
}
18+
19+
public struct SocketParams<T: Decodable>: Decodable {
20+
public let result: Rpc<T>?
21+
public let subscription: UInt64?
1422
}
1523

1624
public struct ResponseError: Decodable {
@@ -145,6 +153,14 @@ public struct BufferInfo<T: BufferLayout>: Decodable {
145153
public let rentEpoch: UInt64
146154
}
147155

156+
public struct BufferInfoParsed<T: Decodable>: Decodable {
157+
public let lamports: Lamports
158+
public let owner: String
159+
public let data: T?
160+
public let executable: Bool
161+
public let rentEpoch: UInt64
162+
}
163+
148164
public struct PerformanceSample: Decodable {
149165
public let numSlots: UInt64
150166
public let numTransactions: UInt64

Sources/SolanaSwift/Models/SolanaError.swift

+7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public enum SolanaError: Swift.Error, Equatable {
4040
// Socket error
4141
case socket(Swift.Error)
4242

43+
// Transaction has not been confirmed
44+
case transactionHasNotBeenConfirmed
45+
4346
// Other
4447
case other(String)
4548
case unknown
@@ -48,4 +51,8 @@ public enum SolanaError: Swift.Error, Equatable {
4851
public static var couldNotRetrieveAccountInfo: Self {
4952
.other("Could not retrieve account info")
5053
}
54+
55+
public static var couldNotRetrieveBuffer: Self {
56+
.other("Could not retrieve buffer")
57+
}
5158
}

0 commit comments

Comments
 (0)