├── Gemfile
├── codecov.yml
├── .slather.yml
├── TezosKit
├── Common
│ ├── Models
│ │ ├── Hex.swift
│ │ ├── Address.swift
│ │ ├── KeyChainWallet.swift
│ │ └── SecureEnclaveWallet.swift
│ ├── RPC
│ │ ├── Header.swift
│ │ └── RPC.swift
│ ├── Michelson
│ │ ├── ListMichelsonParameter.swift
│ │ ├── StringMichelsonParameter.swift
│ │ ├── NoneMichelsonParameter.swift
│ │ ├── UnitMichelsonParameter.swift
│ │ ├── BoolMichelsonParameter.swift
│ │ ├── MichelsonAnnotation.swift
│ │ ├── SomeMichelsonParameter.swift
│ │ ├── LeftMichelsonParameter.swift
│ │ ├── RawMichelineMichelsonParameter.swift
│ │ ├── RightMichelsonParameter.swift
│ │ ├── AddressMichelsonParameter.swift
│ │ ├── MichelsonComparable.swift
│ │ ├── TimestampMichelsonParameter.swift
│ │ ├── PairMichelsonParameter.swift
│ │ ├── BytesMichelsonParameter.swift
│ │ ├── SignatureMichelsonParameter.swift
│ │ ├── ChainIDMichelsonParameter.swift
│ │ ├── KeyHashMichelsonParameter.swift
│ │ ├── KeyMichelsonParameter.swift
│ │ ├── MichelsonParameter.swift
│ │ ├── AbstractMichelsonParameter.swift
│ │ ├── IntMichelsonParameter.swift
│ │ └── NatMichelsonParameter.swift
│ ├── TezosKitError.swift
│ └── Services
│ │ └── Logger.swift
├── Conseil
│ ├── Models
│ │ ├── ConseilPlatform.swift
│ │ ├── ConseilNetwork.swift
│ │ ├── ConseilEntity.swift
│ │ └── ConseilQuery.swift
│ └── RPC
│ │ ├── ResponseAdapters
│ │ └── TransactionsResponseAdapter.swift
│ │ ├── ConseilQueryRPC.swift
│ │ ├── GetReceivedTransactions.RPC.swift
│ │ ├── GetSentTransactionsRPC.swift
│ │ ├── GetOriginatedContractsRPC.swift
│ │ └── GetRecievedSmartContractTransactionsRPC.swift
├── Crypto
│ ├── Sodium+TezosCrypto.swift
│ ├── EllipticalCurve.swift
│ ├── PublicKeyProtocol.swift
│ ├── EllipticCurveKeyPair
│ │ └── README.md
│ ├── Base58+TezosCrypto.swift
│ ├── Prefixes.swift
│ └── CryptoUtils.swift
├── TezosNode
│ ├── Models
│ │ ├── GasLimitPolicy.swift
│ │ ├── SimulationResult.swift
│ │ ├── PeriodKind.swift
│ │ ├── OperationFeePolicy.swift
│ │ ├── Operation
│ │ │ ├── OperationKind.swift
│ │ │ ├── Operation.swift
│ │ │ ├── OperationWithCounter.swift
│ │ │ ├── RevealOperation.swift
│ │ │ ├── AbstractOperation.swift
│ │ │ ├── TransactionOperation.swift
│ │ │ ├── OriginationOperation.swift
│ │ │ ├── SmartContractInvocationOperation.swift
│ │ │ ├── DelegationOperation.swift
│ │ │ └── OperationResponse.swift
│ │ ├── TezosProtocol.swift
│ │ ├── ForgingPolicy.swift
│ │ ├── OperationFees.swift
│ │ └── OperationMetadata.swift
│ ├── RPC
│ │ ├── GetExpectedQuorumRPC.swift
│ │ ├── GetChainHeadHashRPC.swift
│ │ ├── GetBallotsListRPC.swift
│ │ ├── GetBallotsRPC.swift
│ │ ├── GetChainHeadRPC.swift
│ │ ├── GetCurrentPeriodKindRPC.swift
│ │ ├── GetProposalsListRPC.swift
│ │ ├── GetProposalUnderEvaluationRPC.swift
│ │ ├── GetVotingDelegateRightsRPC.swift
│ │ ├── ResponseAdapters
│ │ │ ├── TezResponseAdapter.swift
│ │ │ ├── IntegerResponseAdapter.swift
│ │ │ ├── ResponseAdapter.swift
│ │ │ ├── PeriodKindResponseAdapter.swift
│ │ │ ├── AbstractResponseAdapter.swift
│ │ │ ├── JSONDictionaryResponseAdapter.swift
│ │ │ ├── JSONArrayResponseAdapter.swift
│ │ │ ├── StringResponseAdapter.swift
│ │ │ ├── PackDataResponseAdapter.swift
│ │ │ └── SimulationResultResponseAdapter.swift
│ │ ├── GetAddressBalanceRPC.swift
│ │ ├── GetAddressCounterRPC.swift
│ │ ├── GetAddressDelegateRPC.swift
│ │ ├── GetAddressManagerKeyRPC.swift
│ │ ├── GetContractStorageRPC.swift
│ │ ├── InjectOperationRPC.swift
│ │ ├── PackDataRPC.swift
│ │ ├── RunOperationRPC.swift
│ │ ├── GetBigMapValueByIDRPC.swift
│ │ ├── ForgeOperationRPC.swift
│ │ ├── PreapplyOperationRPC.swift
│ │ ├── Payload
│ │ │ ├── SignedProtocolOperationPayload.swift
│ │ │ ├── RunOperationPayload.swift
│ │ │ ├── SignedOperationPayload.swift
│ │ │ ├── OperationPayload.swift
│ │ │ └── PackDataPayload.swift
│ │ └── GetBigMapValueRPC.swift
│ └── Services
│ │ ├── SigningService.swift
│ │ ├── InjectionService.swift
│ │ └── OperationPayloadFactory.swift
├── Info.plist
└── Utils
│ ├── JailbreakUtils.swift
│ ├── MnemonicUtil.swift
│ └── JSONUtils.swift
├── Tests
├── UnitTests
│ └── TezosKit
│ │ ├── TezosNodeClientTests.swift
│ │ ├── CodingUtilTest.swift
│ │ ├── GetChainHeadRPCTest.swift
│ │ ├── GetBallotsRPCTest.swift
│ │ ├── GetChainHeadHashRPCTest.swift
│ │ ├── GetBallotsListRPCTest.swift
│ │ ├── GetProposalsListRPCTest.swift
│ │ ├── GetExpectedQuorumRPCTest.swift
│ │ ├── OperationWithCounterTest.swift
│ │ ├── InjectOperationRPCTest.swift
│ │ ├── GetCurrentPeriodKindRPCTest.swift
│ │ ├── GetVotingDelegateRightsRPCTest.swift
│ │ ├── GetProposalUnderEvaluationRPCTest.swift
│ │ ├── GetDelegateRPCTest.swift
│ │ ├── RPCTest.swift
│ │ ├── GetAddressBalanceRPCTest.swift
│ │ ├── GetAddressCounterRPCTest.swift
│ │ ├── GetContractStorageRPCTest.swift
│ │ ├── ConseilEntityTest.swift
│ │ ├── SignedProtocolOperationPayload.swift
│ │ ├── ConseilNetworkTest.swift
│ │ ├── ConseilPlatformTest.swift
│ │ ├── SignedOperationPayloadTest.swift
│ │ ├── MichelsonAnnotationTests.swift
│ │ ├── SigningServiceTests.swift
│ │ ├── RunOperationRPCTest.swift
│ │ ├── GetAddressManagerKeyRPCTest.swift
│ │ ├── ForgeOperationRPCTest.swift
│ │ ├── PackDataResponseAdapterTest.swift
│ │ ├── GetBigMapValueRPCTest.swift
│ │ ├── Info.plist
│ │ ├── PreapplyOperationRPCTest.swift
│ │ ├── RevealOperationTest.swift
│ │ ├── TransactionOperationTest.swift
│ │ ├── FeeEstimatorTest.swift
│ │ ├── JSONArrayResponseAdapterTest.swift
│ │ ├── StringResponseAdapterTest.swift
│ │ ├── InjectionServiceTest.swift
│ │ ├── JSONDictionaryResponseAdapterTest.swift
│ │ ├── TokenContractClientTests.swift
│ │ ├── AbstractOperationTest.swift
│ │ ├── IntegerResponseAdapterTest.swift
│ │ ├── TezResponseAdapterTest.swift
│ │ ├── PeriodKindResponseAdapterTest.swift
│ │ ├── DelegationOperationTest.swift
│ │ ├── TransactionsResponseAdapterTest.swift
│ │ ├── TransactionTest.swift
│ │ ├── ForgingServiceTests.swift
│ │ ├── JSONUtilsTest.swift
│ │ └── MnemonicUtilsTest.swift
├── IntegrationTests
│ ├── TezosKit
│ │ ├── Info.plist
│ │ └── ConseilClientIntegrationTests.swift
│ ├── Extensions
│ │ └── PromiseKit
│ │ │ └── ConseilClientIntegrationTests+Promises.swift
│ └── ManagerContractClientIntegrationTest.swift
└── Common
│ └── TestHelpers.swift
├── TezosKit.xcodeproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Examples
├── TezosKitExample.playground
│ ├── contents.xcplayground
│ └── Contents.swift
└── SecureEnclave
│ ├── Enclave.entitlements
│ ├── BackgroundHighlightedButton.swift
│ ├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
│ ├── Info.plist
│ └── SecureEnclaveAppDelegate.swift
├── docs
├── AthensProtocolFees.md
├── FutureWork.md
├── README.md
├── AdvancedFunctionality.md
├── PromiseKit.md
├── Fees.md
├── Operations.md
├── Networking.md
└── Testing.md
├── TezosKit.xcworkspace
├── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── contents.xcworkspacedata
├── Cartfile
├── Cartfile.resolved
├── Extensions
└── PromiseKit
│ ├── Common
│ └── NetworkClient+Promises.swift
│ └── Conseil
│ └── ConseilClient+Promises.swift
├── .gitignore
├── LICENSE
├── .swiftlint.yml
├── TezosKit.podspec
├── project.yml
└── .travis.yml
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'slather'
4 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | # List of ignore directories for code coverage.
2 | ignore:
3 | - "Tests"
4 |
--------------------------------------------------------------------------------
/.slather.yml:
--------------------------------------------------------------------------------
1 | coverage_service: cobertura_xml
2 | xcodeproj: ./TezosKit.xcodeproj
3 | scheme: TezosKit_iOS
4 | output_directory: ./
5 |
--------------------------------------------------------------------------------
/TezosKit/Common/Models/Hex.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | public typealias Hex = String
6 |
--------------------------------------------------------------------------------
/TezosKit/Common/Models/Address.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | public typealias Address = String
6 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/TezosNodeClientTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | @testable import TezosKit
4 | import XCTest
5 |
6 | class TezosNodeClientTests: XCTestCase {
7 | }
8 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/Models/ConseilPlatform.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | /// Platforms supported by Conseil.
4 | public enum ConseilPlatform: String, CaseIterable {
5 | case tezos
6 | }
7 |
--------------------------------------------------------------------------------
/TezosKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/TezosKitExample.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TezosKit/Crypto/Sodium+TezosCrypto.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 | import Sodium
5 |
6 | /// Wrapper the sodium library which allows a single instance to be shared.
7 | extension Sodium {
8 | public static let shared = Sodium()
9 | }
10 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/GasLimitPolicy.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A policy to apply when deciding what gas limit to use.
6 | public enum GasLimitPolicy {
7 | case estimate
8 | case `default`
9 | case custom(Int)
10 | }
11 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/SimulationResult.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// The result of simulating an operation.
6 | public struct SimulationResult {
7 | public let consumedGas: Int
8 | public let consumedStorage: Int
9 | }
10 |
--------------------------------------------------------------------------------
/docs/AthensProtocolFees.md:
--------------------------------------------------------------------------------
1 | # Fees for Athens Protocol (Protocol 004)
2 |
3 | Nomadic Labs (Athens protocol developers) has published operation fees here: http://tezos.gitlab.io/master/protocols/004_Pt24m4xi.html.
4 |
5 | The history for this file contains my previous recommended values.
6 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/Models/ConseilNetwork.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | /// Networks supported by Conseil.
4 | public enum ConseilNetwork: String, CaseIterable {
5 | case zeronet
6 | case alphanet
7 | case babylonnet
8 | case carthagenet
9 | case mainnet
10 | }
11 |
--------------------------------------------------------------------------------
/TezosKit/Crypto/EllipticalCurve.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// Elliptical curves that can be used in elliptical curve cryptographic operations.
6 | public enum EllipticalCurve {
7 | case ed25519
8 | case secp256k1
9 | case p256
10 | }
11 |
--------------------------------------------------------------------------------
/TezosKit.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TezosKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/FutureWork.md:
--------------------------------------------------------------------------------
1 | # Future Work
2 | A list of planned improvements to TezosKit.
3 | ## Fee Estimation
4 | Estimate fees and gas limits based on running an operation
5 | ## Local Forging
6 | Forge operations locally rather than forging on the remote node.
7 | ## Secure Enclave Support
8 | Build out support for signing using the secure enclave.
9 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/PeriodKind.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An enum representing the current period for protocol upgrades.
6 | public enum PeriodKind: String {
7 | case proposal
8 | case testingVote = "testing_vote"
9 | case testing
10 | case promotionVote = "promotion_vote"
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/Crypto/PublicKeyProtocol.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020
2 |
3 | import Foundation
4 |
5 | /// Opaque representation of a public key in TezosKit.
6 | public protocol PublicKeyProtocol {
7 | var base58CheckRepresentation: String { get }
8 | var signingCurve: EllipticalCurve { get }
9 | var publicKeyHash: String { get }
10 | }
11 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "attaswift/BigInt" ~> 5.0.0
2 | github "mxcl/PromiseKit" ~> 6.13.1
3 | github "keefertaylor/Base58Swift" ~> 2.1.14
4 | github "keefertaylor/MnemonicKit" ~> 1.3.11
5 | github "jedisct1/swift-sodium" ~> 0.8.0
6 | github "krzyzanowskim/CryptoSwift" ~> 1.3.0
7 | github "keefertaylor/secp256k1.swift" ~> 8.0.6
8 | github "thii/DTTJailbreakDetection" "master"
9 |
--------------------------------------------------------------------------------
/TezosKit/Common/RPC/Header.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | /// An encapsulation of headers to use in an RPC.
6 | public struct Header {
7 | public static let contentTypeApplicationJSON = Header(field: "Content-Type", value: "application/json")
8 |
9 | public let field: String
10 | public let value: String
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/ListMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020.
2 |
3 | import Foundation
4 |
5 | public class ListMichelsonParameter: AbstractMichelsonParameter {
6 | public init(args: [MichelsonParameter]) {
7 | let argArray = args.map { $0.networkRepresentation }
8 | super.init(networkRepresentation: argArray)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/CodingUtilTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class CryptoUtilTest: XCTestCase {
7 | public func testHexToBin() {
8 | XCTAssertEqual(CryptoUtils.hexToBin("1234"), [18, 52])
9 | }
10 |
11 | public func testBinToHex() {
12 | XCTAssertEqual(CryptoUtils.binToHex([18, 52]), "1234")
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "attaswift/BigInt" "5.1.0"
2 | github "jedisct1/swift-sodium" "0.8.0"
3 | github "keefertaylor/Base58Swift" "2.1.14"
4 | github "keefertaylor/MnemonicKit" "1.3.12"
5 | github "keefertaylor/secp256k1.swift" "8.0.6"
6 | github "krzyzanowskim/CryptoSwift" "1.3.1"
7 | github "mxcl/PromiseKit" "6.13.1"
8 | github "thii/DTTJailbreakDetection" "35c3fafabff7f28f1142471ed31a0616a024ae2f"
9 |
--------------------------------------------------------------------------------
/Examples/SecureEnclave/Enclave.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | keychain-access-groups
6 |
7 | $(AppIdentifierPrefix)com.keefertaylor.TezosKit.SecureEnclave
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetChainHeadRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetChainHeadRPCTest: XCTestCase {
7 | public func testGetChainHeadRPC() {
8 | let rpc = GetChainHeadRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetExpectedQuorumRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve the expected quorum.
6 | public class GetExpectedQuorumRPC: RPC {
7 | public init() {
8 | let endpoint = "chains/main/blocks/head/votes/current_quorum"
9 | super.init(endpoint: endpoint, responseAdapterClass: IntegerResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetBallotsRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetBallotsRPCTest: XCTestCase {
7 | public func testGetBallotsRPC() {
8 | let rpc = GetBallotsRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/votes/ballots")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetChainHeadHashRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve the hash of the head of the main chain.
6 | public class GetChainHeadHashRPC: RPC {
7 | public init() {
8 | let endpoint = "chains/main/blocks/head/hash"
9 | super.init(endpoint: endpoint, responseAdapterClass: StringResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetChainHeadHashRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetChainHeadHashRPCTest: XCTestCase {
7 | public func testGetChainHeadHashRPC() {
8 | let rpc = GetChainHeadHashRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/hash")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetBallotsListRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetBallotsListRPCTest: XCTestCase {
7 | public func testGetBallotsListRPC() {
8 | let rpc = GetBallotsListRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/votes/ballot_list")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetProposalsListRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetProposalsListRPCTest: XCTestCase {
7 | public func testGetProposalsListRPC() {
8 | let rpc = GetProposalsListRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/votes/proposals")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetBallotsListRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve ballots cast so far during a voting period.
6 | public class GetBallotsListRPC: RPC<[[String: Any]]> {
7 | public init() {
8 | let endpoint = "/chains/main/blocks/head/votes/ballot_list"
9 | super.init(endpoint: endpoint, responseAdapterClass: JSONArrayResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetBallotsRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve the sum of ballots cast so far during a voting period.
6 | public class GetBallotsRPC: RPC<[String: Any]> {
7 | public init() {
8 | let endpoint = "chains/main/blocks/head/votes/ballots"
9 | super.init(endpoint: endpoint, responseAdapterClass: JSONDictionaryResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetChainHeadRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve a JSON dictionary of info about the head of the current chain.
6 | public class GetChainHeadRPC: RPC<[String: Any]> {
7 | public init() {
8 | let endpoint = "/chains/main/blocks/head"
9 | super.init(endpoint: endpoint, responseAdapterClass: JSONDictionaryResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/StringMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a string parameter in Michelson.
6 | public class StringMichelsonParameter: AbstractMichelsonParameter {
7 | public init(string: String, annotations: [MichelsonAnnotation]? = nil) {
8 | super.init(networkRepresentation: [MichelineConstants.string: string], annotations: annotations)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/OperationFeePolicy.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A policy that determines how to apply fees on an operation.
6 | public enum OperationFeePolicy {
7 | /// Use the default fees provided by TezosKit.
8 | case `default`
9 |
10 | /// Use custom fees in the associated value.
11 | case custom(OperationFees)
12 |
13 | /// Estimate the fees.
14 | case estimate
15 | }
16 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetCurrentPeriodKindRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve the current period kind for voting.
6 | public class GetCurrentPeriodKindRPC: RPC {
7 | public init() {
8 | let endpoint = "chains/main/blocks/head/votes/current_period_kind"
9 | super.init(endpoint: endpoint, responseAdapterClass: PeriodKindResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetProposalsListRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve a list of proposals with number of supporters.
6 | public class GetProposalsListRPC: RPC<[[String: Any]]> {
7 | public init() {
8 | let endpoint = "chains/main/blocks/head/votes/proposals"
9 | super.init(endpoint: endpoint, responseAdapterClass: JSONArrayResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetProposalUnderEvaluationRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve the current proposal under evaluation.
6 | public class GetProposalUnderEvaluationRPC: RPC {
7 | public init() {
8 | let endpoint = "chains/main/blocks/head/votes/current_proposal"
9 | super.init(endpoint: endpoint, responseAdapterClass: StringResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetExpectedQuorumRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetExpectedQuorumRPCTest: XCTestCase {
7 | public func testGetExpectedQuorumRPCTest() {
8 | let rpc = GetExpectedQuorumRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/votes/current_quorum")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/OperationWithCounterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class OperationWithCounterTest: XCTestCase {
7 | public func testDictionaryRepresentation() {
8 | let dictionaryRepresentation = OperationWithCounter.testOperationWithCounter.dictionaryRepresentation
9 | XCTAssertEqual(dictionaryRepresentation["counter"] as? String, String(Int.testAddressCounter))
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/InjectOperationRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class InjectOperationRPCTest: XCTestCase {
7 | public func testInjectRPC() {
8 | let payload = "payload"
9 | let rpc = InjectionRPC(payload: payload)
10 |
11 | XCTAssertEqual(rpc.endpoint, "/injection/operation")
12 | XCTAssertEqual(rpc.payload, payload)
13 | XCTAssertTrue(rpc.isPOSTRequest)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/OperationKind.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An enum representing all supported operation types. Raw values of the enum represent the string the Tezos blockchain
6 | /// expects for the "kind" attribute when forging / pre-applying / injecting operations
7 | public enum OperationKind: String {
8 | case transaction
9 | case reveal
10 | case delegation
11 | case origination
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetCurrentPeriodKindRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetCurrentPeriodKindRPCTest: XCTestCase {
7 | public func testGetCurrentPeriodKindRPC() {
8 | let rpc = GetCurrentPeriodKindRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/votes/current_period_kind")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetVotingDelegateRightsRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetVotingDelegateRightsRPCTest: XCTestCase {
7 | public func testGetVotingDelegateRightsRPC() {
8 | let rpc = GetVotingDelegateRightsRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/votes/listings")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/NoneMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a none param in Michelson.
6 | public class NoneMichelsonParameter: AbstractMichelsonParameter {
7 | public init(annotations: [MichelsonAnnotation]? = nil) {
8 | super.init(
9 | networkRepresentation: [ MichelineConstants.primitive: MichelineConstants.none ],
10 | annotations: annotations
11 | )
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetVotingDelegateRightsRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will retrieve a list of delegates with their voting weight, in number of rolls.
6 | public class GetVotingDelegateRightsRPC: RPC<[[String: Any]]> {
7 | public init() {
8 | let endpoint = "chains/main/blocks/head/votes/listings"
9 | super.init(endpoint: endpoint, responseAdapterClass: JSONArrayResponseAdapter.self)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/UnitMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of an unit parameter in Michelson.
6 | public class UnitMichelsonParameter: AbstractMichelsonParameter {
7 | public init(annotations: [MichelsonAnnotation]? = nil) {
8 | super.init(
9 | networkRepresentation: [ MichelineConstants.primitive: MichelineConstants.unit ],
10 | annotations: annotations
11 | )
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/RPC/ResponseAdapters/TransactionsResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | public class TransactionsResponseAdapter: AbstractResponseAdapter<[Transaction]> {
6 | public override class func parse(input: Data) -> [Transaction]? {
7 | guard let transactions = JSONArrayResponseAdapter.parse(input: input) else {
8 | return nil
9 | }
10 | return transactions.compactMap { Transaction($0) }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/TezosProtocol.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// Protocols supported by TezosKit.
6 | ///
7 | /// - Note: The Tezos network upgrades asynchronously and protocol versions between Zeronet, Alphanet and Mainnet are
8 | /// not necessarily the same.
9 | public enum TezosProtocol {
10 | case athens // Protocol version 4
11 | case babylon // Protocol version 5
12 | case carthage // Protocol version 6
13 | }
14 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/Models/ConseilEntity.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | /// Entities that may be queried in the Conseil API.
6 | public enum ConseilEntity: String, CaseIterable {
7 | case account = "accounts"
8 | case baker = "bakers"
9 | case ballots = "ballots"
10 | case block = "blocks"
11 | case fee = "fees"
12 | case operation = "operations"
13 | case operationGroup = "operation_groups"
14 | case proposal = "proposals"
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetProposalUnderEvaluationRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetProposalUnderEvaluationRPCTest: XCTestCase {
7 | public func testGetProposalUnderEvaluationRPC() {
8 | let rpc = GetProposalUnderEvaluationRPC()
9 |
10 | XCTAssertEqual(rpc.endpoint, "chains/main/blocks/head/votes/current_proposal")
11 | XCTAssertNil(rpc.payload)
12 | XCTAssertFalse(rpc.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/TezResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Parse a given response as a string representing an amount of Tez.
6 | public class TezResponseAdapter: AbstractResponseAdapter {
7 | public override class func parse(input: Data) -> Tez? {
8 | guard let balanceString = StringResponseAdapter.parse(input: input) else {
9 | return nil
10 | }
11 | return Tez(balanceString)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetDelegateRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetDelegateRPCTest: XCTestCase {
7 | public func testGetDelegateRPC() {
8 | let address = "abc123"
9 | let rpc = GetDelegateRPC(address: address)
10 |
11 | XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/context/contracts/" + address + "/delegate")
12 | XCTAssertNil(rpc.payload)
13 | XCTAssertFalse(rpc.isPOSTRequest)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/RPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | @testable import TezosKit
4 | import XCTest
5 |
6 | class RPCTest: XCTestCase {
7 | public func testIsPOSTRequest() {
8 | let getRPC = RPC(endpoint: "a", responseAdapterClass: StringResponseAdapter.self)
9 | XCTAssertFalse(getRPC.isPOSTRequest)
10 |
11 | let postRPC = RPC(endpoint: "a", responseAdapterClass: StringResponseAdapter.self, payload: "abc")
12 | XCTAssertTrue(postRPC.isPOSTRequest)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetAddressBalanceRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetAddressBalanceRPCTest: XCTestCase {
7 | public func testGetAddressBalanceRPC() {
8 | let address = "abc123"
9 | let rpc = GetAddressBalanceRPC(address: address)
10 |
11 | XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/context/contracts/" + address + "/balance")
12 | XCTAssertNil(rpc.payload)
13 | XCTAssertFalse(rpc.isPOSTRequest)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetAddressCounterRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class GetAddressCounterRPCTest: XCTestCase {
7 | public func testGetAddressCounterRPC() {
8 | let address = "abc123"
9 | let rpc = GetAddressCounterRPC(address: address)
10 |
11 | XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/context/contracts/" + address + "/counter")
12 | XCTAssertNil(rpc.payload)
13 | XCTAssertFalse(rpc.isPOSTRequest)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetContractStorageRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class GetContractStorageRPCTest: XCTestCase {
7 | func testGetContractStorageRPC() {
8 | let address = "abc123"
9 | let rpc = GetContractStorageRPC(address: address)
10 |
11 | XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/context/contracts/\(address)/storage")
12 | XCTAssertNil(rpc.payload)
13 | XCTAssertFalse(rpc.isPOSTRequest)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/ForgingPolicy.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// An enum defining policies used to forge operations.
6 | public enum ForgingPolicy {
7 | /// Always forge operations remotely on the node.
8 | case remote
9 |
10 | /// Always forge locally. Fail if the operation cannot be forged locally.
11 | case local
12 |
13 | /// Attempt to forge locally but fallback to remote forging if local forging is not possible.
14 | case localWithRemoteFallBack
15 | }
16 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetAddressBalanceRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC that will retrieve the balance of a given address.
6 | public class GetAddressBalanceRPC: RPC {
7 | /// - Parameter address: The address to retrieve info about.
8 | public init(address: Address) {
9 | let endpoint = "/chains/main/blocks/head/context/contracts/" + address + "/balance"
10 | super.init(endpoint: endpoint, responseAdapterClass: TezResponseAdapter.self)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetAddressCounterRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// A RPC which will retrieve the counter for an address.
6 | public class GetAddressCounterRPC: RPC {
7 | /// - Parameter address: The address to retrieve info about.
8 | public init(address: Address) {
9 | let endpoint = "/chains/main/blocks/head/context/contracts/" + address + "/counter"
10 | super.init(endpoint: endpoint, responseAdapterClass: IntegerResponseAdapter.self)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetAddressDelegateRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC that will retrieve the delegate of a given address.
6 | public class GetDelegateRPC: RPC {
7 | /// - Parameter address: The address to retrieve info about.
8 | public init(address: Address) {
9 | let endpoint = "/chains/main/blocks/head/context/contracts/" + address + "/delegate"
10 | super.init(endpoint: endpoint, responseAdapterClass: StringResponseAdapter.self)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/IntegerResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Parse a given response as a string representing an Integer.
6 | public class IntegerResponseAdapter: AbstractResponseAdapter {
7 | public override class func parse(input: Data) -> Int? {
8 | guard let parsedString = StringResponseAdapter.parse(input: input),
9 | let integer = Int(parsedString) else {
10 | return nil
11 | }
12 | return integer
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/BoolMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a boolean parameter in Michelson.
6 | public class BoolMichelsonParameter: AbstractMichelsonParameter {
7 | public init(bool: Bool, annotations: [MichelsonAnnotation]? = nil) {
8 | let stringRep = bool ? MichelineConstants.true : MichelineConstants.false
9 | super.init(networkRepresentation: [MichelineConstants.primitive: stringRep ], annotations: annotations)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetAddressManagerKeyRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC that will retrieve the manager key of a given address.
6 | public class GetAddressManagerKeyRPC: RPC {
7 | /// - Parameter address: The address to retrieve info about.
8 | public init(address: Address) {
9 | let endpoint = "/chains/main/blocks/head/context/contracts/" + address + "/manager_key"
10 | super.init(endpoint: endpoint, responseAdapterClass: StringResponseAdapter.self)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetContractStorageRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// An RPC that will retrieve the storage of a given contract.
6 | public class GetContractStorageRPC: RPC<[String: Any]> {
7 | /// - Parameter address: The address to retrieve info about.
8 | public init(address: Address) {
9 | let endpoint = "/chains/main/blocks/head/context/contracts/\(address)/storage"
10 | super.init(endpoint: endpoint, responseAdapterClass: JSONDictionaryResponseAdapter.self)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/ConseilEntityTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class ConseilEntityTest: XCTestCase {
7 | func testEntityRawValueURLEncoded() {
8 | for entity in ConseilEntity.allCases {
9 | guard let escapedEntity =
10 | entity.rawValue.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else {
11 | XCTFail()
12 | return
13 | }
14 | XCTAssertEqual(entity.rawValue, escapedEntity)
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/ResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// A response adapter is an adapter that takes abstract data and turns it into a given type.
6 | /// Adapters can be used to transform arbitrary network bytes from RPCs into concrete model types.
7 | protocol ResponseAdapter {
8 | /// The type that this ResponseAdapter will parse to.
9 | associatedtype ParsedType
10 |
11 | /// Parse the given data to the given type.
12 | static func parse(input: Data) -> ParsedType?
13 | }
14 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/SignedProtocolOperationPayload.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class SignedProtocolOperationPayloadTest: XCTestCase {
7 | public func testDictionaryRepresentation() {
8 | let dictionaryRepresentation =
9 | SignedProtocolOperationPayload.testSignedProtocolOperationPayload.dictionaryRepresentation
10 |
11 | XCTAssertEqual(dictionaryRepresentation.count, 1)
12 | XCTAssertEqual(dictionaryRepresentation[0]["protocol"] as? String, String.testProtocol)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/PeriodKindResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Parse a given response as a string representing an PeriodKind.
6 | public class PeriodKindResponseAdapter: AbstractResponseAdapter {
7 | public override class func parse(input: Data) -> PeriodKind? {
8 | guard let parsedString = StringResponseAdapter.parse(input: input),
9 | let periodKind = PeriodKind(rawValue: parsedString) else {
10 | return nil
11 | }
12 | return periodKind
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/ConseilNetworkTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class ConseilNetworkTest: XCTestCase {
7 | func testNetworkRawValueURLEncoded() {
8 | for network in ConseilNetwork.allCases {
9 | guard let escapedNetwork = network.rawValue.addingPercentEncoding(
10 | withAllowedCharacters: CharacterSet.urlQueryAllowed
11 | ) else {
12 | XCTFail()
13 | return
14 | }
15 | XCTAssertEqual(network.rawValue, escapedNetwork)
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/MichelsonAnnotation.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A Michelson annotation.
6 | public class MichelsonAnnotation {
7 | public let value: String
8 |
9 | /// Initialize a new annotation.
10 | ///
11 | /// - Note: Michelson annotations must start with ':', '%', or '@'.
12 | public init?(annotation: String) {
13 | guard annotation.starts(with: ":") || annotation.starts(with: "%") || annotation.starts(with: "@") else {
14 | return nil
15 | }
16 | self.value = annotation
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/ConseilPlatformTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class ConseilPlatformTest: XCTestCase {
7 | func testPlatformRawValueURLEncoded() {
8 | for platform in ConseilPlatform.allCases {
9 | guard let escapedPlatform = platform.rawValue.addingPercentEncoding(
10 | withAllowedCharacters: CharacterSet.urlQueryAllowed
11 | ) else {
12 | XCTFail()
13 | return
14 | }
15 | XCTAssertEqual(platform.rawValue, escapedPlatform)
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Services/SigningService.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Base58Swift
4 | import Foundation
5 |
6 | /// An opaque object which implements public key cryptography functions.
7 | public protocol SignatureProvider {
8 | func sign(_ hex: String) -> [UInt8]?
9 | var publicKey: PublicKeyProtocol { get }
10 | }
11 |
12 | /// Manages signing of transactions.
13 | public enum SigningService {
14 | public static func sign(_ hex: String, with signatureProvider: SignatureProvider) -> [UInt8]? {
15 | return signatureProvider.sign(hex)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/SignedOperationPayloadTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | // TODO(keefertaylor): Merge in crypto tests.
7 |
8 | class SignedOperationPayloadTest: XCTestCase {
9 | public func testDictionaryRepresentation() {
10 | let dictionaryRepresentation = SignedOperationPayload.testSignedOperationPayload.dictionaryRepresentation
11 | XCTAssertEqual(
12 | dictionaryRepresentation["signature"] as? String,
13 | CryptoUtils.base58(signature: .testSignature, signingCurve: .ed25519)
14 | )
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Examples/SecureEnclave/BackgroundHighlightedButton.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020.
2 |
3 | import UIKit
4 |
5 | class BackgroundHighlightedButton: UIButton {
6 | @IBInspectable var highlightedBackgroundColor: UIColor?
7 | @IBInspectable var nonHighlightedBackgroundColor: UIColor?
8 | override var isHighlighted: Bool {
9 | get {
10 | return super.isHighlighted
11 | }
12 | set {
13 | if newValue {
14 | self.backgroundColor = .blue
15 | } else {
16 | self.backgroundColor = .gray
17 | }
18 | super.isHighlighted = isHighlighted
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/SomeMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | // A representation of a some parameter in Michelson.
6 | public class SomeMichelsonParameter: AbstractMichelsonParameter {
7 | public init(some: MichelsonParameter, annotations: [MichelsonAnnotation]? = nil) {
8 | super.init(
9 | networkRepresentation: [
10 | MichelineConstants.primitive: MichelineConstants.some,
11 | MichelineConstants.args: [
12 | some.networkRepresentation
13 | ]
14 | ],
15 | annotations: annotations
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/AbstractResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An abstract super class for all response adapters. This class is a shim to allow generics and associated types to
6 | /// play nicely together.
7 | public class AbstractResponseAdapter: ResponseAdapter {
8 | public class func parse(input _: Data) -> T? {
9 | fatalError("Use a concrete implementation of the response adapter class")
10 | }
11 |
12 | /// Please do not instantiate adapters. Adapters should only be used as static utility classes.
13 | private init() {}
14 | }
15 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/MichelsonAnnotationTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class MichelsonAnnotationTests: XCTestCase {
7 | func testValidAnnotations() {
8 | let annotationValue = "tezoskit"
9 | let validAnnotations = [ "@\(annotationValue)", "%\(annotationValue)", ":\(annotationValue)"]
10 | for annotation in validAnnotations {
11 | XCTAssertNotNil(MichelsonAnnotation(annotation: annotation))
12 | }
13 | }
14 |
15 | func testInvalidAnnotation() {
16 | XCTAssertNil(MichelsonAnnotation(annotation: "&nonsense"))
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/LeftMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a left parameter in Michelson.
6 | public class LeftMichelsonParameter: AbstractMichelsonParameter {
7 | public init(arg: MichelsonParameter, annotations: [MichelsonAnnotation]? = nil) {
8 | let argArray = [ arg.networkRepresentation ]
9 | super.init(
10 | networkRepresentation: [
11 | MichelineConstants.primitive: MichelineConstants.left,
12 | MichelineConstants.args: argArray
13 | ],
14 | annotations: annotations
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/RawMichelineMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RawMichelineMichelsonParameter.swift
3 | // TezosKit
4 | //
5 | // Created by Simon Mcloughlin on 04/03/2020.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Allows passing in Raw Micheline as a dictionary to handle any unsupported types or structures
11 | public class RawMichelineMichelsonParameter: AbstractMichelsonParameter {
12 | public init(micheline: [String: Any]) {
13 | super.init(networkRepresentation: micheline, annotations: nil)
14 | }
15 |
16 | public init(micheline: [Any]) {
17 | super.init(networkRepresentation: micheline)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/RightMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | // A representation of a right parameter in Michelson.
6 | public class RightMichelsonParameter: AbstractMichelsonParameter {
7 | public init(arg: MichelsonParameter, annotations: [MichelsonAnnotation]? = nil) {
8 | let argArray = [ arg.networkRepresentation ]
9 | super.init(
10 | networkRepresentation: [
11 | MichelineConstants.primitive: MichelineConstants.right,
12 | MichelineConstants.args: argArray
13 | ],
14 | annotations: annotations
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/OperationFees.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An object encapsulating the payment for an operation on the blockchain.
6 | public struct OperationFees {
7 | public let fee: Tez
8 | public let gasLimit: Int
9 | public let storageLimit: Int
10 |
11 | /// A zero-ed fees object.
12 | internal static let zeroFees = OperationFees(fee: .zeroBalance, gasLimit: 0, storageLimit: 0)
13 |
14 | public init(fee: Tez, gasLimit: Int, storageLimit: Int) {
15 | self.fee = fee
16 | self.gasLimit = gasLimit
17 | self.storageLimit = storageLimit
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/InjectOperationRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will inject an operation on the Tezos blockchain.
6 | public class InjectionRPC: RPC {
7 | /// - Parameter payload: A JSON encoded string that represents the signed payload to inject.
8 | public init(
9 | payload: String
10 | ) {
11 | let endpoint = "/injection/operation"
12 | super.init(
13 | endpoint: endpoint,
14 | headers: [Header.contentTypeApplicationJSON],
15 | responseAdapterClass: StringResponseAdapter.self,
16 | payload: payload
17 | )
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/PackDataRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020
2 |
3 | import Foundation
4 |
5 | /// An RPC which will pack data to a binary format.
6 | public class PackDataRPC: RPC {
7 | public init(
8 | payload: PackDataPayload
9 | ) {
10 | let endpoint = "/chains/main/blocks/head/helpers/scripts/pack_data"
11 | let payload = JSONUtils.jsonString(for: payload.dictionaryRepresentation)
12 |
13 | super.init(
14 | endpoint: endpoint,
15 | headers: [ Header.contentTypeApplicationJSON ],
16 | responseAdapterClass: PackDataResponseAdapter.self,
17 | payload: payload
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/JSONDictionaryResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Parse the given data as a JSON encoded string representing a dictionary.
6 | public class JSONDictionaryResponseAdapter: AbstractResponseAdapter<[String: Any]> {
7 | public override class func parse(input: Data) -> [String: Any]? {
8 | do {
9 | let json = try JSONSerialization.jsonObject(with: input)
10 | guard let typedJSON = json as? [String: Any] else {
11 | return nil
12 | }
13 |
14 | return typedJSON
15 | } catch {
16 | return nil
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/SigningServiceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class SigningServiceTests: XCTestCase {
7 | func testSign() {
8 | let mockTransaction = "abc123"
9 | let expectedSignature: [UInt8] = [1, 2, 3, 4]
10 |
11 | let fakeSignatureProvider =
12 | FakeSignatureProvider(signature: expectedSignature, publicKey: FakePublicKey.testPublicKey)
13 |
14 | guard let signature = SigningService.sign(mockTransaction, with: fakeSignatureProvider) else {
15 | XCTFail()
16 | return
17 | }
18 | XCTAssertEqual(signature, expectedSignature)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/AddressMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020.
2 |
3 | import Foundation
4 |
5 | /// A representation of a address parameter in Michelson.
6 | public class AddressMichelsonParameter: AbstractMichelsonParameter {
7 | /// Initialize a new address parameter.
8 | ///
9 | /// - Parameters:
10 | /// - address: An address.
11 | /// - annotations: An optional array of annotations to apply, default is none.
12 | public init(address: String, annotations: [MichelsonAnnotation]? = nil) {
13 | super.init(networkRepresentation: [MichelineConstants.string: address], annotations: annotations)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/MichelsonComparable.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A comparable Michelson type.
6 | public enum MichelsonComparable: String {
7 | case address
8 | case bool
9 | case bytes
10 | case int
11 | case keyHash = "key_hash"
12 | case mutez
13 | case nat
14 | case string
15 | case timestamp
16 |
17 | private enum JSON {
18 | public enum Keys {
19 | public static let prim = "prim"
20 | }
21 | }
22 |
23 | public static func networkRepresentation(for type: MichelsonComparable) -> [String: String] {
24 | return [ JSON.Keys.prim: type.rawValue ]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TezosKit/Common/TezosKitError.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | public indirect enum TezosKitError: Error, Equatable {
6 | case internalError
7 | case invalidURL
8 | case localForgingNotSupportedForOperation
9 | case operationError([OperationResponseInternalResultError])
10 | case preapplicationError(description: String)
11 | case rpcError(description: String)
12 | case signingError
13 | case transactionFormationFailure(underlyingError: TezosKitError)
14 | case unexpectedRequestFormat(description: String)
15 | case unexpectedResponse(description: String)
16 | case unknown(description: String?)
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/JSONArrayResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Parse the given data as a JSON encoded string representing an array of nested dictionaries.
6 | public class JSONArrayResponseAdapter: AbstractResponseAdapter<[[String: Any]]> {
7 | public override class func parse(input: Data) -> [[String: Any]]? {
8 | do {
9 | let json = try JSONSerialization.jsonObject(with: input)
10 | guard let typedJSON = json as? [[String: Any]] else {
11 | return nil
12 | }
13 | return typedJSON
14 | } catch {
15 | return nil
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/TimestampMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020.
2 |
3 | import Foundation
4 |
5 | /// A representation of a date parameter in Michelson.
6 | public class Timestamp: AbstractMichelsonParameter {
7 | public init(date: Date, annotations: [MichelsonAnnotation]? = nil) {
8 | let dateFormatter = DateFormatter()
9 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
10 | dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
11 |
12 | let string = dateFormatter.string(from: date)
13 |
14 | super.init(networkRepresentation: [MichelineConstants.string: string], annotations: annotations)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/PairMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | // A representation of a pair parameter in Michelson.
6 | public class PairMichelsonParameter: AbstractMichelsonParameter {
7 | public init(left: MichelsonParameter, right: MichelsonParameter, annotations: [MichelsonAnnotation]? = nil) {
8 | let argArray = [ left.networkRepresentation, right.networkRepresentation ]
9 | super.init(
10 | networkRepresentation: [
11 | MichelineConstants.primitive: MichelineConstants.pair,
12 | MichelineConstants.args: argArray
13 | ],
14 | annotations: annotations
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/BytesMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a bytes parameter in Michelson.
6 | public class BytesMichelsonParameter: AbstractMichelsonParameter {
7 | public convenience init?(bytes: [UInt8], annotations: [MichelsonAnnotation]? = nil) {
8 | guard let hex = CryptoUtils.binToHex(bytes) else {
9 | return nil
10 | }
11 | self.init(hex: hex, annotations: annotations)
12 | }
13 |
14 | public init(hex: Hex, annotations: [MichelsonAnnotation]? = nil) {
15 | super.init(networkRepresentation: [ MichelineConstants.bytes: hex ], annotations: annotations)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/SignatureMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020.
2 |
3 | import Foundation
4 |
5 | /// A representation of a signature parameter in Michelson.
6 | public class SignatureMichelsonParameter: AbstractMichelsonParameter {
7 | /// Initialize a new address parameter.
8 | ///
9 | /// - Parameters:
10 | /// - signature: A base58check encoded signature.
11 | /// - annotations: An optional array of annotations to apply, default is none.
12 | public init(signature: String, annotations: [MichelsonAnnotation]? = nil) {
13 | super.init(networkRepresentation: [MichelineConstants.string: signature], annotations: annotations)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/TezosKit/Common/Models/KeyChainWallet.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020.
2 |
3 | import Foundation
4 |
5 | /// A wallet which stores keys in a device's keychain.
6 | public class KeyChainWallet: DeviceWallet {
7 | /// Labels for keys in the Keychain.
8 | private enum KeyLabels {
9 | public static let `public` = "TezosKitKeyChain.public"
10 | public static let `private` = "TezosKitKeyChain.private"
11 | }
12 |
13 | /// - Parameter prompt: A prompt to use when asking the wallet to sign bytes.
14 | public init?(prompt: String) {
15 | super.init(prompt: prompt, token: .keychain, publicKeyLabel: KeyLabels.public, privateKeyLabel: KeyLabels.private)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/RunOperationRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import XCTest
4 |
5 | @testable import TezosKit
6 |
7 | class RunOperationRPCTest: XCTestCase {
8 | public func testForgeOperationRPC() {
9 | let rpc = RunOperationRPC(runOperationPayload: .testRunOperationPayload)
10 |
11 | let expectedEndpoint =
12 | "/chains/main/blocks/head/helpers/scripts/run_operation"
13 | let expectedPayload =
14 | JSONUtils.jsonString(for: RunOperationPayload.testRunOperationPayload.dictionaryRepresentation)
15 |
16 | XCTAssertEqual(rpc.endpoint, expectedEndpoint)
17 | XCTAssertEqual(rpc.payload, expectedPayload)
18 | XCTAssertTrue(rpc.isPOSTRequest)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetAddressManagerKeyRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | //
5 | // GetAddressManagerKeyRPCTest.swift
6 | // TezosKitTests
7 | //
8 | // Created by Keefer Taylor on 10/26/18.
9 | // Copyright © 2018 Keefer Taylor. All rights reserved.
10 | //
11 | import XCTest
12 |
13 | class GetAddressManagerKeyRPCTest: XCTestCase {
14 | public func testGetAddressManagerKeyRPC() {
15 | let address = "abc123"
16 | let rpc = GetAddressManagerKeyRPC(address: address)
17 |
18 | XCTAssertEqual(rpc.endpoint, "/chains/main/blocks/head/context/contracts/" + address + "/manager_key")
19 | XCTAssertNil(rpc.payload)
20 | XCTAssertFalse(rpc.isPOSTRequest)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/ChainIDMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a chain ID parameter in Michelson.
6 | public class ChainIDMichelsonParameter: AbstractMichelsonParameter {
7 | public convenience init?(chainIDBytes: [UInt8], annotations: [MichelsonAnnotation]? = nil) {
8 | guard let hex = CryptoUtils.binToHex(chainIDBytes) else {
9 | return nil
10 | }
11 | self.init(chainIDHex: hex, annotations: annotations)
12 | }
13 |
14 | public init(chainIDHex: Hex, annotations: [MichelsonAnnotation]? = nil) {
15 | super.init(networkRepresentation: [ MichelineConstants.bytes: chainIDHex ], annotations: annotations)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/KeyHashMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a key hash parameter in Michelson.
6 | public class KeyHashMichelsonParameter: AbstractMichelsonParameter {
7 | public convenience init?(keyHashBytes: [UInt8], annotations: [MichelsonAnnotation]? = nil) {
8 | guard let hex = CryptoUtils.binToHex(keyHashBytes) else {
9 | return nil
10 | }
11 | self.init(keyHashHex: hex, annotations: annotations)
12 | }
13 |
14 | public init(keyHashHex: Hex, annotations: [MichelsonAnnotation]? = nil) {
15 | super.init(networkRepresentation: [ MichelineConstants.bytes: keyHashHex ], annotations: annotations)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/RunOperationRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// An RPC that will run an operation.
6 | public class RunOperationRPC: RPC {
7 | /// - Parameter runOperationPayload: A payload containing an operation to run.
8 | public init(runOperationPayload: RunOperationPayload) {
9 | let endpoint = "/chains/main/blocks/head/helpers/scripts/run_operation"
10 | let jsonPayload = JSONUtils.jsonString(for: runOperationPayload.dictionaryRepresentation)
11 | super.init(
12 | endpoint: endpoint,
13 | headers: [Header.contentTypeApplicationJSON],
14 | responseAdapterClass: SimulationResultResponseAdapter.self,
15 | payload: jsonPayload
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/Operation.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Protocol representing all operations. Operations are first class representations of JSON object which can be forged
6 | /// pre-applied / injected on the Tezos Blockchain.
7 | public protocol Operation: NSMutableCopying {
8 | /// The kind of the operation.
9 | var kind: OperationKind { get }
10 |
11 | /// Whether the given operation requires the account to be revealed.
12 | var requiresReveal: Bool { get }
13 |
14 | /// Fees associated with the operation.
15 | var operationFees: OperationFees { get set }
16 |
17 | /// Retrieve a dictionary representing the operation.
18 | var dictionaryRepresentation: [String: Any] { get }
19 | }
20 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetBigMapValueByIDRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import BigInt
4 | import Foundation
5 |
6 | /// An RPC that will retrieve the value of a big map for the given key.
7 | public class GetBigMapValueByIDRPC: RPC<[String: Any]> {
8 | /// - Parameters:
9 | /// - address: The address of a smart contract with a big map.
10 | /// - key: The key in the big map to look up.
11 | /// - type: The michelson type of the key.
12 | public init(bigMapID: BigInt, expression: String) {
13 | let endpoint = "chains/main/blocks/head/context/big_maps/" + String(bigMapID) + "/" + expression
14 |
15 | super.init(
16 | endpoint: endpoint,
17 | responseAdapterClass: JSONDictionaryResponseAdapter.self
18 | )
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/ForgeOperationRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | @testable import TezosKit
4 | import XCTest
5 |
6 | class ForgeOperationRPCTest: XCTestCase {
7 | public func testForgeOperationRPC() {
8 | let rpc = ForgeOperationRPC(operationPayload: .testOperationPayload, operationMetadata: .testOperationMetadata)
9 |
10 | let expectedEndpoint =
11 | "/chains/main/blocks/" + OperationMetadata.testOperationMetadata.branch + "/helpers/forge/operations"
12 | let expectedPayload = JSONUtils.jsonString(for: OperationPayload.testOperationPayload.dictionaryRepresentation)
13 |
14 | XCTAssertEqual(rpc.endpoint, expectedEndpoint)
15 | XCTAssertEqual(rpc.payload, expectedPayload)
16 | XCTAssertTrue(rpc.isPOSTRequest)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/OperationWithCounter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// An operation which wraps an operation with an address counter for that operation.
6 | public struct OperationWithCounter {
7 | /// The internal operation.
8 | internal let operation: Operation
9 |
10 | /// The address counter for the internal operation.
11 | internal let counter: Int
12 |
13 | public var dictionaryRepresentation: [String: Any] {
14 | var operationDict = operation.dictionaryRepresentation
15 | operationDict["counter"] = String(counter)
16 | return operationDict
17 | }
18 |
19 | public init(operation: Operation, counter: Int) {
20 | self.operation = operation
21 | self.counter = counter
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/PackDataResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class PackDataResponseAdapterTest: XCTestCase {
7 | public func testParsePackedData() {
8 | let expected = "exprv6UsC1sN3Fk2XfgcJCL8NCerP5rCGy1PRESZAqr7L2JdzX55EN"
9 | let response = [
10 | "packed": "050a000000160000b2e19a9e74440d86c59f13dab8a18ff873e889ea",
11 | "gas": "799901"
12 | ]
13 |
14 | guard
15 | let jsonResponse = JSONUtils.jsonString(for: response),
16 | let data = jsonResponse.data(using: .utf8)
17 | else {
18 | XCTFail()
19 | return
20 | }
21 |
22 | let result = PackDataResponseAdapter.parse(input: data)
23 |
24 | XCTAssertEqual(result, expected)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Extensions/PromiseKit/Common/NetworkClient+Promises.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 | import PromiseKit
5 |
6 | /// Extension of NetworkClient which provides Promise based functionality.
7 | extension NetworkClient {
8 | /// Send an RPC and return the result as a promise.
9 | /// - Parameters:
10 | /// - rpc: The RPC to send.
11 | /// - Returns: A promise which will resolve to the result of the RPC.
12 | public func send(_ rpc: RPC) -> Promise {
13 | return Promise { seal in
14 | send(rpc) { result in
15 | switch result {
16 | case .success(let data):
17 | seal.fulfill(data)
18 | case .failure(let error):
19 | seal.reject(error)
20 | }
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/GetBigMapValueRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class GetBigMapValueRPCTest: XCTestCase {
7 | func testGetBigMapValueRPC() {
8 | let michelsonAddress = StringMichelsonParameter(string: .testAddress)
9 | let rpc = GetBigMapValueRPC(address: .testAddress, key: michelsonAddress, type: .address)
10 |
11 | let expectedEndpoint = "/chains/main/blocks/head/context/contracts/\(String.testAddress)/big_map_get"
12 | let expectedPayload = "{\"key\":{\"string\":\"\(String.testAddress)\"},\"type\":{\"prim\":\"address\"}}"
13 |
14 | XCTAssertEqual(rpc.endpoint, expectedEndpoint)
15 | XCTAssertEqual(rpc.payload, Helpers.orderJSONString(expectedPayload))
16 | XCTAssertTrue(rpc.isPOSTRequest)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/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 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Tests/IntegrationTests/TezosKit/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 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/TezosKit/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 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/OperationMetadata.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// A lightweight property bag containing data about the current chain state in order to forge / preapply / inject
6 | /// operations on the Tezos blockchain.
7 | public struct OperationMetadata {
8 | /// The ID of the chain being operated on.
9 | public let chainID: String
10 |
11 | /// The hash of the head of the chain being operated on.
12 | public let branch: String
13 |
14 | /// The hash of the protocol being operated on.
15 | public let `protocol`: String
16 |
17 | /// The counter for the address being operated on.
18 | public let addressCounter: Int
19 |
20 | /// The base58encoded public key for the address, or nil if the key is not yet revealed.
21 | public let key: String?
22 | }
23 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ForgeOperationRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will forge an operation.
6 | public class ForgeOperationRPC: RPC {
7 | /// - Parameters:
8 | /// - operationPayload: A payload to forge.
9 | /// - operationMetadata: Metadata about the operation.
10 | public init(
11 | operationPayload: OperationPayload,
12 | operationMetadata: OperationMetadata
13 | ) {
14 | let endpoint =
15 | "/chains/main/blocks/" + operationMetadata.branch + "/helpers/forge/operations"
16 | let payload = JSONUtils.jsonString(for: operationPayload.dictionaryRepresentation)
17 | super.init(
18 | endpoint: endpoint,
19 | headers: [Header.contentTypeApplicationJSON],
20 | responseAdapterClass: StringResponseAdapter.self,
21 | payload: payload
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Examples/TezosKitExample.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 |
5 | let tezosNodeClient = TezosNodeClient()
6 | let wallet = Wallet()!
7 |
8 | tezosNodeClient.getHeadHash { headHash, error in
9 | guard let headHash = headHash,
10 | error == nil else {
11 | print("Couldn't fetch head :(")
12 | if let error = error {
13 | print("Error: \(error)")
14 | }
15 | return
16 | }
17 | print("The hash of the head block was \(headHash)")
18 | }
19 |
20 | tezosNodeClient.getBalance(wallet: wallet) { balance, error in
21 | guard let balance = balance,
22 | error == nil else {
23 | print("Couldn't fetch balance :(")
24 | if let error = error {
25 | print("Error: \(error)")
26 | }
27 | return
28 | }
29 | print("The balance of \(wallet.address) was \(balance.humanReadableRepresentation)")
30 | }
31 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/PreapplyOperationRPCTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class PreapplyOperationRPCTest: XCTestCase {
7 | func testPreapplyOperationRPC() {
8 | let rpc = PreapplyOperationRPC(
9 | signedProtocolOperationPayload: .testSignedProtocolOperationPayload,
10 | operationMetadata: .testOperationMetadata
11 | )
12 |
13 | let expectedEndpoint =
14 | "/chains/main/blocks/" + OperationMetadata.testOperationMetadata.branch + "/helpers/preapply/operations"
15 | let expectedPayloadDictionary =
16 | SignedProtocolOperationPayload.testSignedProtocolOperationPayload.dictionaryRepresentation
17 | let expectedPayload = JSONUtils.jsonString(for: expectedPayloadDictionary)
18 |
19 | XCTAssertEqual(rpc.endpoint, expectedEndpoint)
20 | XCTAssertEqual(rpc.payload, expectedPayload)
21 | XCTAssertTrue(rpc.isPOSTRequest)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Tests/Common/TestHelpers.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 | import TezosKit
5 |
6 | // swiftlint:disable force_cast
7 | // swiftlint:disable force_try
8 |
9 | /// Common helpers for all TezosKit unit tests.
10 | public enum Helpers {
11 | /// Fix the expected input to the expected output of Swift's JSON serializer.
12 | ///
13 | /// Expected output is taken from the human readable version of the JSON serialization format. Swift outputs JSON
14 | /// keys either (1) non-deterministically or (2) ordered by key. This function re-orders the expected outputs of a
15 | /// input JSON string by key so that asserts can work properly.
16 | public static func orderJSONString(_ expected: String) -> String {
17 | let data = expected.data(using: .utf8)!
18 | let dictionary = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
19 | return JSONUtils.jsonString(for: dictionary)!
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/RPC/ConseilQueryRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | /// Generic RPC to query Conseil.
6 | public class ConseilQueryRPC: RPC {
7 | /// - Parameters:
8 | /// - query: A query to send to Conseil
9 | /// - entity: The entity to query.
10 | /// - responseAdapterClass: The class of the response adapter which will take bytes received from the request and
11 | /// transform them into a specific type.
12 | public init(
13 | query: [String: Any],
14 | entity: ConseilEntity,
15 | responseAdapterClass: AbstractResponseAdapter.Type
16 | ) {
17 | let endpoint = "\(entity.rawValue)"
18 | let headers = [
19 | Header.contentTypeApplicationJSON
20 | ]
21 |
22 | super.init(
23 | endpoint: endpoint,
24 | headers: headers,
25 | responseAdapterClass: responseAdapterClass,
26 | payload: JSONUtils.jsonString(for: query)
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # TezosKit Documentation
2 |
3 | ## Contents
4 |
5 | ### Getting Started
6 | - [Working with the Tezos Node](TezosNode.md): Instructions to work with a TezosNode
7 | - [Working with Conseil Service](Conseil.md): Instructions to work with Conseil
8 | - [Planned Work](FutureWork.md): Planned work for the future
9 | - [Testing](Testing.md): TezosKit unit and integration testing philosophy and instructions
10 | - [Advanced Functionality](AdvancedFunctionality.md): Instructions to use advanced Functions in TezosKit
11 |
12 | ### Advanced Topics
13 | - [Michelson](Michelson.md): Information about working with Michelson in TezosKit.
14 | - [Fees](Fees.md): Information about how TezosKit assigns fees
15 | - [Networking](Networking.md): Information about Networking in TezosKit
16 | - [Operations](Operations.md): Information about Operations in TezosKit
17 | - [Promises / PromiseKit](PromiseKit.md): About TezosKit's [PromiseKit](https://github.com/mxcl/PromiseKit) integration
18 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/RevealOperationTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class RevealOperationTest: XCTestCase {
7 | public func testDictionaryRepresentation() {
8 | let publicKey = FakePublicKey.testPublicKey
9 |
10 | guard case let .success(operation) = OperationFactory.testFactory.revealOperation(
11 | from: publicKey.publicKeyHash,
12 | publicKey: publicKey,
13 | operationFeePolicy: .default,
14 | signatureProvider: FakeSignatureProvider.testSignatureProvider
15 | ) else {
16 | XCTFail()
17 | return
18 | }
19 | let dictionary = operation.dictionaryRepresentation
20 |
21 | XCTAssertNotNil(dictionary["source"])
22 | XCTAssertEqual(dictionary["source"] as? String, publicKey.publicKeyHash)
23 |
24 | XCTAssertNotNil(dictionary["public_key"])
25 | XCTAssertEqual(dictionary["public_key"] as? String, publicKey.base58CheckRepresentation)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/PreapplyOperationRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An RPC which will pre-apply an operation.
6 | public class PreapplyOperationRPC: RPC<[[String: Any]]> {
7 | /// - Parameters:
8 | /// - signedProtocolOperationPayload: A payload to send with the operation.
9 | /// - operationMetadata: Metadata about the operation to be pre-applied.
10 | public init(
11 | signedProtocolOperationPayload: SignedProtocolOperationPayload,
12 | operationMetadata: OperationMetadata
13 | ) {
14 | let endpoint = "/chains/main/blocks/" + operationMetadata.branch + "/helpers/preapply/operations"
15 | let payload = JSONUtils.jsonString(for: signedProtocolOperationPayload.dictionaryRepresentation)
16 | super.init(
17 | endpoint: endpoint,
18 | headers: [Header.contentTypeApplicationJSON],
19 | responseAdapterClass: JSONArrayResponseAdapter.self,
20 | payload: payload
21 | )
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/StringResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Parse the given data as a string.
6 | ///
7 | /// Note that the API returns strings enclosed inside of whitespace, newlines and double quotes.
8 | /// These characters are stripped by this adapter.
9 | public class StringResponseAdapter: AbstractResponseAdapter {
10 | public override class func parse(input: Data) -> String? {
11 | guard
12 | let decodedString = String(data: input, encoding: .utf8)
13 | else {
14 | return nil
15 | }
16 |
17 | let characterSet = CharacterSet(charactersIn: "\"").union(.whitespacesAndNewlines)
18 | let normalizedString = decodedString.trimmingCharacters(in: characterSet)
19 | // RPC API will just pass through `null` when response is not found.
20 | guard normalizedString != "null" else {
21 | return nil
22 | }
23 |
24 | return normalizedString
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/RPC/GetReceivedTransactions.RPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | /// An RPC which fetches received transactions for an account.
4 | public class GetReceivedTransactionsRPC: ConseilQueryRPC<[Transaction]> {
5 | /// - Parameters:
6 | /// - account: The account to query.
7 | /// - limit: The number of items to return.
8 | public init(account: String, limit: Int) {
9 | let predicates: [ConseilPredicate] = [
10 | ConseilQuery.Predicates.predicateWith(field: "kind", set: ["transaction"]),
11 | ConseilQuery.Predicates.predicateWith(field: "destination", set: [account])
12 | ]
13 | let orderBy: ConseilOrderBy = ConseilQuery.OrderBy.orderBy(field: "timestamp")
14 | let query: [String: Any] = ConseilQuery.query(predicates: predicates, orderBy: orderBy, limit: limit)
15 |
16 | super.init(
17 | query: query,
18 | entity: .operation,
19 | responseAdapterClass: TransactionsResponseAdapter.self
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docs/AdvancedFunctionality.md:
--------------------------------------------------------------------------------
1 | # Advanced Functionality
2 | TezosKit provides advanced functionality and extensibility out of the box.
3 |
4 | ## Custom RPCs and Operations
5 | Users can create their own RPCs and Operations to support operations on the chain which TezosKit does not provide support for.
6 |
7 | See:
8 | * [RPC Documentation](Networking.md)
9 | * [Operation Documentation](Operations.md)
10 |
11 | ## Preapplication
12 |
13 | Every operation in TezosKit is pre-applied before it is injected in the block chain to catch errors.
14 |
15 | A future update of TezosKit will make this functionality optional.
16 |
17 | ## Local Operation Forging
18 | Forging of operations is currently done remotely.
19 |
20 | A future update of TezosKit will make this functionality available locally and provide a toggle to change functionality.
21 |
22 | ## Fees
23 | Users can set custom fees on operations. See the [Fees Documentation](Fees.md).
24 |
25 | A future update of TezosKit will provide gas estimation.
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Services/InjectionService.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | public class InjectionService {
6 | /// The network client.
7 | private let networkClient: NetworkClient
8 |
9 | public init(networkClient: NetworkClient) {
10 | self.networkClient = networkClient
11 | }
12 |
13 | /// Inject the given hex into the remote node.
14 | ///
15 | /// - Parameters:
16 | /// - payload: A hex payload to inject.
17 | /// - completion: A completion block that will be called with either an operation hash or an error.
18 | public func inject(payload: Hex, completion: @escaping (Result) -> Void) {
19 | let injectRPC = InjectionRPC(payload: payload)
20 | networkClient.send(injectRPC) { result in
21 | switch result {
22 | case .failure(let txError):
23 | completion(.failure(txError))
24 | case .success(let txHash):
25 | completion(.success(txHash))
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/RPC/GetSentTransactionsRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | /// An RPC which fetches sent transactions from an account.
4 | public class GetSentTransactionsRPC: ConseilQueryRPC<[Transaction]> {
5 | /// - Parameters:
6 | /// - account: The account to query.
7 | /// - limit: The number of items to return.
8 | public init(account: String, limit: Int) {
9 | let predicates = [
10 | ConseilQuery.Predicates.predicateWith(field: "kind", operation: .in, set: ["transaction", "delegation", "origination", "reveal", "activate_account"], inverse: false),
11 | ConseilQuery.Predicates.predicateWith(field: "source", set: [account])
12 | ]
13 | let orderBy = ConseilQuery.OrderBy.orderBy(field: "timestamp")
14 | let query = ConseilQuery.query(predicates: predicates, orderBy: orderBy, limit: limit)
15 |
16 | super.init(
17 | query: query,
18 | entity: .operation,
19 | responseAdapterClass: TransactionsResponseAdapter.self
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/KeyMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A representation of a key parameter in Michelson.
6 | public class KeyMichelsonParameter: AbstractMichelsonParameter {
7 | /// Initialize a new parameter using a secret key.
8 | public convenience init(secretKey: SecretKey, annotations: [MichelsonAnnotation]? = nil) {
9 | self.init(string: secretKey.base58CheckRepresentation, annotations: annotations)
10 | }
11 |
12 | /// Initialize a new parameter using a public key.
13 | public convenience init(publicKey: PublicKeyProtocol, annotations: [MichelsonAnnotation]? = nil) {
14 | self.init(string: publicKey.base58CheckRepresentation, annotations: annotations)
15 | }
16 |
17 | /// Private initializer which receives a base58check representation of a key.
18 | private init(string: String, annotations: [MichelsonAnnotation]? = nil) {
19 | super.init(networkRepresentation: [MichelineConstants.string: string], annotations: annotations)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/PackDataResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020
2 |
3 | import Base58Swift
4 | import Foundation
5 | import Sodium
6 |
7 | /// Parse a give response to a Base58check encoded set of packed data.
8 | public class PackDataResponseAdapter: AbstractResponseAdapter {
9 | public override class func parse(input: Data) -> String? {
10 | guard
11 | let dictionary = JSONDictionaryResponseAdapter.parse(input: input),
12 | let packedHex = dictionary["packed"] as? String,
13 | let packedBinary = CryptoUtils.hexToBin(packedHex)
14 | else {
15 | return nil
16 | }
17 |
18 | // Prefix and Base58Check encode.
19 | // TODO(keefertaylor): Push this down into the crypto library.
20 | let hashed = Sodium.shared.genericHash.hash(message: packedBinary, outputLength: 32)!
21 | let prefix: [UInt8] = [13, 44, 64, 27] // expr
22 | let prefixedPackedBinary = prefix + hashed
23 |
24 | return Base58.base58CheckEncode(prefixedPackedBinary)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/RPC/GetOriginatedContractsRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | /// An RPC which fetches originated contracts for an account.
4 | public class GetOriginatedContractsRPC: ConseilQueryRPC<[[String: Any]]> {
5 | /// - Parameters:
6 | /// - account: The account to query.
7 | /// - limit: The number of items to return.
8 | public init(account: String, limit: Int) {
9 | let predicates: [ConseilPredicate] = [
10 | ConseilQuery.Predicates.predicateWith(field: "source", set: [account]),
11 | ConseilQuery.Predicates.predicateWith(field: "kind", set: ["origination"]),
12 | ConseilQuery.Predicates.predicateWith(field: "status", set: ["applied"])
13 | ]
14 | let orderBy: ConseilOrderBy = ConseilQuery.OrderBy.orderBy(field: "block_level")
15 | let query: [String: Any] = ConseilQuery.query(predicates: predicates, orderBy: orderBy, limit: limit)
16 |
17 | super.init(
18 | query: query,
19 | entity: .operation,
20 | responseAdapterClass: JSONArrayResponseAdapter.self
21 | )
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/MichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// String constants used in Micheline param JSON encoding.
6 | internal enum MichelineConstants {
7 | public static let annotations = "annots"
8 | public static let args = "args"
9 | public static let primitive = "prim"
10 | public static let bytes = "bytes"
11 | public static let int = "int"
12 | public static let left = "Left"
13 | public static let `false` = "False"
14 | public static let none = "None"
15 | public static let pair = "Pair"
16 | public static let right = "Right"
17 | public static let some = "Some"
18 | public static let string = "string"
19 | public static let `true` = "True"
20 | public static let unit = "Unit"
21 | }
22 |
23 | /// An abstract representation of a Michelson param.
24 | ///
25 | /// - SeeAlso: https://tezos.gitlab.io/master/whitedoc/michelson.html
26 | public protocol MichelsonParameter {
27 | /// A dictionary representing the paramater as a JSON object.
28 | var networkRepresentation: JSONCodable { get }
29 | }
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | cobertura.xml
2 |
3 | # Created by http://www.gitignore.io
4 |
5 | ### OSX ###
6 | .DS_Store
7 | .AppleDouble
8 | .LSOverride
9 |
10 | # Icon must end with two \r
11 | Icon
12 |
13 | # Thumbnails
14 | ._*
15 |
16 | # Files that might appear on external disk
17 | .Spotlight-V100
18 | .Trashes
19 |
20 | # Directories potentially created on remote AFP share
21 | .AppleDB
22 | .AppleDesktop
23 | Network Trash Folder
24 | Temporary Items
25 | .apdisk
26 |
27 | ### Objective-C ###
28 | # Xcode
29 | #
30 | build/
31 | *.pbxuser
32 | !default.pbxuser
33 | *.mode1v3
34 | !default.mode1v3
35 | *.mode2v3
36 | !default.mode2v3
37 | *.perspectivev3
38 | !default.perspectivev3
39 | xcuserdata
40 | *.xccheckout
41 | *.moved-aside
42 | DerivedData
43 | *.hmap
44 | *.ipa
45 | *.xcuserstate
46 |
47 | # CocoaPods
48 | Pods/
49 |
50 | # Carthage
51 | Carthage
52 | Romefile
53 |
54 | #Fastlane
55 | fastlane/report.xml
56 | fastlane/Preview.html
57 | fastlane/screenshots
58 | fastlane/test_output
59 | screenshots
60 |
61 | #build output
62 | /outputs
63 |
64 | # Playgrounds
65 | timeline.xctimeline
66 | playground.xcworkspace
67 |
--------------------------------------------------------------------------------
/TezosKit/Utils/JailbreakUtils.swift:
--------------------------------------------------------------------------------
1 | #if canImport(DTTJailbreakDetection)
2 | import DTTJailbreakDetection
3 | #endif
4 |
5 | /// Utilities for working with Jailbroken devices.
6 | public enum JailbreakUtils {
7 | /// Crash if the host device is jailbroken.
8 | ///
9 | /// Jailbroken device have no access control on root files, hence rendering the sandbox mode useless. This potentially exposes keys and can lead to loss
10 | /// of funds.
11 | public static func crashIfJailbroken() {
12 | if JailbreakUtils.isJailBroken() {
13 | fatalError(
14 | """
15 | Jailbreak detected on host device. Using TezosKit on a jailbroken device may expose your keys and lead to loss
16 | of funds.
17 | """
18 | )
19 | }
20 | }
21 |
22 | /// Return whether or not the host device is jailbroken.
23 | ///
24 | /// On non-iOS builds, this function will always return `false`.
25 | public static func isJailBroken() -> Bool {
26 | #if canImport(DTTJailbreakDetection)
27 | return DTTJailbreakDetection.isJailbroken()
28 | #else
29 | return false
30 | #endif
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Keefer Taylor
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.
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/TransactionOperationTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | // swiftlint:disable force_cast
7 |
8 | class TransactionOperationTest: XCTestCase {
9 | public func testTransation() {
10 | let balance = Tez(3.50)
11 | guard case let .success(operation) = OperationFactory.testFactory.transactionOperation(
12 | amount: balance,
13 | source: .testAddress,
14 | destination: .testDestinationAddress,
15 | operationFeePolicy: .default,
16 | signatureProvider: FakeSignatureProvider.testSignatureProvider
17 | ) else {
18 | XCTFail()
19 | return
20 | }
21 | let dictionary = operation.dictionaryRepresentation
22 |
23 | XCTAssertNotNil(dictionary["source"])
24 | XCTAssertEqual(dictionary["source"] as? String, .testAddress)
25 |
26 | XCTAssertNotNil(dictionary["destination"])
27 | XCTAssertEqual(dictionary["destination"] as? String, .testDestinationAddress)
28 |
29 | XCTAssertNotNil(dictionary["amount"])
30 | XCTAssertEqual(dictionary["amount"] as? String, balance.rpcRepresentation)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/Payload/SignedProtocolOperationPayload.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// An operation payload that is signed and bound to a specific protocol.
6 | public struct SignedProtocolOperationPayload {
7 | /// Retrieve a dictionary representation of the payload.
8 | public var dictionaryRepresentation: [[String: Any]] {
9 | var payload = signedOperationPayload.dictionaryRepresentation
10 | payload["protocol"] = `protocol`
11 | return [payload]
12 | }
13 |
14 | /// An operation payload and associated signature.
15 | private let signedOperationPayload: SignedOperationPayload
16 |
17 | /// The hash of the protocol for the payload.
18 | private let `protocol`: String
19 |
20 | /// - Parameters:
21 | /// - signedOperationPayload: An operation payload and associated signature.
22 | /// - operationMetadata: Metadata for the operation.
23 | public init(signedOperationPayload: SignedOperationPayload, operationMetadata: OperationMetadata) {
24 | self.signedOperationPayload = signedOperationPayload
25 | self.protocol = operationMetadata.`protocol`
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/Payload/RunOperationPayload.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A payload for an running an operation.
6 | public struct RunOperationPayload {
7 | private enum JSON {
8 | public static let chainID = "chain_id"
9 | public static let operation = "operation"
10 | }
11 |
12 | /// The operation payload.
13 | private let signedOperationPayload: SignedOperationPayload
14 |
15 | /// The Chain ID to run on.
16 | private let chainID: String
17 |
18 | /// Retrieve a dictionary representation of the payload.
19 | public var dictionaryRepresentation: [String: Any] {
20 | return [
21 | JSON.operation: signedOperationPayload.dictionaryRepresentation,
22 | JSON.chainID: chainID
23 | ]
24 | }
25 |
26 | /// - Parameters:
27 | /// - signedOperationPayload: The `SignedOperationPayload`
28 | /// - operationMetadata: The `OperationMetadata`.
29 | public init(signedOperationPayload: SignedOperationPayload, operationMetadata: OperationMetadata) {
30 | self.signedOperationPayload = signedOperationPayload
31 | self.chainID = operationMetadata.chainID
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/RPC/GetRecievedSmartContractTransactionsRPC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GetRecievedSmartContractTransactionsRPC.swift
3 | // TezosKit
4 | //
5 | // Created by Simon Mcloughlin on 09/04/2020.
6 | //
7 |
8 | import Foundation
9 |
10 | /// An RPC which fetches received transactions for an account, that came from smart contracts. For example if an account reiceved some FA1.2 tokens
11 | public class GetReceivedSmartContractTransactionsRPC: ConseilQueryRPC<[Transaction]> {
12 | /// - Parameters:
13 | /// - account: The account to query.
14 | /// - limit: The number of items to return.
15 | public init(account: String, limit: Int) {
16 | let predicates: [ConseilPredicate] = [
17 | ConseilQuery.Predicates.predicateWith(field: "parameters", operation: .like, set: [account], inverse: false)
18 | ]
19 | let orderBy: ConseilOrderBy = ConseilQuery.OrderBy.orderBy(field: "timestamp")
20 | let query: [String: Any] = ConseilQuery.query(predicates: predicates, orderBy: orderBy, limit: limit)
21 |
22 | super.init(
23 | query: query,
24 | entity: .operation,
25 | responseAdapterClass: TransactionsResponseAdapter.self
26 | )
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/Payload/SignedOperationPayload.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A payload for an operation and associated signature.
6 | public struct SignedOperationPayload {
7 | /// The operation payload.
8 | private let operationPayload: OperationPayload
9 |
10 | /// The signature for the operation payload.
11 | private let base58Signature: String
12 |
13 | /// Retrieve a dictionary representation of the payload.
14 | public var dictionaryRepresentation: [String: Any] {
15 | var payload = operationPayload.dictionaryRepresentation
16 | payload["signature"] = base58Signature
17 | return payload
18 | }
19 |
20 | /// - Parameters:
21 | /// - operationPayload: The operation payload.
22 | /// - signature: The signature for the operation payload.
23 | public init?(operationPayload: OperationPayload, signature: [UInt8], signingCurve: EllipticalCurve) {
24 | self.operationPayload = operationPayload
25 | guard let base58Signature = CryptoUtils.base58(signature: signature, signingCurve: signingCurve) else {
26 | return nil
27 | }
28 | self.base58Signature = base58Signature
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/TezosKit/Common/Services/Logger.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Log levels for TezosKit.
4 | /// Implicitly ordered from most to least verbose.
5 | public enum LogLevel: Int {
6 | case debug = 0
7 | case info = 1
8 | case none = 2
9 | }
10 |
11 | /// Provides logging functionality in TezosKit.
12 | public class Logger {
13 | /// A shared singleton logging instance.
14 | public static let shared = Logger(logLevel: .none)
15 |
16 | /// The level at which messages will be logged.
17 | public var logLevel: LogLevel
18 |
19 | /// Please use the shared singleton rather than instantiating this class directly.
20 | ///
21 | /// - Parameter logLevel: The level of logs this logger will show.
22 | private init(logLevel: LogLevel) {
23 | self.logLevel = logLevel
24 | }
25 |
26 | /// Log a message.
27 | ///
28 | /// - Parameters:
29 | /// - message: The message to log.
30 | /// - logLevel: The logLevel of the message.
31 | public func log(_ message: String, level: LogLevel) {
32 | // Only log if the log level of the Logger is less than the log level of the mssage
33 | if self.logLevel.rawValue <= level.rawValue {
34 | print(message)
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/TezosKit/Crypto/EllipticCurveKeyPair/README.md:
--------------------------------------------------------------------------------
1 | # Elliptic Curve Key Pair
2 |
3 | This library was imported from [agens-no/EllipticCurveKeyPair](https://github.com/agens-no/EllipticCurveKeyPair) on Feb 07, 2020. The library has received updates but was last released (via both GitHub Tags / Carthage and CocoaPods) in November 2017. While Carthage would allow consumption of a fork of the library, CocoaPods requires the owner to publish it. There have been regular updates to this repo, so publishing a new CocoaPods version / forking for Carthage feels less than useful.
4 |
5 | ## Imported Files
6 |
7 | This folder contains the contents of the `Sources/` folder. Headers of the files indicate the commit hash they were imported at.
8 |
9 | ## Tracking a new Release
10 |
11 | If a new version of EllipticCurveKeyPair is released, this library should move to consuming the code as a dependency. [Issue 48](https://github.com/agens-no/EllipticCurveKeyPair/issues/48) tracks this request.
12 |
13 | ## History
14 | ** Please update this if new version are ever imported. **
15 |
16 | - Feb 09, 2020: [Performed minor fixes and upgrades for Swift 5](https://github.com/keefertaylor/TezosKit/pull/164)
17 | - Feb 07, 2020: Initial import
18 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/AbstractMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// Models a swift dictionary as a JSON representation (Micheline) of a Michelson parameter.
6 | ///
7 | /// This abstract base class can be used to create Michelson parameters which TezosKit doesn't support.
8 | public class AbstractMichelsonParameter: MichelsonParameter {
9 | public let networkRepresentation: JSONCodable
10 |
11 | /// - Parameter networkRepresentation: A dictionary representation of the parameter which can be encoded to JSON.
12 | /// - Parameter annotations: Optional annotations
13 | public init(networkRepresentation: [String: Any], annotations: [MichelsonAnnotation]? = nil) {
14 | var annotationAugmentedDictionary = networkRepresentation
15 | if let annotations = annotations {
16 | annotationAugmentedDictionary[MichelineConstants.annotations] = annotations.map { $0.value }
17 | }
18 |
19 | self.networkRepresentation = annotationAugmentedDictionary
20 | }
21 |
22 | /// - Parameter networkRepresentation: An array representation of the parameter which can be encoded to JSON.
23 | public init(networkRepresentation: [Any]) {
24 | self.networkRepresentation = networkRepresentation
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TezosKit/Utils/MnemonicUtil.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import MnemonicKit
4 |
5 | /// A static utility wrapper which provides a facade for a mnemonic library.
6 | public enum MnemonicUtil {
7 | /// Generate a mnemonic.
8 | public static func generateMnemonic() -> String? {
9 | return Mnemonic.generateMnemonic(strength: 128)
10 | }
11 |
12 | /// Generate a seed string from a given mnemonic.
13 | ///
14 | /// - Parameters:
15 | /// - mnemonic: A BIP39 mnemonic phrase.
16 | /// - passphrase: An optional passphrase used for encryption.
17 | public static func seedString(from mnemonic: String, passphrase: String = "") -> String? {
18 | guard let rawSeedString =
19 | Mnemonic.deterministicSeedString(from: mnemonic, passphrase: passphrase) else {
20 | return nil
21 | }
22 | return String(rawSeedString[.. Bool {
31 | return Mnemonic.validate(mnemonic: mnemonic)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - file_length
3 | - function_parameter_count
4 | - function_body_length
5 | - nesting
6 | - type_body_length
7 | - xctfail_message
8 |
9 | opt_in_rules:
10 | - attributes
11 | - closure_end_indentation
12 | - closure_spacing
13 | - empty_count
14 | - empty_string
15 | - explicit_init
16 | - file_header
17 | - first_where
18 | # TODO: Fix force unwrapping in XCTests, then enable.
19 | # - force_unwrapping
20 | - implicitly_unwrapped_optional
21 | - literal_expression_end_indentation
22 | - multiline_arguments
23 | - multiline_arguments_brackets
24 | - multiline_literal_brackets
25 | - multiline_parameters
26 | - multiline_parameters_brackets
27 | - multiple_closures_with_trailing_closure
28 | - no_extension_access_modifier
29 | - number_separator
30 | - object_literal
31 | - operator_usage_whitespace
32 | - overridden_super_call
33 | - prohibited_super_call
34 | - redundant_nil_coalescing
35 | - sorted_imports
36 | - trailing_closure
37 | - vertical_parameter_alignment_on_call
38 | # TODO: Documentation.
39 |
40 | excluded:
41 | - Base58String/
42 | - Carthage/
43 | - Pods/
44 |
45 | identifier_name:
46 | min_length: 1
47 |
48 | file_header:
49 | required_pattern: Copyright Keefer Taylor, 201(8|9)
50 |
--------------------------------------------------------------------------------
/docs/PromiseKit.md:
--------------------------------------------------------------------------------
1 | # PromiseKit
2 |
3 | The core of TezosKit provides closure style callbacks.
4 |
5 | An extension of the library provides [PromiseKit](https://github.com/mxcl/PromiseKit) functionality. In CocoaPods, PromiseKit is split into a separate module. In Carthage, all code is delivered as a single module.
6 |
7 | ### Using Promises
8 |
9 | In general, any call you can make on a `TezosNodeClient` or `ConseilClient` can also be done with promises.
10 |
11 | For instance, the following closure style callbacks:
12 |
13 | ```swift
14 | let tezosNodeClient = TezosClient()
15 |
16 | // Closure completion handler
17 | tezosNodeClient.getBalance(address: "KT1BVAXZQUc4BGo3WTJ7UML6diVaEbe4bLZA") { result in
18 | switch result {
19 | case .success(let balance):
20 | print("The balance of the contract is \(balance.humanReadableRepresentation)")
21 | case .failure(let error):
22 | print("Error getting balance: \(error)")
23 | }
24 | }
25 | ```
26 |
27 | Are equivalent to:
28 |
29 | ```swift
30 | let tezosNodeClient = TezosClient()
31 |
32 | // PromiseKit Promises
33 | tezosNodeClient.getBalance(address: "KT1BVAXZQUc4BGo3WTJ7UML6diVaEbe4bLZA").done { balance in
34 | print("The balance of the contract is \(balance.humanReadableRepresentation)")
35 | } .catch { _ in
36 | print("Couldn't get balance.")
37 | }
38 | ```
39 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/RevealOperation.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An operation to reveal an address.
6 | ///
7 | /// - Note: TezosKit will automatically inject this operation when required for supported operations.
8 | public class RevealOperation: AbstractOperation {
9 | /// The public key for the address being revealed.
10 | private let publicKey: PublicKeyProtocol
11 |
12 | public override var dictionaryRepresentation: [String: Any] {
13 | var operation = super.dictionaryRepresentation
14 | operation["public_key"] = publicKey.base58CheckRepresentation
15 | return operation
16 | }
17 |
18 | /// Initialize a new reveal operation.
19 | ///
20 | /// - Parameters:
21 | /// - address: The address to reveal.
22 | /// - publicKey: The public key of the address to reveal.
23 | /// - operationFees: OperationFees for the transaction.
24 | public init(from address: Address, publicKey: PublicKeyProtocol, operationFees: OperationFees) {
25 | self.publicKey = publicKey
26 | super.init(source: address, kind: .reveal, operationFees: operationFees)
27 | }
28 |
29 | public override func mutableCopy(with zone: NSZone? = nil) -> Any {
30 | return RevealOperation(from: source, publicKey: publicKey, operationFees: operationFees)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/TezosKit/Crypto/Base58+TezosCrypto.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Base58Swift
4 | import Foundation
5 |
6 | /// Helper functions on Base58Swift.
7 | extension Base58 {
8 | /// Encode a Base58Check string from the given message and prefix.
9 | ///
10 | /// The returned address is a Base58 encoded String with the following format: [prefix][key][4 byte checksum]
11 | public static func encode(message: [UInt8], prefix: [UInt8]) -> String {
12 | let prefixedMessage = prefix + message
13 | return Base58.base58CheckEncode(prefixedMessage)
14 | }
15 |
16 | /// Decode a Base58Check string to bytes and verify that the bytes begin with the given prefix.
17 | ///
18 | /// - Parameters:
19 | /// - string: The Base58Check string to decode.
20 | /// - prefix: The expected prefix bytes after decoding.
21 | /// - Returns: The raw bytes without the given prefix if the string was valid Base58Check and had the expected prefix,
22 | /// otherwise, nil.
23 | public static func base58CheckDecodeWithPrefix(string: String, prefix: [UInt8]) -> [UInt8]? {
24 | guard
25 | let bytes = Base58.base58CheckDecode(string),
26 | bytes.prefix(prefix.count).elementsEqual(prefix)
27 | else {
28 | return nil
29 | }
30 | return Array(bytes.suffix(from: prefix.count))
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/Payload/OperationPayload.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// A payload that can be forged into operation bytes.
6 | public struct OperationPayload {
7 | /// An array of dictionaries representing operations.
8 | internal let operations: [OperationWithCounter]
9 |
10 | /// The hash of the head of the chain to apply the operation on.
11 | internal let branch: String
12 |
13 | /// Retrieve a dictionary representation of the payload.
14 | public var dictionaryRepresentation: [String: Any] {
15 | var contents: [[String: Any]] = []
16 | for operation in operations {
17 | contents.append(operation.dictionaryRepresentation)
18 | }
19 | return [
20 | "contents": contents,
21 | "branch": branch
22 | ]
23 | }
24 |
25 | /// Creates a operation payload from a list of operations.
26 | ///
27 | /// This initializer will automatically add reveal operations and set address counters properly.
28 | ///
29 | /// - Parameters:
30 | /// - operations: A list of operations to forge.
31 | /// - operationMetadata: Metadata about the operations.
32 | public init(
33 | operations: [OperationWithCounter],
34 | operationMetadata: OperationMetadata
35 | ) {
36 | self.operations = operations
37 | self.branch = operationMetadata.branch
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/TezosKit/Utils/JSONUtils.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// Marks an object as codable to JSON.
6 | public protocol JSONCodable {}
7 |
8 | /// Mark arrays and dictionaries as JSONCodable.
9 | extension Dictionary: JSONCodable where Key == String {}
10 | extension Array: JSONCodable {}
11 |
12 | /// A facade for a JSON parsing library.
13 | public enum JSONUtils {
14 | /// Returns a JSON encoded string representation of a given string.
15 | public static func jsonString(for string: String) -> String? {
16 | return "\"" + string + "\""
17 | }
18 |
19 | /// Return a JSON encoded string representation of a given integer.
20 | public static func jsonString(for int: Int) -> String? {
21 | return jsonString(for: String(int))
22 | }
23 |
24 | /// Return a JSON encoded sstring representation of the given codable.
25 | public static func jsonString(for object: JSONCodable) -> String? {
26 | do {
27 | var options: JSONSerialization.WritingOptions = []
28 | if #available(iOS 11.0, OSX 10.13, *) {
29 | options = [.sortedKeys]
30 | }
31 | let jsonData = try JSONSerialization.data(withJSONObject: object, options: options)
32 | guard let jsonPayload = String(data: jsonData, encoding: .utf8) else {
33 | return nil
34 | }
35 | return jsonPayload
36 | } catch {
37 | return nil
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/FeeEstimatorTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class FeeEstimatorTest: XCTestCase {
7 | func testEstimateFees() {
8 | let address = Address.testAddress
9 | let signatureProvider = FakeSignatureProvider.testSignatureProvider
10 | let operationFactory = OperationFactory.testFactory
11 | guard
12 | case let .success(operation) = operationFactory.delegateOperation(
13 | source: address,
14 | to: .testDestinationAddress,
15 | operationFeePolicy: .default,
16 | signatureProvider: signatureProvider
17 | )
18 | else {
19 | XCTFail()
20 | return
21 | }
22 |
23 | let feeEstimator = FeeEstimator(
24 | forgingService: .testForgingService,
25 | operationMetadataProvider: .testOperationMetadataProvider,
26 | simulationService: .testSimulationService
27 | )
28 |
29 | let completionExpectation = XCTestExpectation(description: "completion called")
30 |
31 | feeEstimator.estimate(operation: operation, address: address, signatureProvider: signatureProvider) { result in
32 | switch result {
33 | case .success:
34 | completionExpectation.fulfill()
35 | case .failure:
36 | XCTFail()
37 | }
38 | }
39 |
40 | wait(for: [completionExpectation], timeout: .expectationTimeout)
41 |
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/TezosKit/Crypto/Prefixes.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | /// Common prefixes used across Tezos Cryptography.
6 | public enum Prefix {
7 | public enum Watermark {
8 | public static let operation: [UInt8] = [ 3 ] // 03
9 | }
10 |
11 | public enum Keys {
12 | public enum Ed25519 {
13 | public static let `public`: [UInt8] = [13, 15, 37, 217] // edpk
14 | public static let secret: [UInt8] = [43, 246, 78, 7] // edsk
15 | public static let seed: [UInt8] = [13, 15, 58, 7] // edsk
16 | public static let signature: [UInt8] = [9, 245, 205, 134, 18] // edsig
17 | }
18 |
19 | public enum P256 {
20 | public static let secret: [UInt8] = [16, 81, 238, 189] // p2sk
21 | public static let `public`: [UInt8] = [3, 178, 139, 127] // p2pk
22 | public static let signature: [UInt8] = [54, 240, 44, 52] // p2sig
23 | }
24 |
25 | public enum Secp256k1 {
26 | public static let `public`: [UInt8] = [3, 254, 226, 86] // sppk
27 | public static let secret: [UInt8] = [17, 162, 224, 201] // spsk
28 | public static let signature: [UInt8] = [13, 115, 101, 19, 63] // spsig
29 | }
30 | }
31 |
32 | public enum Address {
33 | public static let tz1: [UInt8] = [6, 161, 159] // tz1
34 | public static let tz2: [UInt8] = [6, 161, 161] // tz2
35 | public static let tz3: [UInt8] = [6, 161, 164] // tz3
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/JSONArrayResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class JSONArrayResponseAdapterTest: XCTestCase {
7 | public func testParseArray() {
8 | let validJSONString = "[{\"a\": \"b\"}, {\"c\": \"d\"}, {\"e\": \"f\"}]"
9 | guard let validJSONData = validJSONString.data(using: .utf8),
10 | let parsedArray = JSONArrayResponseAdapter.parse(input: validJSONData) else {
11 | XCTFail()
12 | return
13 | }
14 |
15 | XCTAssertEqual(parsedArray.count, 3)
16 | XCTAssertNotNil(parsedArray[0])
17 | XCTAssertNotNil(parsedArray[1])
18 | XCTAssertNotNil(parsedArray[2])
19 | }
20 |
21 | public func testParseArrayWithDictionary() {
22 | let validJSONString = "{\"a\": \"b\", \"c\": { \"d\": \"e\" }}"
23 | guard let validJSONData = validJSONString.data(using: .utf8) else {
24 | XCTFail()
25 | return
26 | }
27 |
28 | let parsedArray = JSONArrayResponseAdapter.parse(input: validJSONData)
29 | XCTAssertNil(parsedArray)
30 | }
31 |
32 | public func testParseDictionaryWithInvalidJSON() {
33 | let validJSONString = "abc:[]123"
34 | guard let validJSONData = validJSONString.data(using: .utf8) else {
35 | XCTFail()
36 | return
37 | }
38 |
39 | let parsedArray = JSONArrayResponseAdapter.parse(input: validJSONData)
40 | XCTAssertNil(parsedArray)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/AbstractOperation.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An abstract super class representing an operation to perform on the blockchain. Common parameters across operations
6 | /// and default parameter values are provided by the abstract class's implementation.
7 | public class AbstractOperation: Operation {
8 | public let source: Address
9 | public let kind: OperationKind
10 | public var operationFees: OperationFees
11 |
12 | public var requiresReveal: Bool {
13 | switch kind {
14 | case .delegation, .transaction, .origination:
15 | return true
16 | case .reveal:
17 | return false
18 | }
19 | }
20 |
21 | public var dictionaryRepresentation: [String: Any] {
22 | var operation: [String: String] = [:]
23 | operation["kind"] = kind.rawValue
24 | operation["source"] = source
25 |
26 | operation["storage_limit"] = String(operationFees.storageLimit)
27 | operation["gas_limit"] = String(operationFees.gasLimit)
28 | operation["fee"] = operationFees.fee.rpcRepresentation
29 |
30 | return operation
31 | }
32 |
33 | public init(source: Address, kind: OperationKind, operationFees: OperationFees) {
34 | self.source = source
35 | self.kind = kind
36 | self.operationFees = operationFees
37 | }
38 | }
39 |
40 | extension AbstractOperation: NSMutableCopying {
41 | public func mutableCopy(with zone: NSZone? = nil) -> Any {
42 | return AbstractOperation(source: source, kind: kind, operationFees: operationFees)
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/GetBigMapValueRPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | private enum GetBigMapValueJSONConstants {
6 | public static let key = "key"
7 | public static let prim = "prim"
8 | public static let type = "type"
9 | }
10 |
11 | /// An RPC that will retrieve the value of a big map for the given key.
12 | public class GetBigMapValueRPC: RPC<[String: Any]> {
13 | /// - Parameters:
14 | /// - address: The address of a smart contract with a big map.
15 | /// - key: The key in the big map to look up.
16 | /// - type: The michelson type of the key.
17 | public init(address: Address, key: MichelsonParameter, type: MichelsonComparable) {
18 | let endpoint = "/chains/main/blocks/head/context/contracts/" + address + "/big_map_get"
19 |
20 | let payload = JSONUtils.jsonString(
21 | for: [
22 | GetBigMapValueJSONConstants.key: key.networkRepresentation,
23 | GetBigMapValueJSONConstants.type: MichelsonComparable.networkRepresentation(for: type)
24 | ]
25 | )
26 |
27 | // The TezosNodeClient returns a HTTP 415 error if this header is not included with the payload. This behavior is
28 | // unique to this RPC. Adding this header to other RPCs causes them to fail with HTTP 415. ¯\_(ツ)_/¯
29 | let headers = [
30 | Header.contentTypeApplicationJSON
31 | ]
32 |
33 | super.init(
34 | endpoint: endpoint,
35 | headers: headers,
36 | responseAdapterClass: JSONDictionaryResponseAdapter.self,
37 | payload: payload
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/TezosKit.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "TezosKit"
3 | s.version = "5.2.1"
4 | s.summary = "TezosKit provides a Swift based toolchain for interacting with the Tezos blockchain"
5 | s.description = <<-DESC
6 | TezosKit provides utilities for interacting with the Tezos Blockchain over an RPC API.
7 | DESC
8 |
9 | s.homepage = "https://github.com/keefertaylor/TezosKit"
10 | s.license = { :type => "MIT", :file => "LICENSE" }
11 | s.author = { "Keefer Taylor" => "keefer@keefertaylor.com" }
12 | s.source = { :git => "https://github.com/keefertaylor/TezosKit.git", :tag => "5.2.1" }
13 | s.source_files = "TezosKit/**/*.swift"
14 | s.swift_version = "5.1"
15 | s.ios.deployment_target = "10.0"
16 | s.osx.deployment_target = "10.14"
17 |
18 | s.source_files = [ "TezosKit/**/*.swift", "Extensions/PromiseKit/*.swift"]
19 | s.frameworks = 'Foundation', "Security"
20 |
21 | s.ios.deployment_target = '10.0'
22 | s.osx.deployment_target = '10.14'
23 |
24 | s.dependency "BigInt", "~> 5.0.0"
25 | s.dependency "MnemonicKit", "~> 1.3.12"
26 | s.dependency "PromiseKit", "~> 6.13.1"
27 | s.dependency "Base58Swift", "~> 2.1.10"
28 | s.dependency "CryptoSwift", "~> 1.3.0"
29 | s.dependency "Sodium", "~> 0.8.0"
30 | s.dependency "secp256k1.swift", "~> 0.1.4"
31 | s.ios.dependency "DTTJailbreakDetection", "~> 0.4.0"
32 |
33 | s.test_spec "Tests" do |test_spec|
34 | test_spec.source_files = ["Tests/Common/*.swift", "Tests/TezosKit/*.swift", "Tests/Extensions/PromiseKit/*.swift"]
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/Payload/PackDataPayload.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020
2 |
3 | import Foundation
4 |
5 | /// A payload that can be used to pack data.
6 | public struct PackDataPayload {
7 | // JSON keys and values
8 | private enum JSON {
9 | public enum Keys {
10 | public static let data = "data"
11 | public static let gas = "gas"
12 | public static let prim = "prim"
13 | public static let type = "type"
14 | }
15 | public enum Values {
16 | public static let gasAmount = "8000"
17 | }
18 | }
19 |
20 | // Data to place in the payload.
21 | private let michelsonParameter: MichelsonParameter
22 | private let michelsonComparable: MichelsonComparable
23 |
24 | /// Retrieve a dictionary representation of the payload.
25 | public var dictionaryRepresentation: [String: Any] {
26 | let dictionary: [String: Any] = [
27 | JSON.Keys.gas: JSON.Values.gasAmount,
28 | JSON.Keys.data: michelsonParameter.networkRepresentation,
29 | JSON.Keys.type: MichelsonComparable.networkRepresentation(for: michelsonComparable)
30 | ]
31 | return dictionary
32 | }
33 |
34 | /// Creates a pack data payload with the given inputs.
35 | ///
36 | /// - Parameters:
37 | /// - michelsonParameter: The parameter to pack.
38 | /// - michelsonComparable: The type of the parameter.
39 | public init(
40 | michelsonParameter: MichelsonParameter,
41 | michelsonComparable: MichelsonComparable
42 | ) {
43 | self.michelsonParameter = michelsonParameter
44 | self.michelsonComparable = michelsonComparable
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/StringResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class StringResponseAdapterTest: XCTestCase {
7 | public func testParseString() {
8 | let string = "String"
9 | guard let data = string.data(using: .utf8) else {
10 | XCTFail()
11 | return
12 | }
13 |
14 | let result = StringResponseAdapter.parse(input: data)
15 |
16 | XCTAssertEqual(result, string)
17 | }
18 |
19 | // Expect quotes to be stripped.
20 | public func testQuotedString() {
21 | let string = "String"
22 | let quotedString = "\"" + string + "\""
23 | guard let data = quotedString.data(using: .utf8) else {
24 | XCTFail()
25 | return
26 | }
27 |
28 | let result = StringResponseAdapter.parse(input: data)
29 |
30 | XCTAssertEqual(result, string)
31 | }
32 |
33 | // Expect whitespace to be stripped.
34 | public func testWhitespaceString() {
35 | let string = "String"
36 | let quotedString = " " + string + "\n \n"
37 | guard let data = quotedString.data(using: .utf8) else {
38 | XCTFail()
39 | return
40 | }
41 |
42 | let result = StringResponseAdapter.parse(input: data)
43 |
44 | XCTAssertEqual(result, string)
45 | }
46 |
47 | // Test decode fails on non utf8 string
48 | public func testUnexpectedEncoding() {
49 | let string = "🙃"
50 | guard let data = string.data(using: .utf16) else {
51 | XCTFail()
52 | return
53 | }
54 |
55 | let result = StringResponseAdapter.parse(input: data)
56 | XCTAssertNil(result)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/InjectionServiceTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class InjectionServiceTest: XCTestCase {
7 | func testInjectionService() {
8 | let networkClient = FakeNetworkClient.tezosNodeNetworkClient
9 | let injectionService = InjectionService(networkClient: networkClient)
10 | let hex = String.testSignedBytesForInjection
11 |
12 | let expectation = XCTestExpectation(description: "injection completion called")
13 | injectionService.inject(payload: hex) { result in
14 | switch result {
15 | case .success:
16 | expectation.fulfill()
17 | case .failure:
18 | XCTFail()
19 | }
20 | }
21 |
22 | wait(for: [expectation], timeout: .expectationTimeout)
23 | }
24 |
25 | // swiftlint:disable force_cast
26 |
27 | func testInjectionServiceBadResponse() {
28 | let networkClient = FakeNetworkClient.tezosNodeNetworkClient.copy() as! FakeNetworkClient
29 | networkClient.endpointToResponseMap["/injection/operation"] = nil
30 | let injectionService = InjectionService(networkClient: networkClient)
31 | let hex = String.testSignedBytesForInjection
32 |
33 | let expectation = XCTestExpectation(description: "injection completion called")
34 | injectionService.inject(payload: hex) { result in
35 | switch result {
36 | case .success:
37 | XCTFail()
38 | case .failure:
39 | expectation.fulfill()
40 | }
41 | }
42 |
43 | wait(for: [expectation], timeout: .expectationTimeout)
44 | }
45 |
46 | // swiftlint:enable force_cast
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/TransactionOperation.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An operation to transact XTZ between addresses.
6 | public class TransactionOperation: AbstractOperation {
7 | private enum JSON {
8 | public enum Keys {
9 | public static let amount = "amount"
10 | public static let destination = "destination"
11 | public static let value = "value"
12 | }
13 | }
14 |
15 | internal let amount: Tez
16 | internal let destination: Address
17 |
18 | public override var dictionaryRepresentation: [String: Any] {
19 | var operation = super.dictionaryRepresentation
20 | operation[TransactionOperation.JSON.Keys.amount] = amount.rpcRepresentation
21 | operation[TransactionOperation.JSON.Keys.destination] = destination
22 |
23 | return operation
24 | }
25 |
26 | /// - Parameters:
27 | /// - amount: The amount of XTZ to transact.
28 | /// - from: The address that is sending the XTZ.
29 | /// - to: The address that is receiving the XTZ.
30 | /// - operationFees: OperationFees for the transaction.
31 | public init(
32 | amount: Tez,
33 | source: Address,
34 | destination: Address,
35 | operationFees: OperationFees
36 | ) {
37 | self.amount = amount
38 | self.destination = destination
39 |
40 | super.init(source: source, kind: .transaction, operationFees: operationFees)
41 | }
42 |
43 | public override func mutableCopy(with zone: NSZone? = nil) -> Any {
44 | return TransactionOperation(
45 | amount: amount,
46 | source: source,
47 | destination: destination,
48 | operationFees: operationFees
49 | )
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Examples/SecureEnclave/Base.lproj/Main.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 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/JSONDictionaryResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class JSONDictionaryResponseAdapterTest: XCTestCase {
7 | public func testParseDictionary() {
8 | let validJSONString = "{\"a\": \"b\", \"c\": { \"d\": \"e\" }}"
9 | guard let validJSONData = validJSONString.data(using: .utf8),
10 | let parsedDictionary = JSONDictionaryResponseAdapter.parse(input: validJSONData) else {
11 | XCTFail()
12 | return
13 | }
14 |
15 | XCTAssertNotNil(parsedDictionary["a"])
16 | guard let a = parsedDictionary["a"] as? String else {
17 | XCTFail()
18 | return
19 | }
20 | XCTAssertEqual(a, "b")
21 |
22 | XCTAssertNotNil(parsedDictionary["c"])
23 | guard let c = parsedDictionary["c"] as? [String: String],
24 | let d = c["d"] else {
25 | XCTFail()
26 | return
27 | }
28 | XCTAssertEqual(d, "e")
29 | }
30 |
31 | public func testParseDictionaryWithArray() {
32 | let validJSONString = "[{\"a\": \"b\", \"c\": { \"d\": \"e\" }}]"
33 | guard let validJSONData = validJSONString.data(using: .utf8) else {
34 | XCTFail()
35 | return
36 | }
37 |
38 | let parsedDictionary = JSONDictionaryResponseAdapter.parse(input: validJSONData)
39 | XCTAssertNil(parsedDictionary)
40 | }
41 |
42 | public func testParseDictionaryWithInvalidJSON() {
43 | let validJSONString = "abc:[]123"
44 | guard let validJSONData = validJSONString.data(using: .utf8) else {
45 | XCTFail()
46 | return
47 | }
48 |
49 | let parsedDictionary = JSONDictionaryResponseAdapter.parse(input: validJSONData)
50 | XCTAssertNil(parsedDictionary)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/TezosKit/Common/Models/SecureEnclaveWallet.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020.
2 |
3 | import Foundation
4 |
5 | /// A wallet which stores keys in a device's secure enclave.
6 | ///
7 | /// WARNING: Keys generated in the secure enclave are not able to be backed up. Additionally, iOS may choose to remove these keys at it's discretion, including
8 | /// when biometrics on the device are changed, a the device is restored, or the host app is deleted. This wallet should only be used as part of a
9 | /// multisignature signing scheme with a proper backup.
10 | /// Read more: https://medium.com/@keefertaylor/signing-tezos-transactions-with-ioss-secure-enclave-and-face-id-6166a752519?source=your_stories_page---------------------------
11 | @available(OSX 10.12.1, iOS 9.0, *)
12 | public class SecureEnclaveWallet: DeviceWallet {
13 | /// Labels for keys in the enclave.
14 | private enum KeyLabels {
15 | public static let `public` = "TezosKitEnclave.public"
16 | public static let `private` = "TezosKitEnclave.private"
17 | }
18 |
19 | /// Returns whether the device contains a secure enclave.
20 | public static var deviceHasSecureEnclave: Bool {
21 | return EllipticCurveKeyPair.Device.hasSecureEnclave
22 | }
23 |
24 | /// - Parameter prompt: A prompt to use when asking the wallet to sign bytes.
25 | public init?(prompt: String) {
26 | // Ensure that the device has access to a secure enclave.
27 | guard SecureEnclaveWallet.deviceHasSecureEnclave else {
28 | return nil
29 | }
30 |
31 | super.init(
32 | prompt: prompt,
33 | token: .secureEnclave,
34 | publicKeyLabel: KeyLabels.public,
35 | privateKeyLabel: KeyLabels.private
36 | )
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/docs/Fees.md:
--------------------------------------------------------------------------------
1 | # Fees
2 | Fees are an advanced topic which most users of TezosKit will not need to know about.
3 |
4 | Every `Operation` contains an `OperationFees` struct which represents a set of fees (fee, gas limit, storage limit) that will be applied when it is injected into the network.
5 |
6 | All calls on `TezosNodeClient` that injects an operation on the network provide an opportunity to provide a custom fee structure. If no fee structure is provided, the default fees are used for the operation.
7 |
8 | ### Defaults
9 |
10 | TezosNodeClient will provide default fees for the `TezosProtocol` version that is provided at initialization time. A `DefaultFeeProvider` object provides fees for a given operation type and `TezosProtocol` version.
11 |
12 | ### Custom Fees
13 |
14 | #### TezosNodeClient Operations
15 |
16 | Overriding the fees on an operation supported by the TezosNodeClient is easy. Simply pass a value for the fees parameter:
17 | ```swift
18 | // Create a node client
19 | let tezosNodeClient = TezosNodeClient(...)
20 |
21 | // Create a custom OperationFees object
22 | let customOperationFees = OperationFees(...)
23 |
24 | // Inject an operation with a custom fee.
25 | tezsosNodeClient.delegate(
26 | from: ...
27 | to: ...
28 | keys: ...
29 | operationFees: customOperationFees,
30 | ) { result in
31 | // Handle callback
32 | }
33 | ```
34 |
35 | #### Custom Operations
36 |
37 | If you are writing custom `Operation` objects, then the default fees for that operation are provided by your implementation. See [Operations](Operations.md) for more details.
38 |
39 | ### Estimated Gas Limit
40 |
41 | The Tezos network supports estimating the Gas used in an operation. This functionality will be supported in TezosKit in a future update.
42 |
--------------------------------------------------------------------------------
/Examples/SecureEnclave/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 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/TokenContractClientTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | @testable import TezosKit
4 | import XCTest
5 |
6 | final class TokenContractClientTests: XCTestCase {
7 | private var tokenContractClient: TokenContractClient?
8 |
9 | override func setUp() {
10 | super.setUp()
11 |
12 | let contract = Address.testTokenContractAddress
13 | let networkClient = FakeNetworkClient.tezosNodeNetworkClient
14 |
15 | let tezosNodeClient = TezosNodeClient(networkClient: networkClient)
16 | tokenContractClient = TokenContractClient(
17 | tokenContractAddress: contract,
18 | tezosNodeClient: tezosNodeClient
19 | )
20 | }
21 |
22 | func testTransferTokens() {
23 | let expectation = XCTestExpectation(description: "completion called")
24 |
25 | tokenContractClient?.transferTokens(
26 | from: Address.testAddress,
27 | to: Address.testDestinationAddress,
28 | numTokens: 1,
29 | operationFeePolicy: .custom(OperationFees.testFees),
30 | signatureProvider: FakeSignatureProvider.testSignatureProvider
31 | ) { result in
32 | switch result {
33 | case .success:
34 | expectation.fulfill()
35 | case .failure:
36 | XCTFail()
37 | }
38 | }
39 |
40 | wait(for: [expectation], timeout: .expectationTimeout)
41 | }
42 |
43 | func testGetBalance() {
44 | let expectation = XCTestExpectation(description: "completion called")
45 |
46 | tokenContractClient?.getTokenBalance(address: Address.testAddress) { result in
47 | switch result {
48 | case .success:
49 | expectation.fulfill()
50 | case .failure:
51 | XCTFail()
52 | }
53 | }
54 |
55 | wait(for: [expectation], timeout: .expectationTimeout)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/IntMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import BigInt
4 | import Foundation
5 |
6 | /// A representation of an integer parameter in Michelson.
7 | public class IntMichelsonParameter: AbstractMichelsonParameter {
8 | /// Initialize a representation of an integer using an `Int`.
9 | ///
10 | /// Note that the michelson int type is unbounded while `Int` values have bounded precision. Consider using the `init(bigInt:annotations:)`
11 | /// to represent a full range of values.
12 | public convenience init(int: Int, annotations: [MichelsonAnnotation]? = nil) {
13 | self.init(string: String(int), annotations: annotations)
14 | }
15 |
16 | /// Initialize a representation of an integer using a `Decimal`.
17 | ///
18 | /// Note that the michelson int type is unbounded while `Decimal` values have bounded precision. Consider using the `init(bigInt:annotations:)`
19 | /// to represent a full range of values.
20 | public convenience init(decimal: Decimal, annotations: [MichelsonAnnotation]? = nil) {
21 | self.init(string: "\(decimal)", annotations: annotations)
22 | }
23 |
24 | /// Initialize a representation of an integer using an `BigInt`.
25 | public convenience init(bigInt: BigInt, annotations: [MichelsonAnnotation]? = nil) {
26 | self.init(string: String(bigInt), annotations: annotations)
27 | }
28 |
29 | /// Internal initializer.
30 | ///
31 | /// - Parameters:
32 | /// - string: A numerical string representing the precision.
33 | /// - annotations: An array of annotations to apply to the parameter. Defaults to no annotations.
34 | private init(string: String, annotations: [MichelsonAnnotation]? = nil) {
35 | super.init(networkRepresentation: [MichelineConstants.int: string], annotations: annotations)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Services/OperationPayloadFactory.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | /// A factory which can produce operation payloads
6 | public enum OperationPayloadFactory {
7 | /// Create an operation payload from the given inputs.
8 | public static func operationPayload(
9 | from operations: [Operation],
10 | source: Address,
11 | signatureProvider: SignatureProvider,
12 | operationMetadata: OperationMetadata
13 | ) -> OperationPayload? {
14 | // Determine if the address performing the operations has been revealed. If it has not been, check if any of the
15 | // operations to perform requires the address to be revealed. If so, prepend a reveal operation to the operations to
16 | // perform.
17 | var mutableOperations = operations
18 | if operationMetadata.key == nil && operations.first(where: { $0.requiresReveal }) != nil {
19 | let defaultRevealFees = DefaultFeeProvider.fees(for: .reveal)
20 | let revealOperation = RevealOperation(
21 | from: source,
22 | publicKey: signatureProvider.publicKey,
23 | operationFees: defaultRevealFees
24 | )
25 |
26 | mutableOperations.insert(revealOperation, at: 0)
27 | }
28 |
29 | // Process all operations to have increasing counters and place them in the contents array.
30 | var nextCounter = operationMetadata.addressCounter + 1
31 | var operationsWithCounter: [OperationWithCounter] = []
32 | for operation in mutableOperations {
33 | let operationWithCounter = OperationWithCounter(operation: operation, counter: nextCounter)
34 | operationsWithCounter.append(operationWithCounter)
35 | nextCounter += 1
36 | }
37 |
38 | return OperationPayload(operations: operationsWithCounter, operationMetadata: operationMetadata)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/AbstractOperationTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | @testable import TezosKit
4 | import XCTest
5 |
6 | class AbstractOperationTest: XCTestCase {
7 | public func testRequiresReveal() {
8 | let abstractOperationRequiringReveal = AbstractOperation(
9 | source: "tz1abc",
10 | kind: .delegation,
11 | operationFees: OperationFees.testFees
12 | )
13 | XCTAssertTrue(abstractOperationRequiringReveal.requiresReveal)
14 |
15 | let abstractOperationNotRequiringReveal = AbstractOperation(
16 | source: "tz1abc",
17 | kind: .reveal,
18 | operationFees: OperationFees.testFees
19 | )
20 | XCTAssertFalse(abstractOperationNotRequiringReveal.requiresReveal)
21 | }
22 |
23 | public func testDictionaryRepresentation() {
24 | let source = "tz1abc"
25 | let kind: OperationKind = .delegation
26 | let fee = Tez(1)
27 | let gasLimit = 200
28 | let storageLimit = 300
29 | let operationFees = OperationFees(fee: fee, gasLimit: gasLimit, storageLimit: storageLimit)
30 |
31 | let abstractOperation = AbstractOperation(source: source, kind: kind, operationFees: operationFees)
32 | let dictionary = abstractOperation.dictionaryRepresentation
33 |
34 | XCTAssertNotNil(dictionary["source"])
35 | XCTAssertEqual(dictionary["source"] as? String, source)
36 |
37 | XCTAssertNotNil(dictionary["kind"])
38 | XCTAssertEqual(dictionary["kind"] as? String, kind.rawValue)
39 |
40 | XCTAssertNotNil(dictionary["fee"])
41 | XCTAssertEqual(dictionary["fee"] as? String, fee.rpcRepresentation)
42 |
43 | XCTAssertNotNil(dictionary["gas_limit"])
44 | XCTAssertEqual(dictionary["gas_limit"] as? String, String(gasLimit))
45 |
46 | XCTAssertNotNil(dictionary["storage_limit"])
47 | XCTAssertEqual(dictionary["storage_limit"] as? String, String(storageLimit))
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/TezosKit/Common/Michelson/NatMichelsonParameter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import BigInt
4 | import Foundation
5 |
6 | /// A representation of an nat parameter in Michelson.
7 | public class NatMichelsonParameter: AbstractMichelsonParameter {
8 | /// Initialize a representation of an integer using an `UInt`.
9 | ///
10 | /// Note that the michelson int type is unbounded while `UInt` values have bounded precision. Consider using the `init(bigUInt:annotations:)`
11 | /// to represent a full range of values.
12 | public convenience init(int: UInt, annotations: [MichelsonAnnotation]? = nil) {
13 | self.init(string: String(int), annotations: annotations)
14 | }
15 |
16 | /// Initialize a representation of an integer using a positive `Decimal`.
17 | ///
18 | /// Note that the michelson int type is unbounded while `Decimal` values have bounded precision. Consider using the `init(bigUInt:annotations:)`
19 | /// to represent a full range of values.
20 | public convenience init?(decimal: Decimal, annotations: [MichelsonAnnotation]? = nil) {
21 | guard decimal > 0 else {
22 | return nil
23 | }
24 |
25 | self.init(string: "\(decimal)", annotations: annotations)
26 | }
27 |
28 | /// Initialize a representation of an nat using an `BigUInt`.
29 | public convenience init(bigInt: BigUInt, annotations: [MichelsonAnnotation]? = nil) {
30 | self.init(string: String(bigInt), annotations: annotations)
31 | }
32 |
33 | /// Internal initializer.
34 | ///
35 | /// - Parameters:
36 | /// - string: A numerical string representing the precision.
37 | /// - annotations: An array of annotations to apply to the parameter. Defaults to no annotations.
38 | private init(string: String, annotations: [MichelsonAnnotation]? = nil) {
39 | super.init(networkRepresentation: [MichelineConstants.int: string], annotations: annotations)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/IntegerResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class IntegerResponseAdapterTest: XCTestCase {
7 | let integer = 123
8 | let integerString = "123"
9 |
10 | public func testParseInteger() {
11 | guard let balanceData = integerString.data(using: .utf8),
12 | let parsedInteger = IntegerResponseAdapter.parse(input: balanceData) else {
13 | XCTFail()
14 | return
15 | }
16 |
17 | XCTAssertNotNil(parsedInteger)
18 | XCTAssertEqual(parsedInteger, integer)
19 | }
20 |
21 | // Balances are returned as quoted from the API. Make sure that quotes can be stripped when
22 | // parsing.
23 | public func testParseIntegerWithQuotes() {
24 | guard let balanceData = ("\"" + integerString + "\"").data(using: .utf8),
25 | let parsedInteger = IntegerResponseAdapter.parse(input: balanceData) else {
26 | XCTFail()
27 | return
28 | }
29 |
30 | XCTAssertNotNil(parsedInteger)
31 | XCTAssertEqual(parsedInteger, integer)
32 | }
33 |
34 | // Ensure white space does not mess up parsing.
35 | public func testParseIntegerWithWhitespace() {
36 | guard let balanceData = (" " + integerString + "\n\n\n").data(using: .utf8),
37 | let parsedInteger = IntegerResponseAdapter.parse(input: balanceData) else {
38 | XCTFail()
39 | return
40 | }
41 |
42 | XCTAssertNotNil(parsedInteger)
43 | XCTAssertEqual(parsedInteger, integer)
44 | }
45 |
46 | // Ensure invalid strings cannot be parsed.
47 | public func testParseIntegerWithInvalidInput() {
48 | let invalidIntegerString = "xyz"
49 | guard let invalidIntegerData = invalidIntegerString.data(using: .utf8) else {
50 | XCTFail()
51 | return
52 | }
53 | let parsedInteger = IntegerResponseAdapter.parse(input: invalidIntegerData)
54 |
55 | XCTAssertNil(parsedInteger)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Examples/SecureEnclave/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Tezos HSM
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | NSFaceIDUsageDescription
31 | This app uses Face ID to sign Tezos Transactions
32 | UILaunchStoryboardName
33 | LaunchScreen
34 | UIMainStoryboardFile
35 | Main
36 | UIRequiredDeviceCapabilities
37 |
38 | armv7
39 |
40 | UISupportedInterfaceOrientations
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UISupportedInterfaceOrientations~ipad
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationPortraitUpsideDown
50 | UIInterfaceOrientationLandscapeLeft
51 | UIInterfaceOrientationLandscapeRight
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/TezResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class TezResponseAdapterTest: XCTestCase {
7 | public func testParseBalance() {
8 | guard let balance = Tez("3500000"),
9 | let balanceData = balance.rpcRepresentation.data(using: .utf8),
10 | let parsedBalance = TezResponseAdapter.parse(input: balanceData) else {
11 | XCTFail()
12 | return
13 | }
14 |
15 | XCTAssertNotNil(parsedBalance)
16 | XCTAssertEqual(parsedBalance, balance)
17 | }
18 |
19 | // Balances are returned as quoted from the API. Make sure that quotes can be stripped when
20 | // parsing.
21 | public func testParseBalanceWithQuotes() {
22 | guard let balance = Tez("3500000"),
23 | let balanceData = ("\"" + balance.rpcRepresentation + "\"").data(using: .utf8),
24 | let parsedBalance = TezResponseAdapter.parse(input: balanceData) else {
25 | XCTFail()
26 | return
27 | }
28 |
29 | XCTAssertNotNil(parsedBalance)
30 | XCTAssertEqual(parsedBalance, balance)
31 | }
32 |
33 | // Ensure white space does not mess up parsing.
34 | public func testParseBalanceWithWhitespace() {
35 | guard let balance = Tez("3500000"),
36 | let balanceData = (" " + balance.rpcRepresentation + "\n\n\n").data(using: .utf8),
37 | let parsedBalance = TezResponseAdapter.parse(input: balanceData) else {
38 | XCTFail()
39 | return
40 | }
41 |
42 | XCTAssertNotNil(parsedBalance)
43 | XCTAssertEqual(parsedBalance, balance)
44 | }
45 |
46 | // Ensure invalid strings cannot be parsed.
47 | public func testParseBalanceWithInvalidInput() {
48 | let invalidBalanceString = "xyz"
49 | guard let invalidBalanceData = invalidBalanceString.data(using: .utf8) else {
50 | XCTFail()
51 | return
52 | }
53 | let parsedBalance = TezResponseAdapter.parse(input: invalidBalanceData)
54 |
55 | XCTAssertNil(parsedBalance)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/TezosKit/Common/RPC/RPC.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | ///An abstract RPC class that defines a request and response handler.
6 | ///
7 | /// RPCs have a generic type associated with them, which is the expected type of the decoded bytes received from the
8 | /// network. The given RepsonseAdapter must meet this type.
9 | ///
10 | /// RPCs represent a network request to the Tezos network. RPCs are implicitly considered GET requests by default. If a
11 | /// payload is defined, then the RPC should be interpreted as a POST. This schema is represented in the derived
12 | /// `isPOSTRequest` property.
13 | ///
14 | /// Concrete subclasses should construct an endpoint and payload and inform this class by calling `super.init`.
15 | public class RPC {
16 | public let endpoint: String
17 | public let headers: [Header]
18 | public let payload: String?
19 | public let responseAdapterClass: AbstractResponseAdapter.Type
20 | public var isPOSTRequest: Bool {
21 | if payload != nil {
22 | return true
23 | }
24 | return false
25 | }
26 |
27 | /// Initialize a new request.
28 | ///
29 | /// By default, requests are considered to be GET requests with an empty body. If payload is set the request should be
30 | /// interpreted as a POST request with the given payload.
31 | ///
32 | /// - Parameters:
33 | /// - endpoint: The endpoint to which the request is being made.
34 | /// - headers: A dictionary of headers to use, default is empty headers.
35 | /// - responseAdapterClass: The class of the response adapter which will take bytes received from the request and
36 | /// transform them into a specific type.
37 | /// - payload: A payload that should be sent with a POST request.
38 | public init(
39 | endpoint: String,
40 | headers: [Header] = [],
41 | responseAdapterClass: AbstractResponseAdapter.Type,
42 | payload: String? = nil
43 | ) {
44 | self.endpoint = endpoint
45 | self.headers = headers
46 | self.responseAdapterClass = responseAdapterClass
47 | self.payload = payload
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/OriginationOperation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OriginationOperation.swift
3 | // TezosKit
4 | //
5 | // Created by Simon Mcloughlin on 28/02/2020.
6 | //
7 |
8 | import Foundation
9 |
10 | /// An operation to transact XTZ between addresses.
11 | public class OriginationOperation: AbstractOperation {
12 | private enum JSON {
13 | public enum Keys {
14 | public static let balance = "balance"
15 | public static let code = "code"
16 | public static let storage = "storage"
17 | public static let script = "script"
18 | }
19 | }
20 |
21 | internal let balance: Tez
22 | internal let code: MichelsonParameter
23 | internal let storage: MichelsonParameter
24 |
25 | public override var dictionaryRepresentation: [String: Any] {
26 | var operation = super.dictionaryRepresentation
27 | operation[JSON.Keys.balance] = balance.rpcRepresentation
28 | operation[JSON.Keys.script] = [
29 | JSON.Keys.code: code.networkRepresentation,
30 | JSON.Keys.storage: storage.networkRepresentation
31 | ]
32 |
33 | return operation
34 | }
35 |
36 | /// - Parameters:
37 | /// - source: The address originating the contract
38 | /// - balance: The amount of XTZ to move to the new contract.
39 | /// - code: Michelson parameters which make up code for the contract
40 | /// - storage: Initial storage for the contract
41 | /// - operationFees: OperationFees for the transaction.
42 | public init(
43 | source: Address,
44 | balance: Tez,
45 | code: MichelsonParameter,
46 | storage: MichelsonParameter,
47 | operationFees: OperationFees
48 | ) {
49 | self.balance = balance
50 | self.code = code
51 | self.storage = storage
52 |
53 | super.init(source: source, kind: .origination, operationFees: operationFees)
54 | }
55 |
56 | public override func mutableCopy(with zone: NSZone? = nil) -> Any {
57 | return OriginationOperation(
58 | source: source,
59 | balance: balance,
60 | code: code,
61 | storage: storage,
62 | operationFees: operationFees
63 | )
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/PeriodKindResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class PeriodKindResponseAdapterTest: XCTestCase {
7 | public func testParsePeriodKind() {
8 | guard let proposalData = "proposal".data(using: .utf8),
9 | let testingData = "testing".data(using: .utf8),
10 | let testingVoteData = "testing_vote".data(using: .utf8),
11 | let promotionVoteData = "promotion_vote".data(using: .utf8),
12 | let proposal = PeriodKindResponseAdapter.parse(input: proposalData),
13 | let testing = PeriodKindResponseAdapter.parse(input: testingData),
14 | let testingVote = PeriodKindResponseAdapter.parse(input: testingVoteData),
15 | let promotionVote = PeriodKindResponseAdapter.parse(input: promotionVoteData) else {
16 | XCTFail()
17 | return
18 | }
19 |
20 | XCTAssertEqual(proposal, .proposal)
21 | XCTAssertEqual(testing, .testing)
22 | XCTAssertEqual(testingVote, .testingVote)
23 | XCTAssertEqual(promotionVote, .promotionVote)
24 | }
25 |
26 | // Ensure quotes are stripped properly
27 | public func testParsePeriodKindWithQuotes() {
28 | guard let proposalData = "\"proposal\"".data(using: .utf8),
29 | let proposal = PeriodKindResponseAdapter.parse(input: proposalData) else {
30 | XCTFail()
31 | return
32 | }
33 | XCTAssertEqual(proposal, .proposal)
34 | }
35 |
36 | // Ensure whitespace are stripped properly
37 | public func testParsePeriodKindWithWhitespace() {
38 | guard let proposalData = "\nproposal ".data(using: .utf8),
39 | let proposal = PeriodKindResponseAdapter.parse(input: proposalData) else {
40 | XCTFail()
41 | return
42 | }
43 | XCTAssertEqual(proposal, .proposal)
44 | }
45 |
46 | // Ensure invalid strings cannot be parsed.
47 | public func testParsPeriodKindWithInvalidInput() {
48 | guard let invalidData = "not_valid".data(using: .utf8) else {
49 | XCTFail()
50 | return
51 | }
52 |
53 | let proposal = PeriodKindResponseAdapter.parse(input: invalidData)
54 | XCTAssertNil(proposal)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/project.yml:
--------------------------------------------------------------------------------
1 | name: TezosKit
2 | options:
3 | bundleIdPrefix: com.keefertaylor
4 | settings:
5 | LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks $(PROJECT_DIR)/Carthage/Build/iOS"
6 | targets:
7 | TezosKit_iOS:
8 | type: framework
9 | platform: iOS
10 | sources: [TezosKit, Extensions]
11 | deploymentTarget: 10.0
12 | scheme:
13 | testTargets:
14 | - TezosKitTests_iOS
15 | gatherCoverageData: true
16 | postCompileScripts:
17 | - script: swiftlint autocorrect --config .swiftlint.yml
18 | name: SwiftLint
19 | settings:
20 | base:
21 | PRODUCT_NAME: TezosKit
22 | dependencies:
23 | - carthage: BigInt
24 | - carthage: DTTJailbreakDetection
25 | - carthage: Sodium
26 | - carthage: CryptoSwift
27 | - carthage: PromiseKit
28 | - carthage: Base58Swift
29 | - carthage: MnemonicKit
30 | - carthage: secp256k1
31 | TezosKit_macOS:
32 | type: framework
33 | platform: macOS
34 | sources: [TezosKit, Extensions]
35 | scheme:
36 | testTargets:
37 | - TezosKitTests_macOS
38 | gatherCoverageData: true
39 | postCompileScripts:
40 | - script: swiftlint autocorrect --config .swiftlint.yml
41 | name: SwiftLint
42 | settings:
43 | base:
44 | PRODUCT_NAME: TezosKit
45 | dependencies:
46 | - carthage: BigInt
47 | - carthage: Sodium
48 | - carthage: CryptoSwift
49 | - carthage: PromiseKit
50 | - carthage: Base58Swift
51 | - carthage: MnemonicKit
52 | - carthage: secp256k1
53 | TezosKitTests:
54 | type: bundle.unit-test
55 | platform: [iOS, macOS]
56 | sources: [Tests/UnitTests, Tests/Common]
57 | dependencies:
58 | - target: TezosKit_${platform}
59 | TezosKitIntegrationTests:
60 | type: bundle.unit-test
61 | platform: [iOS, macOS]
62 | sources: [Tests/IntegrationTests, Tests/Common]
63 | dependencies:
64 | - target: TezosKit_${platform}
65 | SecureEnclaveExample:
66 | type: application
67 | platform: iOS
68 | deploymentTarget: "10.0"
69 | sources: [Examples/SecureEnclave]
70 | dependencies:
71 | - target: TezosKit_iOS
72 |
--------------------------------------------------------------------------------
/Tests/IntegrationTests/Extensions/PromiseKit/ConseilClientIntegrationTests+Promises.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 | import PromiseKit
5 | import TezosKit
6 | import XCTest
7 |
8 | /// Integration tests for the ConseilClient Promises Extension.
9 | /// Please see instructions in header of `ConseilClientIntegrationTests.swift`.
10 | extension ConseilClientIntegrationTests {
11 | func testGetTransactionsReceived_promises() {
12 | let expectation = XCTestExpectation(description: "promise fulfilled")
13 |
14 | conseilClient.transactionsReceived(from: Wallet.testWallet.address).done { result in
15 | XCTAssertNotNil(result)
16 | XCTAssert(result.count > 1)
17 | expectation.fulfill()
18 | } .catch { _ in
19 | XCTFail()
20 | }
21 |
22 | wait(for: [expectation], timeout: .expectationTimeout)
23 | }
24 |
25 | func testGetTransactionsSent_promises() {
26 | let expectation = XCTestExpectation(description: "promise fulfilled")
27 |
28 | conseilClient.transactionsSent(from: Wallet.testWallet.address).done { result in
29 | XCTAssertNotNil(result)
30 | XCTAssert(result.count > 1)
31 | expectation.fulfill()
32 | } .catch { _ in
33 | XCTFail()
34 | }
35 |
36 | wait(for: [expectation], timeout: .expectationTimeout)
37 | }
38 |
39 | func testGetTransactions_promises() {
40 | let expectation = XCTestExpectation(description: "promise fulfilled")
41 |
42 | conseilClient.transactions(from: Wallet.testWallet.address).done { result in
43 | XCTAssertNotNil(result)
44 | XCTAssert(result.count > 1)
45 | expectation.fulfill()
46 | } .catch { _ in
47 | XCTFail()
48 | }
49 |
50 | wait(for: [expectation], timeout: .expectationTimeout)
51 | }
52 |
53 | func testGetOriginatedContracts_promises() {
54 | let expectation = XCTestExpectation(description: "promise fulfilled")
55 | conseilClient.originatedContracts(from: Wallet.testWallet.address).done { result in
56 | XCTAssertNotNil(result)
57 | XCTAssert(result.count > 1)
58 | expectation.fulfill()
59 | } .catch { error in
60 | XCTFail("\(error)")
61 | }
62 |
63 | wait(for: [expectation], timeout: .expectationTimeout)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/DelegationOperationTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class DelegationOperationTest: XCTestCase {
7 | public func testDictionaryRepresentation_delegate() {
8 | let source = "tz1abc"
9 | let delegate = "tz1def"
10 |
11 | guard case let .success(operation) = OperationFactory.testFactory.delegateOperation(
12 | source: source,
13 | to: delegate,
14 | operationFeePolicy: .default,
15 | signatureProvider: FakeSignatureProvider.testSignatureProvider
16 | ) else {
17 | XCTFail()
18 | return
19 | }
20 | let dictionary = operation.dictionaryRepresentation
21 |
22 | XCTAssertNotNil(dictionary["source"])
23 | XCTAssertEqual(dictionary["source"] as? String, source)
24 |
25 | XCTAssertNotNil(dictionary["delegate"])
26 | XCTAssertEqual(dictionary["delegate"] as? String, delegate)
27 | }
28 |
29 | public func testDictionaryRepresentation_undelegate() {
30 | let source = "tz1abc"
31 |
32 | guard case let .success(operation) = OperationFactory.testFactory.undelegateOperation(
33 | source: source,
34 | operationFeePolicy: .default,
35 | signatureProvider: FakeSignatureProvider.testSignatureProvider
36 | ) else {
37 | XCTFail()
38 | return
39 | }
40 | let dictionary = operation.dictionaryRepresentation
41 |
42 | XCTAssertNotNil(dictionary["source"])
43 | XCTAssertEqual(dictionary["source"] as? String, source)
44 |
45 | XCTAssertNil(dictionary["delegate"])
46 | }
47 |
48 | public func testDictionaryRepresentation_registerDelegate() {
49 | let source = "tz1abc"
50 |
51 | guard case let .success(operation) = OperationFactory.testFactory.registerDelegateOperation(
52 | source: source,
53 | operationFeePolicy: .default,
54 | signatureProvider: FakeSignatureProvider.testSignatureProvider
55 | ) else {
56 | XCTFail()
57 | return
58 | }
59 | let dictionary = operation.dictionaryRepresentation
60 |
61 | XCTAssertNotNil(dictionary["source"])
62 | XCTAssertEqual(dictionary["source"] as? String, source)
63 |
64 | XCTAssertNotNil(dictionary["delegate"])
65 | XCTAssertEqual(dictionary["delegate"] as? String, source)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Examples/SecureEnclave/SecureEnclaveAppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Enclave
4 | //
5 | // Created by Keefer Taylor on 5/1/19.
6 | // Copyright © 2019 Keefer Taylor. All rights reserved.
7 | //
8 | import TezosKit
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 | var nc: TezosNodeClient?
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 |
19 | // Override point for customization after application launch.
20 | return true
21 | }
22 |
23 | func applicationWillResignActive(_ application: UIApplication) {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
26 | }
27 |
28 | func applicationDidEnterBackground(_ application: UIApplication) {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | func applicationWillEnterForeground(_ application: UIApplication) {
34 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | func applicationDidBecomeActive(_ application: UIApplication) {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | func applicationWillTerminate(_ application: UIApplication) {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/TezosKit/Conseil/Models/ConseilQuery.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 |
5 | public typealias ConseilPredicate = [String: Any]
6 | public typealias ConseilOrderBy = [String: Any]
7 |
8 | public enum ConseilQuery: String {
9 | case fields
10 |
11 | case predicates
12 | public enum Predicates: String {
13 | case set
14 | case field
15 | case operation
16 | public enum Operation: String {
17 | case after = "after"
18 | case before
19 | case between
20 | case endsWith
21 | case equal = "eq"
22 | case greaterThan = "gt"
23 | case `in` = "in"
24 | case isNull = "isnull"
25 | case lessThan = "lt"
26 | case like
27 | case startsWith
28 | }
29 | case inverse
30 |
31 | public static func predicateWith(
32 | field: String,
33 | operation: ConseilQuery.Predicates.Operation = .equal,
34 | set: [String] = [],
35 | inverse: Bool = false
36 | ) -> ConseilPredicate {
37 | return [
38 | ConseilQuery.Predicates.field.rawValue: field,
39 | ConseilQuery.Predicates.operation.rawValue: operation.rawValue,
40 | ConseilQuery.Predicates.inverse.rawValue: inverse,
41 | ConseilQuery.Predicates.set.rawValue: set
42 | ]
43 | }
44 | }
45 |
46 | case aggregation
47 |
48 | case orderBy = "orderby"
49 | public enum OrderBy: String {
50 | case field
51 | case direction
52 | public enum Direction: String {
53 | case ascending = "asc"
54 | case descending = "desc"
55 | }
56 |
57 | public static func orderBy(
58 | field: String,
59 | direction: ConseilQuery.OrderBy.Direction = .descending
60 | ) -> ConseilOrderBy {
61 | return [
62 | ConseilQuery.OrderBy.field.rawValue: field,
63 | ConseilQuery.OrderBy.direction.rawValue: direction.rawValue
64 | ]
65 | }
66 | }
67 |
68 | case limit
69 |
70 | public static func query(
71 | fields: [String] = [],
72 | predicates: [ConseilPredicate],
73 | orderBy: ConseilOrderBy,
74 | limit: Int
75 | ) -> [String: Any] {
76 | return [
77 | ConseilQuery.fields.rawValue: fields,
78 | ConseilQuery.predicates.rawValue: predicates,
79 | ConseilQuery.orderBy.rawValue: orderBy,
80 | ConseilQuery.limit.rawValue: limit
81 | ]
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/TransactionsResponseAdapterTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class TransactionsResponseAdapterTest: XCTestCase {
7 | func testParse() {
8 | let source = "tz1abc"
9 | let destination = "tz1xyz"
10 | let amount = Tez(10.0)
11 | let fee = Tez(1)
12 | let timestamp: TimeInterval = 1_234_567
13 | let blockHash = "BMc3kxPnn95TxYKVPehmYWXuaoKBneoPKeDk4sz7usFp7Aumnez"
14 | let blockLevel = 323_100
15 | let operationGroupHash = "opMiJzXJV8nKWy7VTLh2yxFL8yUGDpVkvnbA5hUwj9dSnpMEEMa"
16 | let operationID = 1_511_646
17 | let kind = "transaction"
18 | let status = "applied"
19 |
20 | let validTransaction: [String: Any] = [
21 | Transaction.JSONKeys.source: source,
22 | Transaction.JSONKeys.destination: destination,
23 | Transaction.JSONKeys.amount: Double(amount.rpcRepresentation)!,
24 | Transaction.JSONKeys.fee: Double(fee.rpcRepresentation)!,
25 | Transaction.JSONKeys.timestamp: timestamp,
26 | Transaction.JSONKeys.blockHash: blockHash,
27 | Transaction.JSONKeys.blockLevel: blockLevel,
28 | Transaction.JSONKeys.operationGroupHash: operationGroupHash,
29 | Transaction.JSONKeys.operationID: operationID,
30 | Transaction.JSONKeys.kind: kind,
31 | Transaction.JSONKeys.status: status
32 | ]
33 |
34 | let transactionWithMissingField: [String: Any] = [
35 | Transaction.JSONKeys.source: source,
36 | Transaction.JSONKeys.destination: destination,
37 | Transaction.JSONKeys.amount: Double(amount.rpcRepresentation)!,
38 | Transaction.JSONKeys.timestamp: timestamp
39 | ]
40 |
41 | let badInput: [String: Any] = [
42 | "publicKey": "edpk123xyz",
43 | "kind": "reveal"
44 | ]
45 |
46 | let transactions = [ validTransaction, transactionWithMissingField, badInput ]
47 | let data = JSONUtils.jsonString(for: transactions)?.data(using: .utf8)
48 |
49 | let parsedTransactions = TransactionsResponseAdapter.parse(input: data!)!
50 | XCTAssertEqual(parsedTransactions.count, 1)
51 | XCTAssertEqual(parsedTransactions[0].source, source)
52 | XCTAssertEqual(parsedTransactions[0].destination, destination)
53 | XCTAssertEqual(parsedTransactions[0].amount, amount)
54 | XCTAssertEqual(parsedTransactions[0].fee, fee)
55 | XCTAssertEqual(parsedTransactions[0].timestamp, timestamp)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Extensions/PromiseKit/Conseil/ConseilClient+Promises.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import PromiseKit
4 |
5 | /// Extension for ConseilClient which provides a Promise/PromiseKit based API.
6 | extension ConseilClient {
7 | /// Retrieve originated contracts.
8 | ///
9 | /// - Parameters:
10 | /// - account: The account to query.
11 | /// - limit: The number of contracts to return, defaults to 100.
12 | /// - completion: A completion callback.
13 | public func originatedContracts(
14 | from account: String,
15 | limit: Int = 100
16 | ) -> Promise<[[String: Any]]> {
17 | let rpc = GetOriginatedContractsRPC(account: account, limit: limit)
18 | return networkClient.send(rpc)
19 | }
20 |
21 | /// Retrieve transactions both sent and received from an account.
22 | ///
23 | /// - Parameters:
24 | /// - account: The account to query.
25 | /// - limit: The number of transactions to return, defaults to 100.
26 | /// - completion: A completion callback.
27 | public func transactions(
28 | from account: String,
29 | limit: Int = 100
30 | ) -> Promise<[Transaction]> {
31 | return Promise { seal in
32 | transactions(from: account, limit: limit) { result in
33 | switch result {
34 | case .success(let data):
35 | seal.fulfill(data)
36 | case .failure(let error):
37 | seal.reject(error)
38 | }
39 | }
40 | }
41 | }
42 |
43 | /// Retrieve transactions received from an account.
44 | /// - Parameters:
45 | /// - account: The account to query.
46 | /// - limit: The number of transactions to return, defaults to 100.
47 | public func transactionsReceived(
48 | from account: String,
49 | limit: Int = 100
50 | ) -> Promise<[Transaction]> {
51 | let rpc = GetReceivedTransactionsRPC(
52 | account: account,
53 | limit: limit
54 | )
55 | return networkClient.send(rpc)
56 | }
57 |
58 | /// Retrieve transactions sent from an account.
59 | /// - Parameters:
60 | /// - account: The account to query.
61 | /// - limit: The number of transactions to return, defaults to 100.
62 | public func transactionsSent(
63 | from account: String,
64 | limit: Int = 100
65 | ) -> Promise<[Transaction]> {
66 | let rpc = GetSentTransactionsRPC(
67 | account: account,
68 | limit: limit
69 | )
70 | return networkClient.send(rpc)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/TransactionTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class TransactionTest: XCTestCase {
7 | func testValidTransaction() {
8 | let source = "tz1abc"
9 | let destination = "tz1xyz"
10 | let amount = Tez(10.0)
11 | let fee = Tez(1)
12 | let timestamp: TimeInterval = 1_234_567
13 | let blockHash = "BMc3kxPnn95TxYKVPehmYWXuaoKBneoPKeDk4sz7usFp7Aumnez"
14 | let blockLevel = 323_100
15 | let operationGroupHash = "opMiJzXJV8nKWy7VTLh2yxFL8yUGDpVkvnbA5hUwj9dSnpMEEMa"
16 | let operationID = 1_511_646
17 | let kind = "transaction"
18 | let status = "applied"
19 |
20 | let jsonDict: [String: Any] = [
21 | Transaction.JSONKeys.source: source,
22 | Transaction.JSONKeys.destination: destination,
23 | Transaction.JSONKeys.amount: Int(amount.rpcRepresentation)!,
24 | Transaction.JSONKeys.fee: Int(fee.rpcRepresentation)!,
25 | Transaction.JSONKeys.timestamp: timestamp,
26 | Transaction.JSONKeys.blockHash: blockHash,
27 | Transaction.JSONKeys.blockLevel: blockLevel,
28 | Transaction.JSONKeys.operationGroupHash: operationGroupHash,
29 | Transaction.JSONKeys.operationID: operationID,
30 | Transaction.JSONKeys.kind: kind,
31 | Transaction.JSONKeys.status: status
32 | ]
33 |
34 | guard let transaction = Transaction(jsonDict) else {
35 | XCTFail()
36 | return
37 | }
38 |
39 | XCTAssertEqual(transaction.source, source)
40 | XCTAssertEqual(transaction.destination, destination)
41 | XCTAssertEqual(transaction.amount, amount)
42 | XCTAssertEqual(transaction.fee, fee)
43 | XCTAssertEqual(transaction.timestamp, timestamp)
44 | }
45 |
46 | func testInValidTransaction_missingFee() {
47 | let source = "tz1abc"
48 | let destination = "tz1xyz"
49 | let amount = Tez(10.0)
50 | let timestamp: TimeInterval = 1_234_567
51 |
52 | let jsonDict: [String: Any] = [
53 | Transaction.JSONKeys.source: source,
54 | Transaction.JSONKeys.destination: destination,
55 | Transaction.JSONKeys.amount: Double(amount.humanReadableRepresentation)!,
56 | Transaction.JSONKeys.timestamp: timestamp
57 | ]
58 |
59 | XCTAssertNil(Transaction(jsonDict))
60 | }
61 |
62 | func testInValidTransaction_badInput() {
63 | let jsonDict: [String: Any] = [
64 | "publicKey": "edpk123xyz",
65 | "kind": "reveal"
66 | ]
67 |
68 | XCTAssertNil(Transaction(jsonDict))
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/SmartContractInvocationOperation.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2020
2 |
3 | import Foundation
4 |
5 | /// An operation to invoke a smart contract.
6 | public class SmartContractInvocationOperation: TransactionOperation {
7 | internal enum JSON {
8 | public enum Keys {
9 | public static let entrypoint = "entrypoint"
10 | public static let parameters = "parameters"
11 | public static let value = "value"
12 | }
13 | public enum Values {
14 | public static let `default` = "default"
15 | }
16 | }
17 |
18 | private let entrypoint: String?
19 | private let parameter: MichelsonParameter?
20 |
21 | public override var dictionaryRepresentation: [String: Any] {
22 | var operation = super.dictionaryRepresentation
23 |
24 | let parameter = self.parameter ?? UnitMichelsonParameter()
25 | let entrypoint = self.entrypoint ?? SmartContractInvocationOperation.JSON.Values.default
26 |
27 | let parameters: [String: Any] = [
28 | SmartContractInvocationOperation.JSON.Keys.entrypoint: entrypoint,
29 | SmartContractInvocationOperation.JSON.Keys.value: parameter.networkRepresentation
30 | ]
31 | operation[SmartContractInvocationOperation.JSON.Keys.parameters] = parameters
32 |
33 | return operation
34 | }
35 |
36 | /// - Parameters:
37 | /// - amount: The amount of XTZ to transact.
38 | /// - entrypoint: An optional entrypoint to use for the transaction. If nil, the default entry point is used.
39 | /// - parameter: An optional parameter to include in the transaction if the call is being made to a smart contract. If nil, the unit parameter is used.
40 | /// - from: The address that is sending the XTZ.
41 | /// - to: The address that is receiving the XTZ.
42 | /// - operationFees: OperationFees for the transaction.
43 | public init(
44 | amount: Tez,
45 | entrypoint: String? = nil,
46 | parameter: MichelsonParameter? = nil,
47 | source: Address,
48 | destination: Address,
49 | operationFees: OperationFees
50 | ) {
51 | self.entrypoint = entrypoint
52 | self.parameter = parameter
53 |
54 | super.init(amount: amount, source: source, destination: destination, operationFees: operationFees)
55 | }
56 |
57 | public override func mutableCopy(with zone: NSZone? = nil) -> Any {
58 | return SmartContractInvocationOperation(
59 | amount: amount,
60 | entrypoint: entrypoint,
61 | parameter: parameter,
62 | source: source,
63 | destination: destination,
64 | operationFees: operationFees
65 | )
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/DelegationOperation.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import Foundation
4 |
5 | /// An operation to set a delegate for an address.
6 | public class DelegationOperation: AbstractOperation {
7 | // swiftlint:disable weak_delegate
8 | /// The address that will be set as the delegate.
9 | public let delegate: Address?
10 | // swiftlint:enable weak_delegate
11 |
12 | public override var dictionaryRepresentation: [String: Any] {
13 | var operation = super.dictionaryRepresentation
14 | if let delegate = self .delegate {
15 | operation["delegate"] = delegate
16 | }
17 | return operation
18 | }
19 |
20 | /// Create a delegation operation.
21 | ///
22 | /// If delegate and source are the same, then the source will be registered as a delegate.
23 | /// If delegate and source are different, then source will delegate to delegate.
24 | /// If delegate is nil, source will clear any delegation.
25 | ///
26 | /// - Parameters:
27 | /// - source: The address that will delegate funds.
28 | /// - delegate: The address to delegate to.
29 | /// - operationFees: OperationFees for the transaction.
30 | internal init(source: Address, delegate: Address?, operationFees: OperationFees) {
31 | self.delegate = delegate
32 | super.init(source: source, kind: .delegation, operationFees: operationFees)
33 | }
34 |
35 | /// Register the given address as a delegate.
36 | /// - Parameters:
37 | /// - source: The address that will register as a delegate.
38 | /// - operationFees: OperationFees for the transaction.
39 | public static func registerDelegateOperation(
40 | source: Address,
41 | operationFees: OperationFees
42 | ) -> DelegationOperation {
43 | return DelegationOperation(source: source, delegate: source, operationFees: operationFees)
44 | }
45 |
46 | /// Delegate to the given address.
47 | /// - Parameters:
48 | /// - source: The address that will delegate funds.
49 | /// - delegate: The address to delegate to.
50 | /// - operationFees: OperationFees for the transaction.
51 | public static func delegateOperation(
52 | source: Address,
53 | to delegate: Address,
54 | operationFees: OperationFees
55 | ) -> DelegationOperation {
56 | return DelegationOperation(source: source, delegate: delegate, operationFees: operationFees)
57 | }
58 |
59 | /// Clear the delegate from the given address.
60 | /// - Parameters:
61 | /// - source: The address that will have its delegate cleared.
62 | /// - operationFees: OperationFees for the transaction.
63 | public static func undelegateOperation(source: Address, operationFees: OperationFees) -> DelegationOperation {
64 | return DelegationOperation(source: source, delegate: nil, operationFees: operationFees)
65 | }
66 |
67 | public override func mutableCopy(with zone: NSZone? = nil) -> Any {
68 | return DelegationOperation(source: source, delegate: delegate, operationFees: operationFees)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Tests/IntegrationTests/TezosKit/ConseilClientIntegrationTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Foundation
4 | import TezosKit
5 | import XCTest
6 |
7 | /// Integration tests to run against a live Conseil server.
8 | ///
9 | /// To get started using Conseil, look at:
10 | /// https://github.com/Cryptonomic/Conseil/blob/master/doc/use-conseil.md
11 | ///
12 | /// These tests are not hermetic and may fail for a number or reasons, such as:
13 | /// - Adverse network conditions.
14 | /// - Changes in Conseil
15 | ///
16 | /// *** Configuration must be done before theses tests can be run. Please configure: ***
17 | /// - Conseil URL
18 | /// - Conseil API Key
19 | let apiKey = "PUT_KEY_HERE"
20 | let remoteNodeURL = URL(string: "https://conseil-dev.cryptonomic-infra.tech:443")!
21 |
22 | class ConseilClientIntegrationTests: XCTestCase {
23 | public lazy var conseilClient: ConseilClient = {
24 | return ConseilClient(remoteNodeURL: remoteNodeURL, apiKey: apiKey, platform: .tezos, network: .babylonnet)
25 | }()
26 |
27 | public func testConseilSent() {
28 | let expectation = XCTestExpectation(description: "completion called")
29 | conseilClient.transactionsSent(from: Wallet.testWallet.address) { result in
30 | switch result {
31 | case .success(let results):
32 | XCTAssert(results.count > 1)
33 | expectation.fulfill()
34 | case .failure:
35 | XCTFail()
36 | }
37 | }
38 | wait(for: [expectation], timeout: .expectationTimeout)
39 | }
40 |
41 | public func testConseilReceived() {
42 | let expectation = XCTestExpectation(description: "completion called")
43 | conseilClient.transactionsReceived(from: Wallet.testWallet.address) { result in
44 | switch result {
45 | case .success(let results):
46 | XCTAssert(results.count > 1)
47 | expectation.fulfill()
48 | case .failure:
49 | XCTFail()
50 | }
51 | }
52 | wait(for: [expectation], timeout: .expectationTimeout)
53 | }
54 |
55 | public func testConseilTransactions() {
56 | let expectation = XCTestExpectation(description: "completion called")
57 | conseilClient.transactions(from: Wallet.testWallet.address) { result in
58 | switch result {
59 | case .success(let results):
60 | XCTAssert(results.count > 1)
61 | expectation.fulfill()
62 | case .failure:
63 | XCTFail()
64 | }
65 | }
66 | wait(for: [expectation], timeout: .expectationTimeout)
67 | }
68 |
69 | public func testConseilOriginatedContracts() {
70 | let expectation = XCTestExpectation(description: "completion called")
71 | conseilClient.originatedContracts(from: Wallet.testWallet.address) { result in
72 | switch result {
73 | case .success(let results):
74 | XCTAssert(results.count > 1)
75 | expectation.fulfill()
76 | case .failure(let error):
77 | print(error)
78 | XCTFail()
79 | }
80 | }
81 | wait(for: [expectation], timeout: .expectationTimeout)
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/RPC/ResponseAdapters/SimulationResultResponseAdapter.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import Foundation
4 |
5 | /// JSON keys and values used in the PreapplicationService.
6 | private enum JSON {
7 | public enum Keys {
8 | public static let consumedGas = "consumed_gas"
9 | public static let contents = "contents"
10 | public static let internalOperationResult = "internal_operation_results"
11 | public static let metadata = "metadata"
12 | public static let operationResult = "operation_result"
13 | public static let result = "result"
14 | public static let status = "status"
15 | public static let storageSize = "storage_size"
16 | }
17 |
18 | public enum Values {
19 | public static let failed = "failed"
20 | }
21 | }
22 |
23 | /// Parse the resulting JSON from a simulation operation to a SimulationResult enum
24 | public class SimulationResultResponseAdapter: AbstractResponseAdapter {
25 | public override class func parse(input: Data) -> SimulationResult? {
26 | guard
27 | let json = JSONDictionaryResponseAdapter.parse(input: input)
28 | else {
29 | return nil
30 | }
31 |
32 | guard
33 | let contents = json[JSON.Keys.contents] as? [[ String: Any ]]
34 | else {
35 | return nil
36 | }
37 |
38 | var consumedGas = 0
39 | var consumedStorage = 0
40 | for content in contents {
41 | guard
42 | let metadata = content[JSON.Keys.metadata] as? [String: Any],
43 | let operationResult = metadata[JSON.Keys.operationResult] as? [String: Any],
44 | let status = operationResult[JSON.Keys.status] as? String
45 | else {
46 | continue
47 | }
48 |
49 | if status == JSON.Values.failed {
50 | return nil
51 | }
52 |
53 | let rawConsumedGas = operationResult[JSON.Keys.consumedGas] as? String ?? "0"
54 | consumedGas += Int(rawConsumedGas) ?? 0
55 |
56 | let rawConsumedStorage = operationResult[JSON.Keys.storageSize] as? String ?? "0"
57 | consumedStorage += Int(rawConsumedStorage) ?? 0
58 |
59 | if let internalOperationResults = metadata[JSON.Keys.internalOperationResult] as? [[String: Any]] {
60 | for internalOperation in internalOperationResults {
61 | guard let intenalOperationResult = internalOperation[JSON.Keys.result] as? [String: Any] else {
62 | continue
63 | }
64 |
65 | let rawInternalConsumedGas = intenalOperationResult[JSON.Keys.consumedGas] as? String ?? "0"
66 | let internalConsumedGas = Int(rawInternalConsumedGas) ?? 0
67 | consumedGas += internalConsumedGas
68 |
69 | let rawInternalConsumedStorage = intenalOperationResult[JSON.Keys.storageSize] as? String ?? "0"
70 | let internalConsumedStorage = Int(rawInternalConsumedStorage) ?? 0
71 | consumedStorage += internalConsumedStorage
72 | }
73 | }
74 | }
75 |
76 | return SimulationResult(consumedGas: consumedGas, consumedStorage: consumedStorage)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/ForgingServiceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class ForgingServiceTests: XCTestCase {
7 | func testForgingServiceWithRemotePolicySync() {
8 | let forgingService = ForgingService(forgingPolicy: .remote, networkClient: FakeNetworkClient.tezosNodeNetworkClient)
9 |
10 | let result = forgingService.forgeSync(
11 | operationPayload: .testOperationPayload,
12 | operationMetadata: .testOperationMetadata
13 | )
14 |
15 | guard case let .success(forgingServiceForgeResult) = result else {
16 | XCTFail()
17 | return
18 | }
19 |
20 | XCTAssertEqual(forgingServiceForgeResult, .testForgeResult)
21 | }
22 |
23 | func testForgingServiceWithRemotePolicy() {
24 | let forgingService = ForgingService(forgingPolicy: .remote, networkClient: FakeNetworkClient.tezosNodeNetworkClient)
25 |
26 | let forgeCompletionExpectation = XCTestExpectation(description: "Forge completion called.")
27 | forgingService.forge(operationPayload: .testOperationPayload, operationMetadata: .testOperationMetadata) { result in
28 | switch result {
29 | case .success(let forgingServiceForgeResult):
30 | XCTAssertEqual(forgingServiceForgeResult, .testForgeResult)
31 | case .failure:
32 | XCTFail()
33 | }
34 | forgeCompletionExpectation.fulfill()
35 | }
36 |
37 | wait(for: [forgeCompletionExpectation], timeout: .expectationTimeout)
38 | }
39 |
40 | func testForgingServiceWithLocalPolicy() {
41 | let forgingService = ForgingService(forgingPolicy: .local, networkClient: FakeNetworkClient.tezosNodeNetworkClient)
42 |
43 | let forgeCompletionExpectation = XCTestExpectation(description: "Forge completion called.")
44 | forgingService.forge(operationPayload: .testOperationPayload, operationMetadata: .testOperationMetadata) { result in
45 | switch result {
46 | case .success:
47 | XCTFail()
48 | case .failure(let error):
49 | XCTAssertEqual(error, .localForgingNotSupportedForOperation)
50 | }
51 | forgeCompletionExpectation.fulfill()
52 | }
53 |
54 | wait(for: [forgeCompletionExpectation], timeout: .expectationTimeout)
55 | }
56 |
57 | func testForgingServiceWithLocalWithRemoteFallbackPolicyAndUnforgeableOperation() {
58 | let forgingService = ForgingService(
59 | forgingPolicy: .localWithRemoteFallBack,
60 | networkClient: FakeNetworkClient.tezosNodeNetworkClient
61 | )
62 |
63 | let forgeCompletionExpectation = XCTestExpectation(description: "Forge completion called.")
64 |
65 | forgingService.forge(operationPayload: .testOperationPayload, operationMetadata: .testOperationMetadata) { result in
66 | switch result {
67 | case .success(let forgingServiceForgeResult):
68 | XCTAssertEqual(forgingServiceForgeResult, .testForgeResult)
69 | case .failure:
70 | XCTFail()
71 | }
72 | forgeCompletionExpectation.fulfill()
73 | }
74 |
75 | wait(for: [forgeCompletionExpectation], timeout: .expectationTimeout)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/JSONUtilsTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | final class JSONUtilsTest: XCTestCase {
7 | func testJSONForString() {
8 | let inputs = [
9 | "A regular string", // Regular string
10 | "\"A quoted string\"" // String with escape sequences
11 | ]
12 |
13 | // Parallel sorted array to |inputs|.
14 | let expectedOutputs = [
15 | "\"A regular string\"",
16 | "\"\"A quoted string\"\""
17 | ]
18 |
19 | for (i, input) in inputs.enumerated() {
20 | let result = JSONUtils.jsonString(for: input)
21 | XCTAssertEqual(result, expectedOutputs[i])
22 | }
23 | }
24 |
25 | func testJSONForDictionary() {
26 | let inputs: [[String: Any]] = [
27 | ["key1": "val1", "key2": "val2"], // Normal
28 | ["key1": "\"quotedVal1\"", "\"quotedKey2\"": "val2"], // Quoted Strings
29 | ["key1": "val1", "key2": Int(42)] // Values besides strings
30 | ]
31 |
32 | // Parallel sorted array to |inputs|.
33 | let expectedOutputs = [
34 | "{\"key1\":\"val1\",\"key2\":\"val2\"}",
35 | "{\"\\\"quotedKey2\\\"\":\"val2\",\"key1\":\"\\\"quotedVal1\\\"\"}",
36 | "{\"key1\":\"val1\",\"key2\":42}"
37 | ]
38 |
39 | for (i, input) in inputs.enumerated() {
40 | // Fail if serialization fails.
41 | guard let result = JSONUtils.jsonString(for: input) else {
42 | XCTFail()
43 | return
44 | }
45 |
46 | XCTAssertEqual(result, expectedOutputs[i])
47 | }
48 | }
49 |
50 | func testJSONForArray() {
51 | let inputs: [[[String: Any]]] = [
52 | [["key1": "val1", "key2": "val2"]], // Normal
53 | // Multiple elements
54 | [
55 | ["dict1key1": "dict1val1", "dict1key2": "dict1val2"],
56 | ["dict2key1": "dict2val1", "dict2key2": "dict2val2"]
57 | ],
58 | [["key1": "\"quotedVal1\"", "\"quotedKey2\"": "val2"]], // Quoted Strings
59 | [["key1": "val1", "key2": Int(42)]] // Values besides strings
60 | ]
61 |
62 | // swiftlint:disable line_length
63 | // Parallel sorted array to |inputs|.
64 | let expectedOutputs = [
65 | "[{\"key1\":\"val1\",\"key2\":\"val2\"}]",
66 | "[{\"dict1key1\":\"dict1val1\",\"dict1key2\":\"dict1val2\"},{\"dict2key1\":\"dict2val1\",\"dict2key2\":\"dict2val2\"}]",
67 | "[{\"\\\"quotedKey2\\\"\":\"val2\",\"key1\":\"\\\"quotedVal1\\\"\"}]",
68 | "[{\"key1\":\"val1\",\"key2\":42}]"
69 | ]
70 | // swiftlint:enable line_length
71 |
72 | for (i, input) in inputs.enumerated() {
73 | // Fail if serialization fails.
74 | guard let result = JSONUtils.jsonString(for: input) else {
75 | XCTFail()
76 | return
77 | }
78 |
79 | XCTAssertEqual(result, expectedOutputs[i])
80 | }
81 | }
82 |
83 | func testJSONForInt() {
84 | let input = Int.testAddressCounter
85 | let expectedOutput = "\"" + String(input) + "\""
86 |
87 | guard let result = JSONUtils.jsonString(for: input) else {
88 | XCTFail()
89 | return
90 | }
91 |
92 | XCTAssertEqual(result, expectedOutput)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Tests/UnitTests/TezosKit/MnemonicUtilsTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2018
2 |
3 | import TezosKit
4 | import XCTest
5 |
6 | class MnemonicUtilsTest: XCTestCase {
7 | // Mnemonic and Passphrase for tests.
8 | private let mnemonic =
9 | "soccer click number muscle police corn couch bitter gorilla camp camera shove expire praise pill"
10 | private let passphrase = "TezosKitTests"
11 |
12 | // Expected seed strings with / without passphrase.
13 | private let expectedSeedStringNoPassphrase = "cce78b57ed8f4ec6767ed35f3aa41df525a03455e24bcc45a8518f63fbeda772"
14 | private let expectedSeedStringWithPassphrase = "cc90fecd0a596e2cd41798612682395faa2ebfe18945a88c6f01e4bfab17c3e3"
15 |
16 | // Mnemonic generation should always succeed.
17 | public func testGenerateMnemonic() {
18 | let result = MnemonicUtil.generateMnemonic()
19 | XCTAssertNotNil(result)
20 | }
21 |
22 | public func testSeedStringFromMnemonicNoPassphrase() {
23 | guard let result = MnemonicUtil.seedString(from: mnemonic) else {
24 | fatalError()
25 | }
26 | XCTAssertEqual(result, expectedSeedStringNoPassphrase)
27 | }
28 |
29 | public func testSeedStringFromMnemonicEmptyPassphrase() {
30 | guard let result = MnemonicUtil.seedString(from: mnemonic, passphrase: "") else {
31 | fatalError()
32 | }
33 |
34 | // Empty passphrase should be the same as no passphrase.
35 | XCTAssertEqual(result, expectedSeedStringNoPassphrase)
36 | }
37 |
38 | public func testSeedStringFromMnemonicWithPassphrase() {
39 | guard let result = MnemonicUtil.seedString(from: mnemonic, passphrase: passphrase) else {
40 | fatalError()
41 | }
42 |
43 | // Empty passphrase should be the same as no passphrase.
44 | XCTAssertEqual(result, expectedSeedStringWithPassphrase)
45 | }
46 |
47 | public func testValidateMnemonic() {
48 | // Valid mnemonic.
49 | let validMnemonic =
50 | "pear peasant pelican pen pear peasant pelican pen pear peasant pelican pen pear peasant pelican pen"
51 | XCTAssertTrue(MnemonicUtil.validate(mnemonic: validMnemonic))
52 |
53 | // Invalid mnemonic.
54 | let invalidMnemonic = "slacktivist snacktivity snuggie"
55 | XCTAssertFalse(MnemonicUtil.validate(mnemonic: invalidMnemonic))
56 |
57 | // Empty string should be invalid.
58 | XCTAssertFalse(MnemonicUtil.validate(mnemonic: ""))
59 |
60 | // Unknown languages don't validate.
61 | let spanishMnemonic = "pera campesina pelican"
62 | XCTAssertFalse(MnemonicUtil.validate(mnemonic: spanishMnemonic))
63 |
64 | // Mixed cases should be normalized.
65 | let mixedCaseMnemonic = "pear PEASANT PeLiCaN pen"
66 | XCTAssertTrue(MnemonicUtil.validate(mnemonic: mixedCaseMnemonic))
67 |
68 | // Mixed valid words and invalid words should be invalid.
69 | let mixedLanguageMnemonic = "pear peasant pelican pen 路 级 少 图"
70 | XCTAssertFalse(MnemonicUtil.validate(mnemonic: mixedLanguageMnemonic))
71 |
72 | // Whitespace padding shouldn't matter.
73 | let whitespacePaddedMnemonic = " pear peasant pelican pen\t\t\n"
74 | XCTAssertTrue(MnemonicUtil.validate(mnemonic: whitespacePaddedMnemonic))
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Tests/IntegrationTests/ManagerContractClientIntegrationTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019.
2 |
3 | @testable import TezosKit
4 | import XCTest
5 |
6 | /// Integration tests to run against a Manager.tz Contract. These tests require a live alphanet node.
7 | ///
8 | /// To get an alphanet node running locally, follow instructions here:
9 | /// https://tezos.gitlab.io/alphanet/introduction/howtoget.html
10 | ///
11 | /// These tests are not hermetic and may fail for a number or reasons, such as:
12 | /// - Insufficient balance in account.
13 | /// - Adverse network conditions.
14 | ///
15 | /// Before running the tests, you should make sure that there's sufficient tokens in the owners account (which is
16 | /// tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW) in the token contract at:
17 | /// Token Contract: https://better-call.dev/babylon/KT1VPVdNiWskBEVHF3pWdxyxepj4ZaWTGKgz/script
18 | /// Address: https://babylonnet.tzstats.com/tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW
19 |
20 | extension Address {
21 | public static let managerContractAddress = "KT1VPVdNiWskBEVHF3pWdxyxepj4ZaWTGKgz"
22 | public static let managerContractDelegate = "tz1aNJyLu5HbwjKv1e6msaEQoTPy9tv6d9oE"
23 | }
24 |
25 | class ManagerContractClientIntegrationTests: XCTestCase {
26 | public var nodeClient: TezosNodeClient!
27 | public var managerClient: ManagerContractClient!
28 |
29 | public override func setUp() {
30 | super.setUp()
31 |
32 | let nodeClient = TezosNodeClient(remoteNodeURL: .nodeURL)
33 | managerClient = ManagerContractClient(
34 | contractAddress: .managerContractAddress,
35 | tezosNodeClient: nodeClient
36 | )
37 | }
38 |
39 | public func testDelegate() {
40 | let completionExpectation = XCTestExpectation(description: "Completion called")
41 |
42 | self.managerClient.delegate(
43 | to: .managerContractDelegate,
44 | signatureProvider: Wallet.testWallet
45 | ) { result in
46 | switch result {
47 | case .success:
48 | completionExpectation.fulfill()
49 | case .failure:
50 | XCTFail()
51 | }
52 | }
53 |
54 | wait(for: [ completionExpectation ], timeout: .expectationTimeout)
55 | }
56 |
57 | public func testUndelegate() {
58 | let completionExpectation = XCTestExpectation(description: "Completion called")
59 |
60 | self.managerClient.undelegate(
61 | signatureProvider: Wallet.testWallet
62 | ) { result in
63 | switch result {
64 | case .success:
65 | completionExpectation.fulfill()
66 | case .failure:
67 | XCTFail()
68 | }
69 | }
70 |
71 | wait(for: [ completionExpectation ], timeout: .expectationTimeout)
72 | }
73 |
74 | public func testTransfer() {
75 | let completionExpectation = XCTestExpectation(description: "Completion called")
76 |
77 | self.managerClient.transfer(
78 | to: Wallet.testWallet.address,
79 | amount: Tez(0.5),
80 | signatureProvider: Wallet.testWallet
81 | ) { result in
82 | switch result {
83 | case .success:
84 | completionExpectation.fulfill()
85 | case .failure:
86 | XCTFail()
87 | }
88 | }
89 |
90 | wait(for: [ completionExpectation ], timeout: .expectationTimeout)
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/docs/Operations.md:
--------------------------------------------------------------------------------
1 | # Operations
2 |
3 | Operations are an advanced topic which most users of TezosKit will not need to know about.
4 |
5 | Any injectable operation in the Tezos blockchain is represented by an object that inherits from `AbstractOperation`, which conforms to the `Operation` protocol.
6 |
7 | ## Supported Operations
8 |
9 | Common operations on Tezos are supported out of the box on TezosKit. These operations include:
10 | * Sending Tezzies
11 | * Calling Smart Contracts
12 | * Delegating the Balance of an Account
13 | * Originating a new Account
14 |
15 | Users of these common operations will never have to work with `Operation` objects directly and can instead call associated methdos on `TezosNodeClient`.
16 |
17 | ## Custom Operations
18 |
19 | Users of TezosKit can create and inject custom operations. Creating a custom operation requires some wiring, but is fairly straightforward.
20 |
21 | ### Creating an Operation
22 |
23 | TezosKit can inject any operation which conforms to the `Operation` protocol. Users can wire up custom operations. In most cases, you'll want to subclass `AbstractOperation` to get baseline functionality.
24 |
25 | The `Operation` protocol requires three properties to be implemented:
26 | * `requiresReveal`: If `true` then TezosKit will automatically add a reveal operation any time this operation is applied to an unrevealed account.
27 | * `defaultFees`: An `OperationFees` object which represents the default fees for the operation.
28 | * `dictionaryRepresentation`: A representation of the dictionary in a `Dictionary`.
29 |
30 | Note that `dictionaryRepresentation` contains only the values represented by the operation. Values like `signature` and `branch` are not required. For instance, a `dictionaryRepresentation` of a `TransactionOperation` looks like:
31 | ```swift
32 | [
33 | "source": "tz1...",
34 | "destination": "tz1...",
35 | "amount": "123",
36 | "kind": "transaction",
37 | "fee": "1",
38 | "gas_limit": "1",
39 | "storage_limit": "1"
40 | ]
41 | ```
42 |
43 | ### Injecting an Operation
44 |
45 | `TezosNodeClient` provides a method which lets you inject a custom operation. Injecting a custom operation in the node is easy:
46 |
47 | ```swift
48 | let wallet = Wallet(...)
49 | let tezosNodeClient = TezosNodeClient(...)
50 | let customOperation = MyCustomOperation(...)
51 |
52 | tezosNodeClient.forgeSignPreapplyAndInject(
53 | customOperation,
54 | source: wallet.address,
55 | keys: wallet.keys
56 | ) { result in
57 | // Handle result hash
58 | }
59 |
60 | ```
61 |
62 | Or if you'd like to inject multiple operations, pass them as an array. Operations are processed in the order they are inserted into the array:
63 |
64 | ```swift
65 | let wallet = Wallet(...)
66 | let tezosNodeClient = TezosNodeClient(...)
67 | let transactionOperation = TransactionOperation(...)
68 | let customOperation = MyCustomOperation(...)
69 |
70 | tezosNodeClient.forgeSignPreapplyAndInject(
71 | [customOperation, transactionOperation],
72 | source: wallet.address,
73 | keys: wallet.keys
74 | ) { result in
75 | // Handle result hash
76 | }
77 |
78 | ```
79 |
80 | Like all `TezosNodeClient` methods, injection of single and multiple operations is supported with closure based callbacks or with Promises.
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | osx_image: xcode11.3
2 | language: swift
3 | cache:
4 | bundler: true
5 | directories:
6 | - Carthage
7 | env:
8 | secure: ink5jhbtR9aGbiVzcrPP+jBLtYKmZ61uxmXmqwqsyAZip2zbL/tktXdLaeNhUnlJHazZF156rOFe5DFSElFmNYvpSZrXJCVMxwWKB+MJWQ879LUk6W4W65jTvkXKZmMrrUqwkOTNNMq0ycfs5tx+eFJBzHd3Z93OX0oKwb44ZjC1NC05LGJfOlZQs+7IWYKXu0XKpZ/Zo+n4jGxMPEaQCMifz8ac8WVAgsgXs5R3ANrAd6AtBHZqNLu+NzGi5FQ2u7M1EERWo69qAmmHQYKB9aYyERi4cU6KD3XF8B89szR3pH41avfYFs+ZqenF7yAm0dN5UoLBqHA5s+yBQ1kzZbvcCunEU88XjgeRuzwTUfyTq8RIN+BNzaAwSCgu/fNMaFLOZjYAXK+gtVm2elx69maC6N+hKpZblmgUtmaOQyXzY3pVNXdqCqqqzIrqSyvqPuuNSzLxjVFJUW8ycYEfB4Iywcwl74IlxWJ58WpeCgMW3cVE4l4kxw+ZWoVniumpVlp69QtgwHloUosOzmzoIwLjgEm6uGZvr3DIKKXwLZACtq8Oj7uhm8iNV4sU5eS0w+b+VC6gYCxI4yOZHWhwtBG4QtrKlEvXC0Gjy4rIXN0JuKiAHLZKiCr0YuCmks2xXv2INNnJAIBVlJ7URVY4OSY+eqCiWUSobO8n48Zu7gk=
9 |
10 | jobs:
11 | include:
12 | - stage: Build - CocoaPods
13 | install:
14 | - gem install cocoapods
15 | script:
16 | # Optimize CI performance by only building for iOS and skipping testing.
17 | # Sources are provided to compile for MacOS in the Carthage phase below. Tests are also
18 | # run in the Carthage phase below.
19 | - travis_wait 500 pod lib lint --platforms=ios --skip-tests
20 | - stage: Build - Carthage And Code Coverage
21 | install:
22 | # Install Carthage by building from source. This works around a bug with Carthage and PromiseKit which
23 | # is resolved in master, but not in a Carthage release yet.
24 | # Note: When re-enabling 'brew update' likely needs to be run.
25 | # TODO(keefertaylor): Remove this when the next version of Carthage is released.
26 | #
27 | # Issue: https://github.com/Carthage/Carthage/issues/2760
28 | # Fix: https://github.com/Carthage/Carthage/pull/2908
29 | - mkdir -p carthage-build
30 | - cd carthage-build
31 | - git clone https://github.com/Carthage/Carthage && cd Carthage && git checkout f132af8e85eb1da84f5d4378ca0e2cdb3b87f598
32 | - sudo make install
33 | - which carthage
34 | - carthage version
35 | - cd ../..
36 | - sudo rm -rf carthage-build
37 |
38 | - brew install xcodegen
39 | - gem install slather
40 | script:
41 | # Carthage requires that XCode project files are checked in. Project.yml is
42 | # the canonical definition for the .xcodeproj file. Remove the .xcodeproj file
43 | # and regenerate it.
44 | # See: https://github.com/Carthage/Carthage/issues/2684
45 | - rm -rf TezosKit.xcodeproj
46 | - xcodegen generate
47 |
48 | # Build with Carthage
49 | - travis_wait 500 carthage bootstrap --platform iOS,mac --no-use-binaries --cache-builds
50 | - set -o pipefail && xcodebuild test -scheme TezosKit_macOS -destination 'platform=macOS,arch=x86_64' ONLY_ACTIVE_ARCH=YES | xcpretty
51 | - set -o pipefail && xcodebuild test -scheme TezosKit_iOS -destination 'platform=iOS Simulator,name=iPhone XS,OS=12.2' ONLY_ACTIVE_ARCH=YES | xcpretty
52 |
53 | after_success:
54 | # Generate and Upload Code Coverage
55 | - slather
56 | - bash <(curl -s https://codecov.io/bash) -f ./cobertura.xml
57 |
58 |
59 |
--------------------------------------------------------------------------------
/docs/Networking.md:
--------------------------------------------------------------------------------
1 | # Networking and RPCs
2 |
3 | Networking and RPCs is an advanced topic which most users of TezosKit will not have to understand or utilize.
4 |
5 | ## Network Stack
6 |
7 | Internally, TezosKit uses a `URLSession` based network stack. The default session is the `shared` session. Users can inject their own session in the initializer of either `TezosNodeClient` or `ConseilClient`.
8 |
9 | ## Components of Networking
10 |
11 | TezosKit's networking and request / response handling is comprised of three major components:
12 | * RPCs: Encapsulate the request to the node, including the endpoint to request, headers to send, and payload data.
13 | * Response Adapters: Encapsulates parsing the data returned from the API to the expected format
14 | * `NetworkClient`: A class which mediates the interaction between `URLSession`, RPC objects and Response Adapters
15 |
16 | ### `NetworkClient`
17 |
18 | `NetworkClient` is a class which contians the core logic for transcribing `RPC` and `ResponseAdapter` objects into requests made to the network. `NetworkClient` provdes a closure callback style API as well as a [PromiseKit](https://github.com/mxcl/Promisekit) style API.
19 |
20 | A `NetworkClient` is instantiated internally in `ConseilClient` and `TezosClient`. `NetworkClients` are immutable and injected into subcomponents of the clients.
21 |
22 | ### Response Adapters
23 |
24 | `URLSession` returns data from the server. Generally, consumers of an API prefer a more structured representation of that data (either as `dictionary` or some other first class object).
25 |
26 | Response Adapter objects parse `Data` to a requested type. The `ResponseAdapter` protocol defines a generic adapter that has an associated type it will parse to. The protocol defines a single method which transforms `Data` to an optional of the associated type.
27 |
28 | Many types of response adapters are provided by default, but users can create their own as well.
29 |
30 | ### RPCs
31 |
32 | The `RPC` class defines an RPC that will make a request to a node and return a response. An `RPC` is generic in `T`, which is the expected type of a response.
33 |
34 | RPCs are expected to be initialized with the following properties:
35 | * endpoint: The endpoint that the request will be made to on the node.
36 | * headers: Headers that will be sent with the RPC. Note that headers for all RPCs can be set on the Abstract node. Headers set on an RPC will override these headers.
37 | * responseAdapterClass: A `ResponseAdapter` that is generic in the same type of the RPC. This adapter will change the returned data to the requested output.
38 | * payload: A payload to be sent with the request.
39 |
40 | ## Building a Custom RPC
41 |
42 | It's easy to build a custom RPC.
43 |
44 | First, find or create a `ResponseAdapter` object that will coerce `Data` to the preferred output of the RPC. If a custom response adapter is required, users should subclass `AbstractResponseAdapter`.
45 |
46 | Second, subclass `RPC` and initialize the super class with the required functionality.
47 |
48 | Lastly, use a generic send method on a client to send the RPC:
49 | ```swift
50 | let nodeClient = TezosNodeClient() // You can also use ConseilClient.
51 | let myCustomRPC = MyCustomRPC(...)
52 | nodeClient.send(myCustomRPC) { result in
53 | // handle result.
54 | }
55 | ````
56 |
--------------------------------------------------------------------------------
/docs/Testing.md:
--------------------------------------------------------------------------------
1 | # Testing
2 |
3 | TezosKit makes use of both unit tests and integration tests to provide confidence in the reliability of the underlying software.
4 |
5 | ## Unit Tests
6 |
7 | Unit tests are used wherever applicable. All unit tests are hermetic and count towards the code coverage metrics.
8 |
9 | ### Running Unit Tests
10 |
11 | Open `TezosKit.xcodeproj`, select the `TezosKitTests` target from the drop down menu and run the unit tests from the XCTest UI.
12 |
13 | ## Integration Tests
14 |
15 | TezosKit comes bundled with integration tests to ensure that the code works against a live node. These tests are not hermetic. However, they do allow thorough testing of the code in real life scenarios.
16 |
17 | As these tests are non-hermetic, you may find that they flake or fail occasionally due to external factors such as:
18 | - Poor connectivity / network conditions
19 | - Adverse network conditions (i.e. A large amount of bakers are offline)
20 | - Adverse network activity (i.e. All blocks are full)
21 |
22 | ### Running Integration Tests
23 |
24 | Running integration tests requires some setup as a custom environment is needed.
25 |
26 | #### Tezos Node Integration Tests
27 |
28 | Running Tezos Node integration tests requires having a running Tezos Node. The integration tests assume that the live node will be running on Alphanet.
29 |
30 | To get an Alphanet node up and running, follow instructions [here](https://tezos.gitlab.io/alphanet/introduction/howtoget.html).
31 |
32 | The integration tests assume that the test account on Alphanet has a sufficient balance (~100 XTZ) to run the tests. The alphanet test account is [tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW](https://alphanet.tzscan.io/tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW). If the test account needs to be topped up, follow instructions to load additional XTZ from the Alphanet fauce [here](https://tezos.gitlab.io/alphanet/introduction/howtouse.html#faucet).
33 |
34 | These tests also utilize a token and Dexter Exchange contract, located at:
35 | https://alphanet.tzscan.io/KT1RrfbcDM5eqho4j4u5EbqbaoEFwBsXA434 and https://alphanet.tzscan.io/KT1Md4zkfCvkdqgxAC9tyRYpRUBKmD1owEi2.
36 |
37 | (For more information about Dexter, see: https://gitlab.com/camlcase-dev/dexter/blob/master/docs/dexter-cli.md)
38 |
39 |
40 | The integration tests assume that an alphanet node is running at the default location, `http://127.0.0.1:8732`. If this is not true, you may change `nodeUrl` in `TezosNodeIntegrationTests.swift` to point to the correct location.
41 |
42 | With setup complete, open `TezosKit.xcodeproj`, select the `IntegrationTests` target from the drop down menu and run the `TezosNodeIntegrationTests` tests from the XCTest UI.
43 |
44 | #### Conseil Integration Tests
45 |
46 | Running Conseil integration tests requires having a running Conseil service. You can find out more about running your own Conseil service [here](https://github.com/Cryptonomic/Conseil/blob/master/doc/use-conseil.md).
47 |
48 | The integration tests will need to know the location and API Key for the Conseil service it will connect to. Open `Tests/IntegrationTests/Conseil/ConseilIntegrationTests` and set the appropriate values for `remoteNodeURL` and `apiKey` at the top of the file.
49 |
50 | With setup complete, open `TezosKit.xcodeproj`, select the `IntegrationTests` target from the drop down menu and run the `ConseilIntegrationTests` tests from the XCTest UI.
51 |
--------------------------------------------------------------------------------
/TezosKit/TezosNode/Models/Operation/OperationResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OperationResponse.swift
3 | // SecureEnclaveExample
4 | //
5 | // Created by Simon Mcloughlin on 17/03/2020.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Codable version of the response object that is returned by the Tezos RPC
11 | struct OperationResponse: Codable {
12 | let contents: [OperationResponseContent]
13 |
14 | /// Check if the operation(s) has been backtracked or reversed due to a failure
15 | func isFailed() -> Bool {
16 | for content in contents {
17 | if content.metadata.operationResult.status == "backtracked" || content.metadata.operationResult.status == "failed" {
18 | return true
19 | }
20 | }
21 |
22 | return false
23 | }
24 |
25 | /// Return the last error object from each internal result. The last error object is the one that contains the location of the error in the smart contract and the `with` string, giving the most debugable information
26 | func errors() -> [OperationResponseInternalResultError] {
27 | var errors: [OperationResponseInternalResultError] = []
28 |
29 | for content in contents {
30 | if let operationError = content.metadata.operationResult.errors?.last {
31 | errors.append(operationError)
32 | }
33 |
34 | if let internalOperationResults = content.metadata.internalOperationResults {
35 | for internalResult in internalOperationResults {
36 | if let error = internalResult.result.errors?.last {
37 | errors.append(error)
38 | }
39 | }
40 | }
41 | }
42 |
43 | return errors
44 | }
45 | }
46 |
47 | struct OperationResponseContent: Codable {
48 | let kind: String
49 | let source: String
50 | let metadata: OperationResponseMetadata
51 | }
52 |
53 | struct OperationResponseMetadata: Codable {
54 | let operationResult: OperationResponseResult
55 | let internalOperationResults: [OperationResponseInternalOperation]?
56 |
57 | private enum CodingKeys: String, CodingKey {
58 | case operationResult = "operation_result"
59 | case internalOperationResults = "internal_operation_results"
60 | }
61 | }
62 |
63 | struct OperationResponseResult: Codable {
64 | let status: String
65 | let consumedGas: String?
66 | let storageSize: String?
67 | let errors: [OperationResponseInternalResultError]?
68 |
69 | private enum CodingKeys: String, CodingKey {
70 | case status
71 | case consumedGas = "consumed_gas"
72 | case storageSize = "storage_size"
73 | case errors
74 | }
75 | }
76 |
77 | struct OperationResponseInternalOperation: Codable {
78 | let kind: String
79 | let source: String
80 | let result: OperationResponseInternalResult
81 | }
82 |
83 | struct OperationResponseInternalResult: Codable {
84 | let status: String
85 | let errors: [OperationResponseInternalResultError]?
86 |
87 | func isFailed() -> Bool {
88 | return status == "failed"
89 | }
90 | }
91 |
92 | public struct OperationResponseInternalResultError: Codable, Equatable {
93 | public let kind: String
94 | public let id: String
95 | public let location: Int?
96 | public let with: OperationResponseInternalResultErrorWith?
97 | }
98 |
99 | public struct OperationResponseInternalResultErrorWith: Codable, Equatable {
100 | public let string: String
101 | }
102 |
--------------------------------------------------------------------------------
/TezosKit/Crypto/CryptoUtils.swift:
--------------------------------------------------------------------------------
1 | // Copyright Keefer Taylor, 2019
2 |
3 | import Base58Swift
4 | import CommonCrypto
5 | import CryptoSwift
6 | import Foundation
7 | import Sodium
8 |
9 | /// A static helper class that provides utility functions for cyptography.
10 | public enum CryptoUtils {
11 | /// Check that a given address is valid public key hash.
12 | public static func validateAddress(address: String) -> Bool {
13 | // Decode bytes. This call verifies the checksum is correct.
14 | guard let decodedBytes = Base58.base58CheckDecode(address) else {
15 | return false
16 | }
17 |
18 | // Check that the prefix is correct.
19 | for (i, byte) in Prefix.Address.tz1.enumerated() where decodedBytes[i] != byte {
20 | return false
21 | }
22 |
23 | return true
24 | }
25 |
26 | /// Convert the given input bytes to hex.
27 | public static func binToHex(_ bin: [UInt8]) -> String? {
28 | return Sodium.shared.utils.bin2hex(bin)
29 | }
30 |
31 | /// Convert the given hex to binary.
32 | public static func hexToBin(_ hex: String) -> [UInt8]? {
33 | return Sodium.shared.utils.hex2bin(hex)
34 | }
35 |
36 | /// Convert signature bytes to their base58 representation.
37 | public static func base58(signature: [UInt8], signingCurve: EllipticalCurve) -> String? {
38 | switch signingCurve {
39 | case .ed25519:
40 | return Base58.encode(message: signature, prefix: Prefix.Keys.Ed25519.signature)
41 | case .secp256k1:
42 | return Base58.encode(message: signature, prefix: Prefix.Keys.Secp256k1.signature)
43 | case .p256:
44 | return Base58.encode(message: signature, prefix: Prefix.Keys.P256.signature)
45 | }
46 | }
47 |
48 | /// Create injectable hex bytes from the given hex operation and signature bytes
49 | public static func injectableHex(_ hex: String, signature: [UInt8]) -> String? {
50 | guard let signatureHex = binToHex(signature) else {
51 | return nil
52 | }
53 | return injectableHex(hex, signatureHex: signatureHex)
54 | }
55 |
56 | /// Create injectable hex bytes from the given hex operation and a hex signature.
57 | public static func injectableHex(_ hex: String, signatureHex: String) -> String {
58 | return hex + signatureHex
59 | }
60 |
61 | /// Compress a 65 byte public key to a 33 byte public key.
62 | ///
63 | /// Tezos expects usage of compressed keys.
64 | public static func compressKey(_ bytes: [UInt8]) -> [UInt8]? {
65 | // A magic byte 0x04 indicates that the key is uncompressed. Compressed keys use 0x02 and 0x03 to indicate the
66 | // key is compressed and the value of the Y coordinate of the keys.
67 | let rawPublicKeyBytes = Array(bytes)
68 | guard
69 | let firstByte = rawPublicKeyBytes.first,
70 | let lastByte = rawPublicKeyBytes.last,
71 | // Expect an uncompressed key to have length = 65 bytes (two 32 byte coordinates, and 1 magic prefix byte)
72 | rawPublicKeyBytes.count == 65,
73 | // Expect the first byte of the public key to be a magic 0x04 byte, indicating an uncompressed key.
74 | firstByte == 4
75 | else {
76 | return nil
77 | }
78 |
79 | // Assign a new magic byte based on the Y coordinate's parity.
80 | // See: https://bitcointalk.org/index.php?topic=644919.0
81 | let magicByte: [UInt8] = lastByte % 2 == 0 ? [2] : [3]
82 | let xCoordinateBytes = rawPublicKeyBytes[1...32]
83 | return magicByte + xCoordinateBytes
84 | }
85 | }
86 |
--------------------------------------------------------------------------------