# The whole new way to do Etherum on swift ## TLDR This release changes whole way of iterating with Etherum chain in a terms of creating, preparing, sending transaction and receiving receipt on it. Now it’s: - **unified** — only one struct `CodableTransaction` to handle - **consistent** — no more transaction to transaction merging, just create one, configure it, and assign it to `[Read|WriteOperation]`. - **clear** — intention of all types and methods are obvious by their names, most of them are generic based, so no more casting to and out of `Any`. - **error prone** — we’ve cleared so much legacy code in that pipeline which only God know what it does, so now seeing source of a method of your interess is a breath. ## Unified Previously there were **four** (!) types that is being used to prepare and send transaction: * `EthereumTransaction` * `TransactionParameters` * `EthereumParameters` * `TransactionOptions` Now their all gone and there’s only one to have: `CodableTransaction`, this one covers all previous usecases, because it’s actually just all in one merged yet. Unfortunately it’s not as clean as i wish it would be, but despite hurting my sense of perfection it’s much, much more better way to work with that it was before. So now to create transaction that you would send to a node further you sould just do the follow: ```swift let transaction: CodableTransaction = .emptyTransaction transaction.from = from ?? transaction.sender // `sender` one is if you have private key of your wallet address, so public key e.g. your wallet address could be infereted transaction.value = value transaction.gasLimitPolicy = .manual(78423) transaction.gasPricePolicy = .manual(20000000000) web3.eth.send(transaction) ``` ## `CodableTransaction` overview So let’s take a look at `CodableTransaction` most important properties closer: 1. Full access `{ get set }`: - `from: EthereumAddress?` — from property to fill it where it needs to. - `to: EthereumAddress` — adressee of a transaction, the only required property on membervise initnalizaion. - `type: TransactionType` — Type of transaction in Ethereum mainnet mean, 0 — legacy, 1 — post EIP-2930, 2 — post EIP-1559. - `chainID: BigUInt?` — The chainId that transaction is targeted for. - `value: BigUInt` — The native value of a transaction. - `data: Data` — data property of a trancaction, most used whilist contract iterating. - `callOnBlock: BlockNumber?` — Block number to call a given transaction on. - `accessList: [AccessListEntry]?` — access list for contract execution (EIP-2930 and EIP-1559 only). 2. Indirect properties `{ get set }` (could be changed in release): 1. **Nonce** — the nonce for the transaction * `nonce: BigUInt { get }` * `noncePolicy: NoncePolicy { set }` 2. **GasLimit** — the max number of gas units allowed to process this transaction * `gasLimit: BigUInt { get }` * `gasLimitPolicy: GasLimitPolicy { set }` 3. **GasPrice** — the price per gas unit for the tranaction (Legacy and EIP-2930 only) * `gasPrice: BigUInt? { get }` * `gasPricePolicy: GasPricePolicy { set }` 4. **MaxFeePerGas** — the max base fee per gas unit (EIP-1559 only) * `maxFeePerGas: BigUInt? { get }` * `maxFeePerGasPolicy: FeePerGasPolicy { set }` 5. **MaxPriorityFeePerGas** — the maximum tip to pay the miner (EIP-1559 only) * `maxPriorityFeePerGas: BigUInt? { get }` * `maxPriorityFeePerGasPolicy: FeePerGasPolicy { set }` 3. Computed properties `{ get }`: - `meta: EthereumMetadata?` — this one provides some information about already executed transaction, such is `blockHash`, `transactionIndex`, `gasprice` and some related. - `sender: EthereumAddress?` — the address of the sender of the transaction recovered from the signature. - `hash: Data?` — A transaction hash. - Sgnature data components: * `v: BigUInt` — signature v component. * `r: BigUInt` — signature r component. * `s: BigUInt` — signature s component. ## Consistent Second big chunk of improvement has happened with `[Read|Write]Transaction` types. So this is actually the general type to iterate with contracts. While doing so you’ve constantly creating and passing this and there this struct, which is encapsulate in itself all contract related data (`contract` object, `method` that you willing to call, `transaction` to be sent to). So this those structs are now renamed into `[Read|Write]Operation` respectively, so as their methods. Dispite that all pipeline is kept the same, so to make some read contract call you have to do follow: ```swift let contract = web3.contract(Web3.Utils.erc20ABI, at: receipt.contractAddress!)! let readOp = contract.createReadOperation("name")! readOp.transaction.from = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901") let response = try await readTX.callContractMethod() ``` and for write call follow: ```swift let abiString = "[]" // some ABI string let bytecode = Data.fromHex("") // some ABI bite sequence let contract = web3.contract(abiString, at: nil, abiVersion: 2)! let parameters = [...] as [AnyObject] let deployOp = contract.prepareDeploy(bytecode: bytecode, constructor: contract.contract.constructor, parameters: parameters)! deployOp.transaction.from = "" // your address deployOp.transaction.gasLimitPolicy = .manual(3000000) let result = try await deployTx.writeToChain(password: "web3swift") ``` ## Clarity You haven’t look under the hood before, but i did, and it was scary one. It suddenly appears that resolving and managing nothing, but five objects to send something peace of bite a chain is not as easy as it might be thoughts, so there was a mess in both method intents and their implementation. ## Transaction pipeline Now most of it’s gone, and the rule number one: **You should not merge any transaction by yourself.** 1. If you want to prepare you call for a given contract method on a contract pipeline stage there’s `CodableTransaction` property in it. 2. If you want to prepare such on a operation stage there’s `CodableTransaction` property in it too. Both these properties named `transaction` and one assigns another, when `Contract` creates appropriate `Operation` to be sent. So thil all leads to rule number two: **You should work to any you would like, but only one transaction object**. Please pay attention to it, it’s still possible to passing your transaction all around your code while filling it in, but it’s strongly not recomendet. The better way is to choose one place where `CodableTransaction` instance would be filled and implement utility methods that filled it in with appropriate data, like `BigUInt` for `gasWhatever` and `BlockNumber` for `callOnBlock` properties. ## Network layer There’s totally new network layer API. It based on generic implementation and on some cascade of protocols. Let’s take a closer look in it. ### How to Using new API is as easy as write three line of a code: ```swift func feeHistory(blockCount: UInt, block: BlockNumber, percentiles:[Double]) async throws -> Web3.Oracle.FeeHistory { let requestCall: APIRequest = .feeHistory(blockCount, block, percentiles) let response: APIResponse<Web3.Oracle.FeeHistory> = try await APIRequest.sendRequest(with: web3.provider, for: requestCall) /// explicitly declaring `Result` type is **required**. return response.result } ``` 1. On the first line you’re creating a request where passing all required and strictly typed parameters. 2. On a second you’re both declaring expected `Result` type and making a network request. 3. On the third one you’re reaching result itself. And that’s it, you’re done here. ## Error prone Thanks to @mliot`s work it’s under the hood transaction signing/decoding with respect to its type. So actually you don’t need to feel `data` property by yourself in most of cases, because it happening itself and it happening on `WriteOperation.writeToChain` and `ReadOperation.callContractMethod` e.g. on the very end of the whole pipeline[^1]. ## Recap So let’s recap new available API pipelines. ### Native transaction send ```swift let transaction: CodableTransaction = .emptyTransaction transaction.from = from ?? transaction.sender // `sender` one is if you have private key of your wallet address, so public key e.g. your wallet address could be infereted transaction.value = value transaction.gasLimitPolicy = .manual(78423) transaction.gasPricePolicy = .manual(20000000000) web3.eth.send(transaction) ``` ### Contract read method call ```swift let contract = web3.contract(Web3.Utils.erc20ABI, at: receipt.contractAddress!)! let readOp = contract.createReadOperation("name")! readOp.transaction.from = EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901") let response = try await readTX.callContractMethod() ``` ### Contract write on chain method call ```swift let abiString = "[]" // some ABI string let bytecode = Data.fromHex("") // some ABI bite sequence let contract = web3.contract(abiString, at: nil, abiVersion: 2)! let parameters = [...] as [AnyObject] let deployOp = contract.prepareDeploy(bytecode: bytecode, constructor: contract.contract.constructor, parameters: parameters)! deployOp.transaction.from = "" // your address deployOp.transaction.gasLimitPolicy = .manual(3000000) let result = try await deployTx.writeToChain(password: "web3swift") ``` ### Sending network request to a node ```swift func feeHistory(blockCount: UInt, block: BlockNumber, percentiles:[Double]) async throws -> Web3.Oracle.FeeHistory { let requestCall: APIRequest = .feeHistory(blockCount, block, percentiles) let response: APIResponse<Web3.Oracle.FeeHistory> = try await APIRequest.sendRequest(with: web3.provider, for: requestCall) /// explicitly declaring `Result` type is **required**. return response.result } ``` [^1]: This is actually not a whole true, because in that stage it’s just all gas related properties gets resolved.