|
| 1 | +// Package: web3swift |
| 2 | +// Created by Alex Vlasov. |
| 3 | +// Copyright © 2018 Alex Vlasov. All rights reserved. |
| 4 | +// |
| 5 | +// Support for EIP-2718 by Mark Loit 2022 |
| 6 | + |
| 7 | +import Foundation |
| 8 | +import BigInt |
| 9 | + |
| 10 | +/* |
| 11 | + AbstractEnvelope is the main protocol definition to enable support for different transaction types. |
| 12 | + it defines the basic parameters and methods required by all transaction types. |
| 13 | + Other than `Legacy` (untyped transaction) and `EIP2718Envelope`, no other transaction type should inherit directly |
| 14 | + from AbstractEnvelope. All typed transactions should inherit from `EIP2718Envelope` instead, as it provides default |
| 15 | + implementations for some required methods. |
| 16 | + |
| 17 | + Adding a new transaction type in the future should be as straight forward as adding to the TransactionType enum here |
| 18 | + then creating a new struct that implements to `EIP2718Envelope`, and implementing the required elements for the type |
| 19 | + Finally adding the type specific inits to the factory routines in `EnvelopeFactory` so that objectts of the new type |
| 20 | + will get generated when `EthereumTransaction` is being created with data for the new type |
| 21 | +*/ |
| 22 | + |
| 23 | +/// Enumeration for supported transaction types |
| 24 | +public enum TransactionType: UInt, CustomStringConvertible, CaseIterable { |
| 25 | + |
| 26 | + /// For untyped and type 0 transactions EIP155 and older |
| 27 | + case legacy |
| 28 | + |
| 29 | + /// For type 1 transactions conforming to EIP2930 |
| 30 | + case eip2930 |
| 31 | + |
| 32 | + /// For type 2 transactions conforming to EIP1559 |
| 33 | + case eip1559 |
| 34 | + |
| 35 | + /// range-checking value, not a valid type, will never be returned as a type |
| 36 | + // case total // always keep immediately after last valid type |
| 37 | + // should there be a need to handle an unknown type, place it after total |
| 38 | + |
| 39 | + public var description: String { |
| 40 | + switch self { |
| 41 | + case .legacy: return "Legacy" // legacy is a pseudo-type, no EIP-2718 transaction will ever be encoded with type = 0 |
| 42 | + // though nodes do appear to return a type of 0 for legacy transactions in their JSON |
| 43 | + case .eip2930: return "EIP-2930" |
| 44 | + case .eip1559: return "EIP-1559" |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +/// Encoding selector for transaction transmission/hashing or signing |
| 50 | +public enum EncodeType { |
| 51 | + |
| 52 | + /// Encode the transaction for transmission or hashing |
| 53 | + case transaction |
| 54 | + |
| 55 | + /// Encode the transaction for signing |
| 56 | + case signature |
| 57 | +} |
| 58 | + |
| 59 | +/// Protocol definition for all transaction envelope types |
| 60 | +/// All envelopes must conform to this protocol to work with `EthereumTransaction` |
| 61 | +/// each implememtation holds all the type specific data |
| 62 | +/// and implments the type specific encoding/decoding |
| 63 | +public protocol AbstractEnvelope: CustomStringConvertible { // possibly add Codable? |
| 64 | + |
| 65 | + /// The type of transaction this envelope represents |
| 66 | + var type: TransactionType { get } |
| 67 | + |
| 68 | + // MARK: common parameters for any transaction |
| 69 | + /// the nonce value for the transaction |
| 70 | + var nonce: BigUInt { get set } |
| 71 | + |
| 72 | + /// On chain address that this transaction is being sent to |
| 73 | + var to: EthereumAddress { get set } |
| 74 | + |
| 75 | + // /// The native value of the transaction in Wei |
| 76 | + // var value: BigUInt { get set } |
| 77 | + |
| 78 | + // /// Any encoded data accompanying the transaction |
| 79 | + // var data: Data { get set } |
| 80 | + |
| 81 | + // Signature data should not be set directly |
| 82 | + /// signature V compoonent |
| 83 | + var v: BigUInt { get set } |
| 84 | + |
| 85 | + /// signature R compoonent |
| 86 | + var r: BigUInt { get set } |
| 87 | + |
| 88 | + /// signature S compoonent |
| 89 | + var s: BigUInt { get set } |
| 90 | + |
| 91 | + /// Transaction Parameters object |
| 92 | + /// used to provide external access to the otherwise |
| 93 | + /// protected parameters of the object |
| 94 | + var parameters: EthereumParameters { get set } |
| 95 | + |
| 96 | + // required initializers |
| 97 | + // for Decodable support |
| 98 | + /// initializer for creating an `EthereumTransaction` with the Decodable protocol |
| 99 | + /// will return an new `EthereumTransaction` object on success |
| 100 | + /// thows a `Web3.dataError` if an error occurs while trying to decode a value |
| 101 | + /// returns nil if a required field is not found in the decoder stream |
| 102 | + init?(from decoder: Decoder) throws // Decodable Protocol |
| 103 | + |
| 104 | + // initializes from a raw stream of bytes |
| 105 | + // can fail if input stream is not of the right size/cannot be decoded |
| 106 | + /// initializer for creating an `EthereumTransaction` with raw bytestream data |
| 107 | + /// will return an new `EthereumTransaction` object on success |
| 108 | + /// returns nil if a required field is not found in the decoder stream, or can't be decoded |
| 109 | + init?(rawValue: Data) // Decode from Ethereum Data |
| 110 | + |
| 111 | + // pseudo memberwise initializer |
| 112 | + // accepts all common parameters (full description with default implementation below) |
| 113 | + // Note the `nil` parameters, even though non-optional in the struct, are there to allow |
| 114 | + // fallback to pulling the value from `options`. If options also does not have a value, a suitable default is used |
| 115 | + // precedence is as follows: direct parameter > options value > default value |
| 116 | + /// Default memberwse initializer that all envelopes must support |
| 117 | + /// - Parameters: |
| 118 | + /// - to: EthereumAddress of destination |
| 119 | + /// - nonce: nonce for the transaction |
| 120 | + /// - v: Signature V component |
| 121 | + /// - r: Signature R component |
| 122 | + /// - s: Signature S component |
| 123 | + /// - parameters: EthereumParameters struct containing any other required parameters |
| 124 | + init(to: EthereumAddress, nonce: BigUInt?, v: BigUInt, r: BigUInt, s: BigUInt, parameters: EthereumParameters?) |
| 125 | + |
| 126 | + /// Applies the passed options to the transaction envelope |
| 127 | + /// - Parameters: |
| 128 | + /// - {default}: TransactionOptions struct |
| 129 | + mutating func applyOptions(_ options: TransactionOptions) |
| 130 | + |
| 131 | + /// Transaction encoder for transmission or signing |
| 132 | + /// - Parameters: |
| 133 | + /// - {default}: EncodeType enum |
| 134 | + /// - when type is .signature the transaction is encoded for signing |
| 135 | + /// - when type is .transaction the thransaction is encoded for hashing or transmission to the blockchain |
| 136 | + /// - Returns: a raw encoding stream representing the transaction, encoded according to it's type |
| 137 | + func encode(for type: EncodeType) -> Data? |
| 138 | + |
| 139 | + /// Encodes the transaction as a set of strings for JSON transmission |
| 140 | + /// - Returns: A TransactionParameters object containg all the parameters for the transaction |
| 141 | + func encodeAsDictionary(from: EthereumAddress?) -> TransactionParameters? |
| 142 | + |
| 143 | + /// used by the signing algorithm to set the v, r, s parameters |
| 144 | + /// - Parameters: |
| 145 | + /// - {default}: UnmarshalledSignature struct containing the v, r, s parameters |
| 146 | + mutating func setUnmarshalledSignatureData(_ unmarshalledSignature: SECP256K1.UnmarshaledSignature) |
| 147 | + |
| 148 | + /// used by the public-key recovery algorithm for determining the signer of the transaction |
| 149 | + /// - Returns: returns the 'unmarshalled' v,r,s psignatrure parameters |
| 150 | + func getUnmarshalledSignatureData() -> SECP256K1.UnmarshaledSignature? |
| 151 | + |
| 152 | + /// used by Transaction.unsign() to reset/clear the signature |
| 153 | + mutating func clearSignatureData() |
| 154 | +} |
| 155 | + |
| 156 | +public extension AbstractEnvelope { |
| 157 | + |
| 158 | + mutating func clearSignatureData() { |
| 159 | + self.v = 1 |
| 160 | + self.r = 0 |
| 161 | + self.s = 0 |
| 162 | + } |
| 163 | +} |
0 commit comments