From 1569b26c69c5333e6f915bf45c7b783178d3d0a9 Mon Sep 17 00:00:00 2001 From: Bogdan Gusiev Date: Mon, 1 Sep 2025 14:35:22 +0200 Subject: [PATCH] Expose WriteOperation#resovlePolicies #signTransaction methods Methods can be used for more direct control on how transaction is signed and sent to blockchain. --- .../web3swift/Operations/WriteOperation.swift | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/Sources/web3swift/Operations/WriteOperation.swift b/Sources/web3swift/Operations/WriteOperation.swift index 10fd972cb..6d8601ad5 100755 --- a/Sources/web3swift/Operations/WriteOperation.swift +++ b/Sources/web3swift/Operations/WriteOperation.swift @@ -20,26 +20,59 @@ public class WriteOperation: ReadOperation { /// - sendRaw: If set to `true` transaction will be signed and sent using `eth_sendRawTransaction`. /// Otherwise, no signing attempts will take place and the `eth_sendTransaction` RPC will be used instead. /// Default value is `true`. - public func writeToChain(password: String, policies: Policies = .auto, sendRaw: Bool = true) async throws -> TransactionSendingResult { - try await policyResolver.resolveAll(for: &transaction, with: policies) + public func writeToChain(password: String, + policies: Policies = .auto, + sendRaw: Bool = true) async throws -> TransactionSendingResult { + try await resolvePolicies(policies) guard sendRaw else { return try await web3.eth.send(transaction) } + try signTransaction(password: password) + + guard let transactionData = transaction.encode(for: .transaction) else { + throw Web3Error.dataError + } + return try await web3.eth.send(raw: transactionData) + } + + /// Resolves all policy-driven transaction attributes: gas limit, gas price, nonce. + /// - Parameters: + /// - policies: Determining the behaviour of how transaction attributes like gas limit and + /// nonce are resolved. Default value is ``Policies/auto``. + /// - Throws: Rethrows any error that occurs during policy resolution. + public func resolvePolicies(_ policies: Policies) async throws { + try await policyResolver.resolveAll(for: &transaction, with: policies) + } + + /// Signs the transaction locally using the attached keystore manager. + /// - Parameters: + /// - password: Password for the private key in the keystore manager attached to the provider + /// you set to `web3` passed in the initializer. + /// - Throws: + /// - ``Web3Error/inputError`` if no keystore is attached to the provider, + /// or if signing fails with the provided password. + @discardableResult + public func signTransaction(password: String) throws -> CodableTransaction { guard let attachedKeystoreManager = web3.provider.attachedKeystoreManager else { - throw Web3Error.inputError(desc: "Failed to locally sign a transaction. Web3 provider doesn't have keystore attached.") + throw Web3Error.inputError( + desc: "Failed to locally sign a transaction. Web3 provider doesn't have keystore attached." + ) } do { - try Web3Signer.signTX(transaction: &transaction, - keystore: attachedKeystoreManager, - account: transaction.from ?? transaction.sender ?? EthereumAddress.contractDeploymentAddress(), - password: password) + try Web3Signer.signTX( + transaction: &transaction, + keystore: attachedKeystoreManager, + account: transaction.from ?? transaction.sender ?? EthereumAddress.contractDeploymentAddress(), + password: password + ) + return transaction } catch { - throw Web3Error.inputError(desc: "Failed to locally sign a transaction. \(error.localizedDescription)") + throw Web3Error.inputError( + desc: "Failed to locally sign a transaction. \(error.localizedDescription)" + ) } - guard let transactionData = transaction.encode(for: .transaction) else { throw Web3Error.dataError } - return try await web3.eth.send(raw: transactionData) } }