diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..4f1e210 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..6e8ae8c --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,24 @@ +on: + push: + branches: + - main + pull_request: + types: + - opened + - reopened + - synchronize + +jobs: + test: + runs-on: macos-latest + steps: + - name: checkout + uses: actions/checkout@v3 + - name: xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: "14.2.0" + - name: package + run: xcodebuild test -scheme Web3SwiftMpcProvider -destination "platform=iOS Simulator,OS=16.2,name=iPhone 14" COMPILER_INDEX_STORE_ENABLE=NO + - name: SwiftLint + run: swiftlint lint ./Sources --reporter github-actions-logging \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c1de6b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,97 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm/ + +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ +.idea +.DS_Store diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..c811411 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,151 @@ +{ + "object": { + "pins": [ + { + "package": "BigInt", + "repositoryURL": "https://github.com/attaswift/BigInt.git", + "state": { + "branch": null, + "revision": "0ed110f7555c34ff468e72e1686e59721f2b0da6", + "version": "5.3.0" + } + }, + { + "package": "CryptoSwift", + "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", + "state": { + "branch": null, + "revision": "32f641cf24fc7abc1c591a2025e9f2f572648b0f", + "version": "1.7.2" + } + }, + { + "package": "GenericJSON", + "repositoryURL": "https://github.com/iwill/generic-json-swift", + "state": { + "branch": null, + "revision": "0a06575f4038b504e78ac330913d920f1630f510", + "version": "2.0.2" + } + }, + { + "package": "secp256k1", + "repositoryURL": "https://github.com/GigaBitcoin/secp256k1.swift.git", + "state": { + "branch": null, + "revision": "1a14e189def5eaa92f839afdd2faad8e43b61a6e", + "version": "0.12.2" + } + }, + { + "package": "SocketIO", + "repositoryURL": "https://github.com/socketio/socket.io-client-swift", + "state": { + "branch": null, + "revision": "af5ce97b755d964235348d96f6db5cbdcbe334a5", + "version": "16.0.1" + } + }, + { + "package": "Starscream", + "repositoryURL": "https://github.com/daltoniam/Starscream", + "state": { + "branch": null, + "revision": "df8d82047f6654d8e4b655d1b1525c64e1059d21", + "version": "4.0.4" + } + }, + { + "package": "swift-atomics", + "repositoryURL": "https://github.com/apple/swift-atomics.git", + "state": { + "branch": null, + "revision": "6c89474e62719ddcc1e9614989fff2f68208fe10", + "version": "1.1.0" + } + }, + { + "package": "swift-collections", + "repositoryURL": "https://github.com/apple/swift-collections.git", + "state": { + "branch": null, + "revision": "937e904258d22af6e447a0b72c0bc67583ef64a2", + "version": "1.0.4" + } + }, + { + "package": "swift-log", + "repositoryURL": "https://github.com/apple/swift-log.git", + "state": { + "branch": null, + "revision": "532d8b529501fb73a2455b179e0bbb6d49b652ed", + "version": "1.5.3" + } + }, + { + "package": "swift-nio", + "repositoryURL": "https://github.com/apple/swift-nio.git", + "state": { + "branch": null, + "revision": "cf281631ff10ec6111f2761052aa81896a83a007", + "version": "2.58.0" + } + }, + { + "package": "swift-nio-extras", + "repositoryURL": "https://github.com/apple/swift-nio-extras.git", + "state": { + "branch": null, + "revision": "0e0d0aab665ff1a0659ce75ac003081f2b1c8997", + "version": "1.19.0" + } + }, + { + "package": "swift-nio-ssl", + "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", + "state": { + "branch": null, + "revision": "320bd978cceb8e88c125dcbb774943a92f6286e9", + "version": "2.25.0" + } + }, + { + "package": "swift-nio-transport-services", + "repositoryURL": "https://github.com/apple/swift-nio-transport-services.git", + "state": { + "branch": null, + "revision": "e7403c35ca6bb539a7ca353b91cc2d8ec0362d58", + "version": "1.19.0" + } + }, + { + "package": "tss-client-swift", + "repositoryURL": "https://github.com/torusresearch/tss-client-swift.git", + "state": { + "branch": null, + "revision": "a5f54880550d7c0f2979e06290a1ac0941626b3f", + "version": "1.0.12" + } + }, + { + "package": "web3.swift", + "repositoryURL": "https://github.com/argentlabs/web3.swift", + "state": { + "branch": null, + "revision": "8ca33e700ed8de6137a0e1471017aa3b3c8de0db", + "version": "1.6.0" + } + }, + { + "package": "websocket-kit", + "repositoryURL": "https://github.com/vapor/websocket-kit.git", + "state": { + "branch": null, + "revision": "53fe0639a98903858d0196b699720decb42aee7b", + "version": "2.14.0" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..4fb4a51 --- /dev/null +++ b/Package.swift @@ -0,0 +1,31 @@ +// swift-tools-version: 5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Web3SwiftMpcProvider", + platforms: [.iOS(.v14)], + products: [ + .library( + name: "Web3SwiftMpcProvider", + targets: ["Web3SwiftMpcProvider"]), + + ], + dependencies: [ + .package(url: "https://github.com/argentlabs/web3.swift", from:"1.6.0"), + .package(url: "https://github.com/torusresearch/tss-client-swift.git", from: "1.0.12"), + ], + targets: [ + .target( + name: "Web3SwiftMpcProvider", + dependencies: ["web3.swift", "tss-client-swift"], + path: "Sources/Web3SwiftMpcProvider" + ), + .testTarget( + name: "Web3SwiftMpcProviderTests", + dependencies: ["Web3SwiftMpcProvider"], + path: "Tests"), + ], + swiftLanguageVersions: [.v5] +) diff --git a/README.md b/README.md index ff5ddb3..9f122bb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# mpc-signing-provider-swift +# MpcProvider A description of this package. diff --git a/Sources/.DS_Store b/Sources/.DS_Store new file mode 100644 index 0000000..ca8ef62 Binary files /dev/null and b/Sources/.DS_Store differ diff --git a/Sources/Web3SwiftMpcProvider/EthTssAccountParams.swift b/Sources/Web3SwiftMpcProvider/EthTssAccountParams.swift new file mode 100644 index 0000000..719622c --- /dev/null +++ b/Sources/Web3SwiftMpcProvider/EthTssAccountParams.swift @@ -0,0 +1,42 @@ +public final class EthTssAccountParams { + private(set) var publicKey: String + private(set) var factorKey: String + private(set) var tssNonce: Int32 + private(set) var tssShare: String + private(set) var tssIndex: String + private(set) var selectedTag: String + private(set) var verifier: String + private(set) var verifierID: String + private(set) var nodeIndexes: [Int] + private(set) var tssEndpoints: [String] + private(set) var authSigs: [String] + /// Instantiates EthTssAccountParams which are used to instantiate an EtheriumTssAccount + /// + /// - Parameters: + /// - publicKey : Public key for the account, EtheriumAddress is derived from this. + /// - factorKey: The factor key + /// - tssNonce: The current tss nonce + /// - tssShare: The current tss share + /// - tssIndex: The index corresponding to the tssShare + /// - selectedTag: The current tss tag + /// - verifier: The verifier for the account + /// - verifierID: The identifier for the account + /// - nodeIndexes: The node indexes returned form the sapphire network + /// - tssEndpoints: The tss endpoints for the sapphire network + /// - authSigs: The signatures returned for the sapphire network + /// + // swiftlint:disable:next line_length + public init(publicKey: String, factorKey: String, tssNonce: Int32, tssShare: String, tssIndex: String, selectedTag: String, verifier: String, verifierID: String, nodeIndexes: [Int], tssEndpoints: [String], authSigs: [String]) { + self.publicKey = publicKey + self.factorKey = factorKey + self.tssNonce = tssNonce + self.tssShare = tssShare + self.tssIndex = tssIndex + self.selectedTag = selectedTag + self.verifier = verifier + self.verifierID = verifierID + self.nodeIndexes = nodeIndexes + self.tssEndpoints = tssEndpoints + self.authSigs = authSigs + } +} diff --git a/Sources/Web3SwiftMpcProvider/EthereumTssAccount.swift b/Sources/Web3SwiftMpcProvider/EthereumTssAccount.swift new file mode 100644 index 0000000..ad391ac --- /dev/null +++ b/Sources/Web3SwiftMpcProvider/EthereumTssAccount.swift @@ -0,0 +1,260 @@ +import BigInt +import Foundation +import secp256k1 +import tss_client_swift +import web3 + +enum CustomSigningError: Error { + case generalError(error: String = "") + + public var errorDescription: String { + switch self { + case let .generalError(err): + return err + } + } +} + +enum EthereumSignerError: Error { + case emptyRawTransaction + case unknownError +} + +public final class EthereumTssAccount: EthereumAccountProtocol { + public var address: web3.EthereumAddress + public var ethAccountParams: EthTssAccountParams + + /// Instantiates an EtheriumTssAccount + /// + /// - Parameters: + /// - params : Parameters used to initialize the account + /// + public required init(params: EthTssAccountParams) { + ethAccountParams = params + // swiftlint:disable:next line_length + address = EthereumAddress(KeyUtil.generateAddress(from: Data(hex: ethAccountParams.publicKey).suffix(64)).toChecksumAddress()) + } + + /// Signs using provided Data + /// + /// - Parameters: + /// - data : Data to be signed + /// + /// - Returns: `Data` + /// + /// - Throws: On signing failure + public func sign(data: Data) throws -> Data { + let hash = data.sha3(.keccak256) + let signature = try sign(message: hash) + return signature + } + + /// Signs using provided Hex String + /// + /// - Parameters: + /// - hex : Hex string to be signed + /// + /// - Returns: `Data` + /// + /// - Throws: On signing failure + public func sign(hex: String) throws -> Data { + if let data = Data(hex: hex) { + return try sign(data: data) + } else { + throw EthereumAccountError.signError + } + } + + /// Signs using provided hash + /// + /// - Parameters: + /// - hash : Hash to be used for signing + /// + /// - Returns: `Data` + /// + /// - Throws: On signing failure + public func sign(hash: String) throws -> Data { + if let data = hash.web3.hexData { + return try sign(message: data) + } else { + throw EthereumAccountError.signError + } + } + + /// Signs using provided message data + /// + /// - Parameters: + /// - message : message to be used for signing + /// + /// - Returns: `Data` + /// + /// - Throws: On signing failure + public func sign(message: Data) throws -> Data { + // Create tss Client using helper + let (client, coeffs) = try bootstrapTssClient(params: ethAccountParams) + + // Wait for sockets to be connected + let connected = try client.checkConnected() + if !connected { + throw EthereumSignerError.unknownError + } + + let precompute = try client.precompute(serverCoeffs: coeffs, signatures: ethAccountParams.authSigs) + + let ready = try client.isReady() + if !ready { + throw EthereumSignerError.unknownError + } + + let signingMessage = message.base64EncodedString() + + // swiftlint:disable:next identifier_name line_length + let (s, r, v) = try client.sign(message: signingMessage, hashOnly: true, original_message: nil, precompute: precompute, signatures: ethAccountParams.authSigs) + + try client.cleanup(signatures: ethAccountParams.authSigs) + + // swiftlint:disable:next line_length + let verified = TSSHelpers.verifySignature(msgHash: signingMessage, s: s, r: r, v: v, pubKey: Data(hex: ethAccountParams.publicKey)) + if !verified { + throw EthereumSignerError.unknownError + } + + // swiftlint:disable:next line_length + guard let signature = Data(hexString: try TSSHelpers.hexSignature(s: s, r: r, v: v)) else { throw EthereumSignerError.unknownError } + + return signature + } + + /// Signs using provided message string + /// + /// - Parameters: + /// - message : message to be used for signing + /// + /// - Returns: `Data` + /// + /// - Throws: On signing failure + public func sign(message: String) throws -> Data { + if let data = message.data(using: .utf8) { + return try sign(data: data) + } else { + throw EthereumAccountError.signError + } + } + + /// Signs using provided message data, prefixing the data first + /// + /// - Parameters: + /// - message : message to be used for signing + /// + /// - Returns: `String` + /// + /// - Throws: On signing failure + public func signMessage(message: Data) throws -> String { + let prefix = "\u{19}Ethereum Signed Message:\n\(String(message.count))" + guard var data = prefix.data(using: .ascii) else { + throw EthereumAccountError.signError + } + data.append(message) + let hash = data.web3.keccak256 + + var signed = try sign(message: hash) + + // Check last char (v) + guard var last = signed.popLast() else { + throw EthereumAccountError.signError + } + + if last < 27 { + last += 27 + } + + signed.append(last) + return signed.web3.hexString + } + + /// Signs using provided structured typed message (EIP712) + /// + /// - Parameters: + /// - message : message to be used for signing + /// + /// - Returns: `String` + /// + /// - Throws: On signing failure + public func signMessage(message: TypedData) throws -> String { + let hash = try message.signableHash() + + var signed = try sign(message: hash) + + // Check last char (v) + guard var last = signed.popLast() else { + throw EthereumAccountError.signError + } + + if last < 27 { + last += 27 + } + + signed.append(last) + return signed.web3.hexString + } + + /// Signs an ethereum transaction + /// + /// - Parameters: + /// - transaction : Transaction to be signed + /// + /// - Returns: `SignedTransaction` + /// + /// - Throws: On signing failure + public func sign(transaction: EthereumTransaction) throws -> SignedTransaction { + guard let raw = transaction.raw else { + throw EthereumSignerError.emptyRawTransaction + } + + // hash and sign data + let signed = try sign(data: raw) + + return SignedTransaction(transaction: transaction, signature: signed) + } + + private func bootstrapTssClient(params: EthTssAccountParams) throws -> (TSSClient, [String: String]) { + if params.publicKey.count < 128 || params.publicKey.count > 130 { + throw CustomSigningError.generalError(error: "Public Key should be in uncompressed format") + } + + // generate a random nonce for sessionID + guard let randomKey = SECP256K1.generatePrivateKey() else { + throw CustomSigningError.generalError(error: "Could not generate random key for sessionID nonce") + } + let randomKeyBigUint = BigUInt(randomKey) + let random = BigInt(sign: .plus, magnitude: randomKeyBigUint) + BigInt(Date().timeIntervalSince1970) + let sessionNonce = TSSHelpers.base64ToBase64url(base64: TSSHelpers.hashMessage(message: String(random))) + // create the full session string + // swiftlint:disable:next line_length + let session = TSSHelpers.assembleFullSession(verifier: params.verifier, verifierId: params.verifierID, tssTag: params.selectedTag, tssNonce: String(params.tssNonce), sessionNonce: sessionNonce) + + let userTssIndex = BigInt(params.tssIndex, radix: 16) ?? BigInt.zero + // total parties, including the client + let parties = params.nodeIndexes.count > 0 ? params.nodeIndexes.count + 1 : 4 + + // index of the client, last index of partiesIndexes + let clientIndex = Int32(parties - 1) + // swiftlint:disable:next line_length + let (urls, socketUrls, partyIndexes, nodeInd) = try TSSHelpers.generateEndpoints(parties: parties, clientIndex: Int(clientIndex), nodeIndexes: params.nodeIndexes, urls: params.tssEndpoints) + // swiftlint:disable:next line_length + let coeffs = try TSSHelpers.getServerCoefficients(participatingServerDKGIndexes: nodeInd.map({ BigInt($0) }), userTssIndex: userTssIndex) + + let shareUnsigned = BigUInt(params.tssShare, radix: 16) ?? BigUInt.zero + let share = BigInt(sign: .plus, magnitude: shareUnsigned) + // swiftlint:disable:next line_length + let denormalizeShare = try TSSHelpers.denormalizeShare(participatingServerDKGIndexes: nodeInd.map({ BigInt($0) }), userTssIndex: userTssIndex, userTssShare: share) + // swiftlint:disable:next line_length + let client = try TSSClient(session: session, index: Int32(clientIndex), parties: partyIndexes.map({ Int32($0) }), + // swiftlint:disable:next line_length + endpoints: urls.map({ URL(string: $0 ?? "") }), tssSocketEndpoints: socketUrls.map({ URL(string: $0 ?? "") }), + // swiftlint:disable:next line_length + share: TSSHelpers.base64Share(share: denormalizeShare), pubKey: try TSSHelpers.base64PublicKey(pubKey: Data(hex: params.publicKey))) + + return (client, coeffs) + } +} diff --git a/Sources/Web3SwiftMpcProvider/TorusWeb3Utils.swift b/Sources/Web3SwiftMpcProvider/TorusWeb3Utils.swift new file mode 100644 index 0000000..21837c8 --- /dev/null +++ b/Sources/Web3SwiftMpcProvider/TorusWeb3Utils.swift @@ -0,0 +1,84 @@ +import BigInt +import Foundation +import web3 + +public typealias Ether = Double +public typealias Wei = BigUInt + +enum TorusWeb3UtilsError: Error { + case conversionError +} + +public final class TorusWeb3Utils { + // NOTE: calculate wei by 10^18 + private static let etherInWei = pow(Double(10), 18) + private static let etherInGwei = pow(Double(10), 9) + + /// Converts Wei to Ether + /// + /// - Parameters: + /// - wei : Amount of Wei + /// + /// - Returns: `Ether` + /// + /// - Throws: On conversion failure + public static func toEther(wei: Wei) throws -> Ether { + guard let decimalWei = Double(wei.description) else { + throw TorusWeb3UtilsError.conversionError + } + return decimalWei / etherInWei + } + + /// Converts Gwei to Ether + /// + /// - Parameters: + /// - gwei : Amount of gwei + /// + /// - Returns: `Ether` + /// + /// - Throws: On conversion failure + public static func toEther(gwei: BigUInt) throws -> Ether { + guard let decimalWei = Double(gwei.description) else { + throw TorusWeb3UtilsError.conversionError + } + return decimalWei / etherInGwei + } + + /// Converts Ether to Wei + /// + /// - Parameters: + /// - ether : Amount of Ether + /// + /// - Returns: `Wei` + public static func toWei(ether: Ether) -> Wei { + let wei = Wei(ether * etherInWei) + return wei + } + + /// Converts Ether to Wei + /// + /// - Parameters: + /// - ether : Amount of Ether in string format + /// + /// - Returns: `Wei` + /// + /// - Throws: On conversion failure + public static func toWei(ether: String) throws -> Wei { + guard let decimalEther = Double(ether) else { + throw TorusWeb3UtilsError.conversionError + } + return toWei(ether: decimalEther) + } + + /// Converts Gwei to Wei for calculating gas prive and gas limit + /// + /// - Parameters: + /// - gwei : Amount of Gwei + /// + /// - Returns: `Wei` + /// + /// - Throws: On conversion failure + public static func toWei(gwei: Double) -> Wei { + return Wei(gwei * 1000000000) + } +} diff --git a/Tests/Web3SwiftMpcProviderTests/MpcProviderTests.swift b/Tests/Web3SwiftMpcProviderTests/MpcProviderTests.swift new file mode 100644 index 0000000..efd6fd7 --- /dev/null +++ b/Tests/Web3SwiftMpcProviderTests/MpcProviderTests.swift @@ -0,0 +1,120 @@ +import BigInt +import web3 +import Web3SwiftMpcProvider +import XCTest + +final class Web3SwiftMpcProviderTests: XCTestCase { + let example1 = """ + { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Account", + "wallet": "0x048975d4997d7578a3419851639c10318db430b6" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + } + """.data(using: .utf8)! + + let fullAddress = "04238569d5e12caf57d34fb5b2a0679c7775b5f61fd18cd69db9cc600a651749c3ec13a9367380b7a024a67f5e663f3afd40175c3223da63f6024b05d0bd9f292e" + let factorKey = "3b4af35bc4838471f94825f34c4f649904a258c0907d348bed653eb0c94ec6c0" + let tssNonce = 0 + let tssShare = "4f62ddd962fab8b0777bd18a2e6f3992c7e15ff929df79a15a7046da46af5a05" + let tssIndex = "2" + let selected_tag = "default" + let verifier = "google-lrc" + let verifierId = "hqjang95@gmail.com" + let tssEndpoints = ["https://sapphire-1.auth.network/tss", "https://sapphire-2.auth.network/tss", "https://sapphire-3.auth.network/tss", "https://sapphire-4.auth.network/tss", "https://sapphire-5.auth.network/tss"] + let sigs = ["{\"sig\":\"16de7c5812aedf492e7afe4a9c0607dba6d8d908d30ef1eb2e4761bc300bb3fc62bfbd0e94b03aa5eb496b5ed7adfa4203fa9745d90673cf789d3a989f872ae41b\",\"data\":\"eyJleHAiOjE2OTM0NjYxMTAsInRlbXBfa2V5X3giOiI2MTg3NTM3ZTc1YThhNWQ3NWQzZjhkMGZmYzE4NjMwNTRjYjEzNmE3YzRjYWVjNWRkYjUyZjViNmY1MTcyZDEwIiwidGVtcF9rZXlfeSI6ImFhNTNhNmE2N2YzOTE1NzNmYTA1YTVkZWViZjM2MDVkM2MzODljNjhjMDhlOGI5YzllNDQyODU1ZWYyYWE2ZTkiLCJ2ZXJpZmllcl9uYW1lIjoiZ29vZ2xlLWxyYyIsInZlcmlmaWVyX2lkIjoiaHFqYW5nOTVAZ21haWwuY29tIiwic2NvcGUiOiIifQ==\"}", "{\"sig\":\"50a7451f2a8af5f3e193b3e53768e3107f8d606ef5e9ee70aba15fba8e67a1be279d71f8d3b6a954beef5e5119a10195c3017e48b3f0a93b557ed9366ce38f171c\",\"data\":\"eyJleHAiOjE2OTM0NjYxMTAsInRlbXBfa2V5X3giOiI2MTg3NTM3ZTc1YThhNWQ3NWQzZjhkMGZmYzE4NjMwNTRjYjEzNmE3YzRjYWVjNWRkYjUyZjViNmY1MTcyZDEwIiwidGVtcF9rZXlfeSI6ImFhNTNhNmE2N2YzOTE1NzNmYTA1YTVkZWViZjM2MDVkM2MzODljNjhjMDhlOGI5YzllNDQyODU1ZWYyYWE2ZTkiLCJ2ZXJpZmllcl9uYW1lIjoiZ29vZ2xlLWxyYyIsInZlcmlmaWVyX2lkIjoiaHFqYW5nOTVAZ21haWwuY29tIiwic2NvcGUiOiIifQ==\"}", "{\"sig\":\"d94979a0f743a8a41630167622c5b443b148f231bb2293e60a17ab4ea7ebdf38713b81b0bc9161ecd3949ddcf8cfca9734f136ba02c2e4e670fb4b8523299ab01b\",\"data\":\"eyJleHAiOjE2OTM0NjYxMTAsInRlbXBfa2V5X3giOiI2MTg3NTM3ZTc1YThhNWQ3NWQzZjhkMGZmYzE4NjMwNTRjYjEzNmE3YzRjYWVjNWRkYjUyZjViNmY1MTcyZDEwIiwidGVtcF9rZXlfeSI6ImFhNTNhNmE2N2YzOTE1NzNmYTA1YTVkZWViZjM2MDVkM2MzODljNjhjMDhlOGI5YzllNDQyODU1ZWYyYWE2ZTkiLCJ2ZXJpZmllcl9uYW1lIjoiZ29vZ2xlLWxyYyIsInZlcmlmaWVyX2lkIjoiaHFqYW5nOTVAZ21haWwuY29tIiwic2NvcGUiOiIifQ==\"}"] + + let decoder = JSONDecoder() + + func testSigningMessage() throws { + let params = EthTssAccountParams(publicKey: fullAddress, factorKey: factorKey, tssNonce: Int32(tssNonce), tssShare: tssShare, tssIndex: tssIndex, selectedTag: selected_tag, verifier: verifier, verifierID: verifierId, nodeIndexes: [], tssEndpoints: tssEndpoints, authSigs: sigs) + + let account = EthereumTssAccount(params: params) + + let msg = "hello world" + let _ = try account.sign(message: msg) + } + + func testSigningTransaction() throws { + let params = EthTssAccountParams(publicKey: fullAddress, factorKey: factorKey, tssNonce: Int32(tssNonce), tssShare: tssShare, tssIndex: tssIndex, selectedTag: selected_tag, verifier: verifier, verifierID: verifierId, nodeIndexes: [], tssEndpoints: tssEndpoints, authSigs: sigs) + let tssAccount = EthereumTssAccount(params: params) + let chainID = 5 + let amount = 0.001 + let toAddress = tssAccount.address + let fromAddress = tssAccount.address + let gasPrice = BigUInt(938) + let maxTipInGwie = BigUInt(try TorusWeb3Utils.toEther(gwei: BigUInt(amount))) + let totalGas = gasPrice + maxTipInGwie + let gasLimit = BigUInt(21000) + + let amtInGwie = TorusWeb3Utils.toWei(ether: amount) + let nonce = 0 + let transaction = EthereumTransaction(from: fromAddress, to: toAddress, value: amtInGwie, data: Data(), nonce: nonce + 1, gasPrice: totalGas, gasLimit: gasLimit, chainId: chainID) + let _ = try tssAccount.sign(transaction: transaction) + } + + func testSignTyped() throws { + let typedData = try decoder.decode(TypedData.self, from: example1) + let params = EthTssAccountParams(publicKey: fullAddress, factorKey: factorKey, tssNonce: Int32(tssNonce), tssShare: tssShare, tssIndex: tssIndex, selectedTag: selected_tag, verifier: verifier, verifierID: verifierId, nodeIndexes: [], tssEndpoints: tssEndpoints, authSigs: sigs) + let tssAccount = EthereumTssAccount(params: params) + let _ = try tssAccount.signMessage(message: typedData) + } +}