Skip to content

Commit

Permalink
Reduce the amount of RPC calls for confirming
Browse files Browse the repository at this point in the history
  • Loading branch information
ligi committed Jan 5, 2022
1 parent e5a65d3 commit 61828db
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 23 deletions.
11 changes: 7 additions & 4 deletions src/main/kotlin/org/komputing/fauceth/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import io.ktor.http.content.*
import io.ktor.request.receiveParameters
import io.ktor.response.*
import io.ktor.routing.*
import kotlinx.coroutines.flow.channelFlow
import kotlinx.html.*
import org.kethereum.ETH_IN_WEI
import org.kethereum.crypto.toAddress
Expand All @@ -21,7 +20,6 @@ import org.kethereum.model.*
import org.komputing.fauceth.FaucethLogLevel.*
import org.komputing.fauceth.util.log
import java.math.BigDecimal
import java.math.BigInteger

fun main(args: Array<String>) = io.ktor.server.netty.EngineMain.main(args)

Expand Down Expand Up @@ -140,9 +138,14 @@ fun Application.module() {
+"Chain: ${it.staticChainInfo.name}"
}
b {
+"Nonce: "
+"pending Nonce: "
}
+it.nonce.get().toString()
+it.pendingNonce.get().toString()
br
b {
+"confirmed Nonce: "
}
+it.confirmedNonce.get().toString()
br
b {
+"Balance: "
Expand Down
13 changes: 9 additions & 4 deletions src/main/kotlin/org/komputing/fauceth/Env.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.komputing.kaptcha.HCaptcha
import java.io.File
import java.io.FileOutputStream
import java.math.BigInteger
import java.math.BigInteger.ONE
import kotlin.system.exitProcess

const val ADDRESS_KEY = "address"
Expand Down Expand Up @@ -59,7 +60,8 @@ val unfilteredChains = chainsAdapter.fromJson(chainsDefinitionFile.source().buff

class ExtendedChainInfo(
val staticChainInfo: Chain,
val nonce: AtomicNonce,
val pendingNonce: AtomicNonce,
val confirmedNonce: AtomicNonce,
val rpc: EthereumRPC,
var useEIP1559: Boolean = true, // be optimistic - fallback when no 1559
var lastSeenBalance: BigInteger? = null
Expand All @@ -83,9 +85,12 @@ val chains = unfilteredChains.filter { config.chains.contains(BigInteger.valueOf

log(INFO, "Got initial nonce for chain ${it.name}: $initialNonce for address ${config.keyPair.toAddress()}")

val atomicNonce = AtomicNonce(initialNonce)

ExtendedChainInfo(it, atomicNonce, rpc)
ExtendedChainInfo(
it,
confirmedNonce = AtomicNonce(initialNonce.minus(ONE)),
pendingNonce = AtomicNonce(initialNonce),
rpc = rpc
)
}

internal fun fail(msg: String): Nothing {
Expand Down
42 changes: 27 additions & 15 deletions src/main/kotlin/org/komputing/fauceth/TransactionSender.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.kethereum.rpc.EthereumRPCException
import org.komputing.fauceth.util.log
import org.walleth.khex.toHexString
import java.io.IOException
import java.lang.IllegalStateException
import java.math.BigDecimal
import java.math.BigInteger
import java.math.BigInteger.*
Expand All @@ -30,16 +31,20 @@ suspend fun sendTransaction(address: Address, txChain: ExtendedChainInfo): Strin
val tx = createEmptyTransaction().apply {
to = address
value = config.amount
nonce = txChain.nonce.getAndIncrement()
nonce = txChain.pendingNonce.getAndIncrement()
from = config.keyPair.toAddress()
chain = txChain.staticChainInfo.chainId.toBigInteger()
creationEpochSecond = System.currentTimeMillis()
}

val txHashList = mutableListOf<String>()

try {
while (true) {
tx.gasLimit = txChain.rpc.estimateGas(tx) // TODO usually with most chains it is fixed at 21k - so there is room for RPC call amount optimize here
tx.gasLimit = retry {
// TODO usually with most chains it is fixed at 21k - so there is room for RPC call amount optimize here
txChain.rpc.estimateGas(tx) ?: throw EthereumRPCException("Could not estimate gas limit", 404)
}
if (txChain.useEIP1559) {
val handle1559NotAvailable: RetryPolicy<Throwable> = {
if (reason is EthereumRPCException && reason.message == "the method eth_feeHistory does not exist/is not available") StopRetrying else ContinueRetrying
Expand All @@ -61,11 +66,15 @@ suspend fun sendTransaction(address: Address, txChain: ExtendedChainInfo): Strin
tx.maxFeePerGas = feeSuggestionResult.maxFeePerGas
log(FaucethLogLevel.INFO, "Signing Transaction $tx")
} else {
// replacement fee (e.g. there was a surge after we calculated the fee and tx is not going in this way
tx.maxPriorityFeePerGas = feeSuggestionResult.maxPriorityFeePerGas.max(tx.maxPriorityFeePerGas!!)
// either replace with new feeSuggestion or 20% more than previous to prevent replacement tx underpriced
tx.maxFeePerGas = feeSuggestionResult.maxFeePerGas.max(tx.maxFeePerGas!!.toBigDecimal().multiply(BigDecimal("1.2")).toBigInteger())
log(FaucethLogLevel.INFO, "Signing Transaction with replacement fee $tx")
if (System.currentTimeMillis() - (tx.creationEpochSecond ?: System.currentTimeMillis()) > 20000) {
tx.creationEpochSecond = System.currentTimeMillis()
// replacement fee (e.g. there was a surge after we calculated the fee and tx is not going in this way
tx.maxPriorityFeePerGas = feeSuggestionResult.maxPriorityFeePerGas.max(tx.maxPriorityFeePerGas!!)
// either replace with new feeSuggestion or 20% more than previous to prevent replacement tx underpriced
tx.maxFeePerGas =
feeSuggestionResult.maxFeePerGas.max(tx.maxFeePerGas!!.toBigDecimal().multiply(BigDecimal("1.2")).toBigInteger())
log(FaucethLogLevel.INFO, "Signing Transaction with replacement fee $tx")
}
}
}
} catch (e: EthereumRPCException) {
Expand Down Expand Up @@ -109,16 +118,19 @@ suspend fun sendTransaction(address: Address, txChain: ExtendedChainInfo): Strin

var txBlockNumber: BigInteger?

repeat(20) { // after 20 attempts we will try with a new fee calculation
txHashList.forEach { hash ->
// we wait for *any* tx we signed in this context to confirm - there could be (edge)cases where a old tx confirms and so a replacement tx will not
txBlockNumber = txChain.rpc.getTransactionByHash(hash)?.transaction?.blockNumber
if (txBlockNumber != null) {
return hash
if (tx.nonce == txChain.confirmedNonce.get().plus(ONE)) {
repeat(20) { // after 20 attempts we will try with a new fee calculation
txHashList.forEach { hash ->
// we wait for *any* tx we signed in this context to confirm - there could be (edge)cases where a old tx confirms and so a replacement tx will not
txBlockNumber = txChain.rpc.getTransactionByHash(hash)?.transaction?.blockNumber
if (txBlockNumber != null) {
tx.nonce?.let { txChain.confirmedNonce.setPotentialNewMax(it) }
return hash
}
delay(100)
}
delay(100)
delay(700)
}
delay(700)
}
}
} catch (rpce: EthereumRPCException) {
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/org/komputing/fauceth/util/AtomicNonce.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ class AtomicNonce(initial: BigInteger) {

fun getAndIncrement(): BigInteger = current.getAndAccumulate(ONE) { previous, x -> previous.add(x) }
fun get(): BigInteger = current.get()
fun setPotentialNewMax(new: BigInteger): BigInteger = current.getAndAccumulate(new) { previous, x -> previous.max(x) }
}

0 comments on commit 61828db

Please sign in to comment.