tokenary/Encrypted Ink/Ethereum/Ethereum.swift

136 lines
5.8 KiB
Swift
Raw Normal View History

2021-06-12 14:30:58 +03:00
// Copyright © 2021 Encrypted Ink. All rights reserved.
import Foundation
2021-06-12 18:22:02 +03:00
import Web3Swift
import CryptoSwift
2021-06-12 14:30:58 +03:00
struct Ethereum {
2021-06-13 13:42:42 +03:00
enum Errors: Error {
case invalidInputData
case failedToSendTransaction
}
2021-06-12 14:30:58 +03:00
2021-06-13 13:17:13 +03:00
private static let network: Network = AlchemyNetwork(
2021-06-12 18:22:02 +03:00
chain: "mainnet",
2021-06-21 23:56:35 +03:00
apiKey: Secrets.alchemy
2021-06-12 18:22:02 +03:00
)
static func sign(message: String, account: Account) throws -> String {
let ethPrivateKey = EthPrivateKey(hex: account.privateKey)
2021-06-12 14:30:58 +03:00
2021-06-12 18:22:02 +03:00
let signature = SECP256k1Signature(
privateKey: ethPrivateKey,
message: UTF8StringBytes(string: message),
hashFunction: SHA3(variant: .keccak256).calculate
)
let data = try ConcatenatedBytes(
bytes: [
signature.r(),
signature.s(),
EthNumber(value: signature.recoverID().value() + 27)
]
).value()
return data.toPrefixedHexString()
2021-06-12 14:30:58 +03:00
}
2021-06-12 18:22:02 +03:00
static func signPersonal(message: String, account: Account) throws -> String {
let ethPrivateKey = EthPrivateKey(hex: account.privateKey)
let signed = SignedPersonalMessageBytes(message: message, signerKey: ethPrivateKey)
let data = try signed.value().toPrefixedHexString()
return data
2021-06-12 14:30:58 +03:00
}
2021-06-12 18:22:02 +03:00
static func sign(typedData: String, account: Account) throws -> String {
let data = try EIP712TypedData(jsonString: typedData)
let hash = EIP712Hash(domain: data.domain, typedData: data)
let privateKey = EthPrivateKey(hex: account.privateKey)
let signer = EIP712Signer(privateKey: privateKey)
return try signer.signatureData(hash: hash).toPrefixedHexString()
2021-06-12 14:30:58 +03:00
}
2021-06-13 13:17:13 +03:00
static func send(transaction: Transaction, account: Account) throws -> String {
2021-06-12 18:22:02 +03:00
let bytes = signedTransactionBytes(transaction: transaction, account: account)
2021-06-12 19:16:23 +03:00
let response = try SendRawTransactionProcedure(network: network, transactionBytes: bytes).call()
2021-06-13 13:17:13 +03:00
guard let hash = response["result"].string else {
2021-06-13 13:42:42 +03:00
throw Errors.failedToSendTransaction
2021-06-13 13:17:13 +03:00
}
return hash
2021-06-12 18:22:02 +03:00
}
private static func signedTransactionBytes(transaction: Transaction, account: Account) -> EthContractCallBytes {
let senderKey = EthPrivateKey(hex: account.privateKey)
2021-06-20 16:17:11 +03:00
let contractAddress = EthAddress(hex: transaction.to)
let weiAmount: EthNumber
if let value = transaction.value {
weiAmount = EthNumber(hex: value)
} else {
weiAmount = EthNumber(value: 0)
}
let functionCall = BytesFromHexString(hex: transaction.data)
2021-06-13 13:17:13 +03:00
let bytes: EthContractCallBytes
if let gasPriceString = transaction.gasPrice {
let gasPrice = EthNumber(hex: gasPriceString)
2021-06-20 16:17:11 +03:00
if let gasEstimateString = transaction.gas,
let transctionCountString = transaction.nonce {
2021-06-13 13:17:13 +03:00
let gasEstimate = EthNumber(hex: gasEstimateString)
let transactionCount = EthNumber(hex: transctionCountString)
bytes = EthContractCallBytes(networkID: NetworkID(network: network),
transactionsCount: transactionCount,
gasPrice: gasPrice,
gasEstimate: gasEstimate,
senderKey: senderKey,
contractAddress: contractAddress,
weiAmount: weiAmount,
functionCall: functionCall)
} else {
bytes = EthContractCallBytes(network: network,
gasPrice: gasPrice,
senderKey: senderKey,
contractAddress: contractAddress,
weiAmount: weiAmount,
functionCall: functionCall)
}
} else {
bytes = EthContractCallBytes(network: network,
senderKey: senderKey,
contractAddress: contractAddress,
weiAmount: weiAmount,
functionCall: functionCall)
}
2021-06-12 18:22:02 +03:00
return bytes
2021-06-12 14:30:58 +03:00
}
private static func getGas(network: Network, from: String, to: String, gasPrice: EthNumber, weiAmount: EthNumber, data: String, completion: @escaping (Data?) -> Void) {
let gas = try? EthGasEstimate(
network: network,
senderAddress: EthAddress(hex: from),
recipientAddress: EthAddress(hex: to),
gasEstimate: EthGasEstimate(
network: network,
senderAddress: EthAddress(hex: from),
recipientAddress: EthAddress(hex: to),
gasPrice: gasPrice,
weiAmount: weiAmount,
contractCall: BytesFromHexString(hex: data)
),
gasPrice: gasPrice,
weiAmount: weiAmount,
contractCall: BytesFromHexString(hex: data)
).value()
completion(gas)
}
private static func getGasPrice(completion: @escaping (EthNumber) -> Void) {
let gasPrice = EthNumber(hex: SimpleBytes { try EthGasPrice(network: network).value() })
completion(gasPrice)
}
private static func getNonce(network: Network, from: String, completion: @escaping (Data?) -> Void) {
let data = try? EthTransactions(network: network, address: EthAddress(hex: from), blockChainState: PendingBlockChainState()).count().value()
completion(data)
}
2021-06-12 14:30:58 +03:00
}