feat: eip1559
This commit is contained in:
parent
efe2790db6
commit
c1d61a13c0
|
@ -246,7 +246,7 @@ proc registerUsernameEstimateGas*(username: string, address: string, pubKey: str
|
|||
if success:
|
||||
result = fromHex[int](response)
|
||||
|
||||
proc registerUsername*(username, pubKey, address, gas, gasPrice, password: string, success: var bool): string =
|
||||
proc registerUsername*(username, pubKey, address, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, success: var bool): string =
|
||||
let
|
||||
label = fromHex(FixedBytes[32], label(username))
|
||||
coordinates = extractCoordinates(pubkey)
|
||||
|
@ -261,7 +261,7 @@ proc registerUsername*(username, pubKey, address, gas, gasPrice, password: stri
|
|||
registerAbiEncoded = ensUsernamesContract.methods["register"].encodeAbi(register)
|
||||
approveAndCallObj = ApproveAndCall[132](to: ensUsernamesContract.address, value: price, data: DynamicBytes[132].fromHex(registerAbiEncoded))
|
||||
|
||||
var tx = transactions.buildTokenTransaction(parseAddress(address), sntContract.address, gas, gasPrice)
|
||||
var tx = transactions.buildTokenTransaction(parseAddress(address), sntContract.address, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
|
||||
|
||||
result = sntContract.methods["approveAndCall"].send(tx, approveAndCallObj, password, success)
|
||||
if success:
|
||||
|
@ -288,7 +288,7 @@ proc setPubKeyEstimateGas*(username: string, address: string, pubKey: string, su
|
|||
except RpcException as e:
|
||||
raise
|
||||
|
||||
proc setPubKey*(username, pubKey, address, gas, gasPrice, password: string, success: var bool): string =
|
||||
proc setPubKey*(username, pubKey, address, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, success: var bool): string =
|
||||
var hash = namehash(username)
|
||||
hash.removePrefix("0x")
|
||||
|
||||
|
@ -300,7 +300,7 @@ proc setPubKey*(username, pubKey, address, gas, gasPrice, password: string, succ
|
|||
setPubkey = SetPubkey(label: label, x: x, y: y)
|
||||
resolverAddress = resolver(hash)
|
||||
|
||||
var tx = transactions.buildTokenTransaction(parseAddress(address), parseAddress(resolverAddress), gas, gasPrice)
|
||||
var tx = transactions.buildTokenTransaction(parseAddress(address), parseAddress(resolverAddress), gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
|
||||
|
||||
try:
|
||||
result = resolverContract.methods["setPubkey"].send(tx, setPubkey, password, success)
|
||||
|
|
|
@ -4,6 +4,8 @@ import
|
|||
import
|
||||
web3/[conversions, ethtypes], stint
|
||||
|
||||
import ../types/transaction
|
||||
|
||||
# TODO: make this public in nim-web3 lib
|
||||
template stripLeadingZeros*(value: string): string =
|
||||
var cidx = 0
|
||||
|
@ -12,16 +14,20 @@ template stripLeadingZeros*(value: string): string =
|
|||
cidx.inc
|
||||
value[cidx .. ^1]
|
||||
|
||||
# TODO: update this in nim-web3
|
||||
proc `%`*(x: EthSend): JsonNode =
|
||||
proc `%`*(x: TransactionData): JsonNode =
|
||||
result = newJobject()
|
||||
result["from"] = %x.source
|
||||
result["type"] = %x.txType
|
||||
if x.to.isSome:
|
||||
result["to"] = %x.to.unsafeGet
|
||||
if x.gas.isSome:
|
||||
result["gas"] = %x.gas.unsafeGet
|
||||
if x.gasPrice.isSome:
|
||||
result["gasPrice"] = %("0x" & x.gasPrice.unsafeGet.toHex.stripLeadingZeros)
|
||||
if x.maxFeePerGas.isSome:
|
||||
result["maxFeePerGas"] = %("0x" & x.maxFeePerGas.unsafeGet.toHex)
|
||||
if x.maxPriorityFeePerGas.isSome:
|
||||
result["maxPriorityFeePerGas"] = %("0x" & x.maxPriorityFeePerGas.unsafeGet.toHex)
|
||||
if x.value.isSome:
|
||||
result["value"] = %("0x" & x.value.unsafeGet.toHex)
|
||||
result["data"] = %x.data
|
||||
|
|
|
@ -2,9 +2,9 @@ import
|
|||
web3/ethtypes
|
||||
|
||||
import
|
||||
transactions, ../../types/[rpc_response]
|
||||
transactions, ../../types/[rpc_response, transaction]
|
||||
|
||||
proc sendTransaction*(tx: var EthSend, password: string, success: var bool): string =
|
||||
proc sendTransaction*(tx: var TransactionData, password: string, success: var bool): string =
|
||||
success = true
|
||||
try:
|
||||
let response = transactions.sendTransaction(tx, password)
|
||||
|
@ -13,7 +13,7 @@ proc sendTransaction*(tx: var EthSend, password: string, success: var bool): str
|
|||
success = false
|
||||
result = e.msg
|
||||
|
||||
proc estimateGas*(tx: var EthSend, success: var bool): string =
|
||||
proc estimateGas*(tx: var TransactionData, success: var bool): string =
|
||||
success = true
|
||||
try:
|
||||
let response = transactions.estimateGas(tx)
|
||||
|
|
|
@ -5,7 +5,7 @@ import
|
|||
nimcrypto, web3/[encoding, ethtypes]
|
||||
|
||||
import
|
||||
../../types/[rpc_response], ../coder, eth, transactions
|
||||
../../types/[rpc_response, transaction], ../coder, eth, transactions
|
||||
|
||||
export sendTransaction
|
||||
|
||||
|
@ -38,7 +38,7 @@ proc encodeAbi*(self: Method, obj: object = RootObj()): string =
|
|||
result &= encoded.data
|
||||
result &= data
|
||||
|
||||
proc estimateGas*(self: Method, tx: var EthSend, methodDescriptor: object, success: var bool): string =
|
||||
proc estimateGas*(self: Method, tx: var TransactionData, methodDescriptor: object, success: var bool): string =
|
||||
success = true
|
||||
tx.data = self.encodeAbi(methodDescriptor)
|
||||
try:
|
||||
|
@ -48,11 +48,11 @@ proc estimateGas*(self: Method, tx: var EthSend, methodDescriptor: object, succe
|
|||
success = false
|
||||
result = e.msg
|
||||
|
||||
proc send*(self: Method, tx: var EthSend, methodDescriptor: object, password: string, success: var bool): string =
|
||||
proc send*(self: Method, tx: var TransactionData, methodDescriptor: object, password: string, success: var bool): string =
|
||||
tx.data = self.encodeAbi(methodDescriptor)
|
||||
result = eth.sendTransaction(tx, password, success)
|
||||
|
||||
proc call*[T](self: Method, tx: var EthSend, methodDescriptor: object, success: var bool): T =
|
||||
proc call*[T](self: Method, tx: var TransactionData, methodDescriptor: object, success: var bool): T =
|
||||
success = true
|
||||
tx.data = self.encodeAbi(methodDescriptor)
|
||||
let response: RpcResponse
|
||||
|
|
|
@ -5,9 +5,9 @@ import
|
|||
json_serialization, chronicles, web3/ethtypes
|
||||
|
||||
import
|
||||
../core, ../../types/[rpc_response], ../conversions
|
||||
../core, ../../types/[rpc_response, transaction], ../conversions
|
||||
|
||||
proc estimateGas*(tx: EthSend): RpcResponse =
|
||||
proc estimateGas*(tx: TransactionData): RpcResponse =
|
||||
let response = core.callPrivateRPC("eth_estimateGas", %*[%tx])
|
||||
result = Json.decode(response, RpcResponse)
|
||||
if not result.error.isNil:
|
||||
|
@ -15,7 +15,7 @@ proc estimateGas*(tx: EthSend): RpcResponse =
|
|||
|
||||
trace "Gas estimated succesfully", estimate=result.result
|
||||
|
||||
proc sendTransaction*(tx: EthSend, password: string): RpcResponse =
|
||||
proc sendTransaction*(tx: TransactionData, password: string): RpcResponse =
|
||||
let responseStr = core.sendTransaction($(%tx), password)
|
||||
result = Json.decode(responseStr, RpcResponse)
|
||||
if not result.error.isNil:
|
||||
|
@ -23,7 +23,7 @@ proc sendTransaction*(tx: EthSend, password: string): RpcResponse =
|
|||
|
||||
trace "Transaction sent succesfully", hash=result.result
|
||||
|
||||
proc call*(tx: EthSend): RpcResponse =
|
||||
proc call*(tx: TransactionData): RpcResponse =
|
||||
let responseStr = core.callPrivateRPC("eth_call", %*[%tx, "latest"])
|
||||
result = Json.decode(responseStr, RpcResponse)
|
||||
if not result.error.isNil:
|
||||
|
|
|
@ -145,3 +145,19 @@ proc fetchCryptoServices*(success: var bool): string =
|
|||
success = false
|
||||
error "Error getting crypto services: ", msg = e.msg
|
||||
result = ""
|
||||
|
||||
proc maxPriorityFeePerGas*(): string =
|
||||
let payload = %* []
|
||||
result = callPrivateRPC("eth_maxPriorityFeePerGas", payload)
|
||||
|
||||
proc suggestFees*(): string =
|
||||
let payload = %* []
|
||||
result = callPrivateRPC("wallet_suggestFees", payload)
|
||||
|
||||
proc feeHistory*(n: int): string =
|
||||
let payload = %* [n, "latest", nil]
|
||||
result = callPrivateRPC("eth_feeHistory", payload)
|
||||
|
||||
proc getGasPrice*(): string =
|
||||
let payload = %* []
|
||||
result = callPrivateRPC("eth_gasPrice", payload)
|
||||
|
|
|
@ -56,11 +56,13 @@ const ACC_METHODS = toHashSet(["eth_accounts", "eth_coinbase"])
|
|||
type ProviderModel* = ref object
|
||||
events*: EventEmitter
|
||||
permissions*: PermissionsModel
|
||||
wallet*: WalletModel
|
||||
|
||||
proc newProviderModel*(events: EventEmitter, permissions: PermissionsModel): ProviderModel =
|
||||
proc newProviderModel*(events: EventEmitter, permissions: PermissionsModel, wallet: WalletModel): ProviderModel =
|
||||
result = ProviderModel()
|
||||
result.events = events
|
||||
result.permissions = permissions
|
||||
result.wallet = wallet
|
||||
|
||||
proc requestType(message: string): RequestTypes =
|
||||
let data = message.parseJson
|
||||
|
@ -114,6 +116,8 @@ proc process(self: ProviderModel, data: Web3SendAsyncReadOnly): string =
|
|||
let password = request["password"].getStr()
|
||||
let selectedGasLimit = request["selectedGasLimit"].getStr()
|
||||
let selectedGasPrice = request["selectedGasPrice"].getStr()
|
||||
let selectedTipLimit = request{"selectedTipLimit"}.getStr()
|
||||
let selectedOverallLimit = request{"selectedOverallLimit"}.getStr()
|
||||
let txData = if (request["params"][0].hasKey("data") and request["params"][0]["data"].kind != JNull):
|
||||
request["params"][0]["data"].getStr()
|
||||
else:
|
||||
|
@ -124,8 +128,10 @@ proc process(self: ProviderModel, data: Web3SendAsyncReadOnly): string =
|
|||
var response = ""
|
||||
var validInput: bool = true
|
||||
|
||||
let eip1559Enabled = self.wallet.isEIP1559Enabled()
|
||||
|
||||
try:
|
||||
validateTransactionInput(fromAddress, to, "", value, selectedGasLimit, selectedGasPrice, txData, "dummy")
|
||||
validateTransactionInput(fromAddress, to, "", value, selectedGasLimit, selectedGasPrice, txData, eip1559Enabled, selectedTipLimit, selectedOverallLimit, "dummy")
|
||||
except Exception as e:
|
||||
validInput = false
|
||||
success = false
|
||||
|
@ -133,7 +139,7 @@ proc process(self: ProviderModel, data: Web3SendAsyncReadOnly): string =
|
|||
|
||||
if validInput:
|
||||
# TODO make this async
|
||||
response = wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, password, success, txData)
|
||||
response = wallet.sendTransaction(fromAddress, to, value, selectedGasLimit, selectedGasPrice, eip1559Enabled, selectedTipLimit, selectedOverallLimit, password, success, txData)
|
||||
errorMessage = if not success:
|
||||
if response == "":
|
||||
"web3-response-error"
|
||||
|
|
|
@ -8,6 +8,7 @@ type WalletSignal* = ref object of Signal
|
|||
eventType*: string
|
||||
blockNumber*: int
|
||||
accounts*: seq[string]
|
||||
baseFeePerGas*: string
|
||||
# newTransactions*: ???
|
||||
erc20*: bool
|
||||
|
||||
|
@ -18,6 +19,7 @@ proc fromEvent*(T: type WalletSignal, jsonSignal: JsonNode): WalletSignal =
|
|||
if jsonSignal["event"].kind != JNull:
|
||||
result.eventType = jsonSignal["event"]["type"].getStr
|
||||
result.blockNumber = jsonSignal["event"]{"blockNumber"}.getInt
|
||||
result.baseFeePerGas = jsonSignal["event"]{"baseFeePerGas"}.getStr
|
||||
result.erc20 = jsonSignal["event"]{"erc20"}.getBool
|
||||
result.accounts = @[]
|
||||
if jsonSignal["event"]["accounts"].kind != JNull:
|
||||
|
|
|
@ -50,7 +50,7 @@ proc newStatusInstance*(fleetConfig: string): Status =
|
|||
result.mailservers = mailservers.newMailserversModel(result.events)
|
||||
result.browser = browser.newBrowserModel(result.events)
|
||||
result.tokens = tokens.newTokensModel(result.events)
|
||||
result.provider = provider.newProviderModel(result.events, result.permissions)
|
||||
result.provider = provider.newProviderModel(result.events, result.permissions, result.wallet)
|
||||
result.osnotifications = newOsNotifications(result.events)
|
||||
|
||||
proc initNode*(self: Status, statusGoDir, keystoreDir: string) =
|
||||
|
|
|
@ -43,14 +43,14 @@ proc init*(self: StickersModel) =
|
|||
var evArgs = StickerArgs(e)
|
||||
self.addStickerToRecent(evArgs.sticker, evArgs.save)
|
||||
|
||||
proc buildTransaction(packId: Uint256, address: Address, price: Uint256, approveAndCall: var ApproveAndCall[100], sntContract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
|
||||
proc buildTransaction(packId: Uint256, address: Address, price: Uint256, approveAndCall: var ApproveAndCall[100], sntContract: var Erc20Contract, gas = "", gasPrice = "", isEIP1559Enabled = false, maxPriorityFeePerGas = "", maxFeePerGas = ""): TransactionData =
|
||||
sntContract = status_contracts.getSntContract()
|
||||
let
|
||||
stickerMktContract = status_contracts.getContract("sticker-market")
|
||||
buyToken = BuyToken(packId: packId, address: address, price: price)
|
||||
buyTxAbiEncoded = stickerMktContract.methods["buyToken"].encodeAbi(buyToken)
|
||||
approveAndCall = ApproveAndCall[100](to: stickerMktContract.address, value: price, data: DynamicBytes[100].fromHex(buyTxAbiEncoded))
|
||||
transactions.buildTokenTransaction(address, sntContract.address, gas, gasPrice)
|
||||
transactions.buildTokenTransaction(address, sntContract.address, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
|
||||
|
||||
proc estimateGas*(packId: int, address: string, price: string, success: var bool): int =
|
||||
var
|
||||
|
@ -68,7 +68,7 @@ proc estimateGas*(packId: int, address: string, price: string, success: var bool
|
|||
if success:
|
||||
result = fromHex[int](response)
|
||||
|
||||
proc buyPack*(self: StickersModel, packId: int, address, price, gas, gasPrice, password: string, success: var bool): string =
|
||||
proc buyPack*(self: StickersModel, packId: int, address, price, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas: string, maxFeePerGas: string, password: string, success: var bool): string =
|
||||
var
|
||||
sntContract: Erc20Contract
|
||||
approveAndCall: ApproveAndCall[100]
|
||||
|
@ -79,7 +79,10 @@ proc buyPack*(self: StickersModel, packId: int, address, price, gas, gasPrice, p
|
|||
approveAndCall,
|
||||
sntContract,
|
||||
gas,
|
||||
gasPrice
|
||||
gasPrice,
|
||||
isEIP1559Enabled,
|
||||
maxPriorityFeePerGas,
|
||||
maxFeePerGas
|
||||
)
|
||||
|
||||
result = sntContract.methods["approveAndCall"].send(tx, approveAndCall, password, success)
|
||||
|
|
|
@ -2,19 +2,25 @@ import
|
|||
options, strutils
|
||||
|
||||
import
|
||||
stint, web3/ethtypes
|
||||
stint, web3/ethtypes, types/transaction
|
||||
|
||||
from utils as status_utils import toUInt64, gwei2Wei, parseAddress
|
||||
|
||||
proc buildTransaction*(source: Address, value: Uint256, gas = "", gasPrice = "", data = ""): EthSend =
|
||||
result = EthSend(
|
||||
proc buildTransaction*(source: Address, value: Uint256, gas = "", gasPrice = "", isEIP1559Enabled = false, maxPriorityFeePerGas = "", maxFeePerGas = "", data = ""): TransactionData =
|
||||
result = TransactionData(
|
||||
source: source,
|
||||
value: value.some,
|
||||
gas: (if gas.isEmptyOrWhitespace: Quantity.none else: Quantity(cast[uint64](parseFloat(gas).toUInt64)).some),
|
||||
gasPrice: (if gasPrice.isEmptyOrWhitespace: int.none else: gwei2Wei(parseFloat(gasPrice)).truncate(int).some),
|
||||
data: data
|
||||
)
|
||||
if isEIP1559Enabled:
|
||||
result.txType = "0x02"
|
||||
result.maxPriorityFeePerGas = if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxPriorityFeePerGas)).some
|
||||
result.maxFeePerGas = (if maxFeePerGas.isEmptyOrWhitespace: Uint256.none else: gwei2Wei(parseFloat(maxFeePerGas)).some)
|
||||
else:
|
||||
result.txType = "0x00"
|
||||
|
||||
proc buildTokenTransaction*(source, contractAddress: Address, gas = "", gasPrice = ""): EthSend =
|
||||
result = buildTransaction(source, 0.u256, gas, gasPrice)
|
||||
proc buildTokenTransaction*(source, contractAddress: Address, gas = "", gasPrice = "", isEIP1559Enabled = false, maxPriorityFeePerGas = "", maxFeePerGas = ""): TransactionData =
|
||||
result = buildTransaction(source, 0.u256, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
|
||||
result.to = contractAddress.some
|
|
@ -1,7 +1,7 @@
|
|||
{.used.}
|
||||
|
||||
import strutils
|
||||
|
||||
import web3/ethtypes, options, stint
|
||||
include pending_transaction_type
|
||||
|
||||
type
|
||||
|
@ -22,6 +22,19 @@ type
|
|||
fromAddress*: string
|
||||
to*: string
|
||||
|
||||
type
|
||||
TransactionData* = object
|
||||
source*: Address # the address the transaction is send from.
|
||||
to*: Option[Address] # (optional when creating new contract) the address the transaction is directed to.
|
||||
gas*: Option[Quantity] # (optional, default: 90000) integer of the gas provided for the transaction execution. It will return unused gas.
|
||||
gasPrice*: Option[int] # (optional, default: To-Be-Determined) integer of the gasPrice used for each paid gas.
|
||||
maxPriorityFeePerGas*: Option[Uint256]
|
||||
maxFeePerGas*: Option[Uint256]
|
||||
value*: Option[Uint256] # (optional) integer of the value sent with this transaction.
|
||||
data*: string # the compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI.
|
||||
nonce*: Option[Nonce] # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce
|
||||
txType*: string
|
||||
|
||||
proc cmpTransactions*(x, y: Transaction): int =
|
||||
# Sort proc to compare transactions from a single account.
|
||||
# Compares first by block number, then by nonce
|
||||
|
|
|
@ -88,6 +88,8 @@ proc wei2Eth*(input: string, decimals: int): string =
|
|||
error "Error parsing this wei value", input, msg=e.msg
|
||||
result = "0"
|
||||
|
||||
proc wei2Gwei*(input: string): string =
|
||||
result = wei2Eth(input, 9)
|
||||
|
||||
proc first*(jArray: JsonNode, fieldName, id: string): JsonNode =
|
||||
if jArray == nil:
|
||||
|
@ -139,13 +141,19 @@ proc isAddress*(strAddress: string): bool =
|
|||
return false
|
||||
return true
|
||||
|
||||
proc validateTransactionInput*(from_addr, to_addr, assetAddress, value, gas, gasPrice, data, uuid: string) =
|
||||
proc validateTransactionInput*(from_addr, to_addr, assetAddress, value, gas, gasPrice, data: string, isEIP1599Enabled: bool, maxPriorityFeePerGas, maxFeePerGas, uuid: string) =
|
||||
if not isAddress(from_addr): raise newException(ValueError, "from_addr is not a valid ETH address")
|
||||
if not isAddress(to_addr): raise newException(ValueError, "to_addr is not a valid ETH address")
|
||||
if parseFloat(value) < 0: raise newException(ValueError, "value should be a number >= 0")
|
||||
if parseInt(gas) <= 0: raise newException(ValueError, "gas should be a number > 0")
|
||||
if parseFloat(gasPrice) <= 0: raise newException(ValueError, "gasPrice should be a number > 0")
|
||||
|
||||
if isEIP1599Enabled:
|
||||
if gasPrice != "" and (maxPriorityFeePerGas != "" or maxFeePerGas != ""):
|
||||
raise newException(ValueError, "gasPrice can't be used with maxPriorityFeePerGas and maxFeePerGas")
|
||||
if gasPrice == "":
|
||||
if parseFloat(maxPriorityFeePerGas) <= 0: raise newException(ValueError, "maxPriorityFeePerGas should be a number > 0")
|
||||
if parseFloat(maxFeePerGas) <= 0: raise newException(ValueError, "maxFeePerGas should be a number > 0")
|
||||
else:
|
||||
if parseFloat(gasPrice) <= 0: raise newException(ValueError, "gasPrice should be a number > 0")
|
||||
if uuid.isEmptyOrWhitespace(): raise newException(ValueError, "uuid is required")
|
||||
|
||||
if assetAddress != "": # If a token is being used
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import json, strformat, strutils, chronicles, sequtils, sugar, httpclient, tables, net
|
||||
import json_serialization, stint
|
||||
from web3/ethtypes import Address, EthSend, Quantity
|
||||
import json_serialization, stint, stew/byteutils, algorithm
|
||||
from web3/ethtypes import Address, Quantity
|
||||
from web3/conversions import `$`
|
||||
from libstatus/core import getBlockByNumber
|
||||
import libstatus/accounts as status_accounts
|
||||
|
@ -10,7 +10,7 @@ import libstatus/wallet as status_wallet
|
|||
import libstatus/accounts/constants as constants
|
||||
import libstatus/eth/[eth, contracts]
|
||||
from libstatus/core import getBlockByNumber
|
||||
from utils as libstatus_utils import eth2Wei, gwei2Wei, first, toUInt64, parseAddress
|
||||
from utils as libstatus_utils import eth2Wei, gwei2Wei, wei2Gwei, first, toUInt64, parseAddress
|
||||
import wallet/[balance_manager, collectibles]
|
||||
import wallet/account as wallet_account
|
||||
import transactions
|
||||
|
@ -38,6 +38,8 @@ type WalletModel* = ref object
|
|||
defaultCurrency*: string
|
||||
tokens*: seq[Erc20Contract]
|
||||
totalBalance*: float
|
||||
eip1559Enabled*: bool
|
||||
latestBaseFee*: string
|
||||
|
||||
proc getDefaultCurrency*(self: WalletModel): string
|
||||
proc calculateTotalFiatBalance*(self: WalletModel)
|
||||
|
@ -49,6 +51,7 @@ proc newWalletModel*(events: EventEmitter): WalletModel =
|
|||
result.events = events
|
||||
result.defaultCurrency = ""
|
||||
result.totalBalance = 0.0
|
||||
result.eip1559Enabled = false
|
||||
|
||||
proc initEvents*(self: WalletModel) =
|
||||
self.events.on("currencyChanged") do(e: Args):
|
||||
|
@ -64,12 +67,12 @@ proc initEvents*(self: WalletModel) =
|
|||
proc delete*(self: WalletModel) =
|
||||
discard
|
||||
|
||||
proc buildTokenTransaction(source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = ""): EthSend =
|
||||
proc buildTokenTransaction(source, to, assetAddress: Address, value: float, transfer: var Transfer, contract: var Erc20Contract, gas = "", gasPrice = "", isEIP1559Enabled: bool = false, maxPriorityFeePerGas = "", maxFeePerGas = ""): TransactionData =
|
||||
contract = getErc20Contract(assetAddress)
|
||||
if contract == nil:
|
||||
raise newException(ValueError, fmt"Could not find ERC-20 contract with address '{assetAddress}' for the current network")
|
||||
transfer = Transfer(to: to, value: eth2Wei(value, contract.decimals))
|
||||
transactions.buildTokenTransaction(source, assetAddress, gas, gasPrice)
|
||||
transactions.buildTokenTransaction(source, assetAddress, gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas)
|
||||
|
||||
proc getKnownTokenContract*(self: WalletModel, address: Address): Erc20Contract =
|
||||
getErc20Contracts().concat(getCustomTokens()).getErc20ContractByAddress(address)
|
||||
|
@ -99,15 +102,17 @@ proc confirmTransactionStatus(self: WalletModel, pendingTransactions: JsonNode,
|
|||
)
|
||||
self.events.emit(parseEnum[PendingTransactionType](trx["type"].getStr).confirmed, ev)
|
||||
|
||||
proc getLatestBlockNumber*(self: WalletModel): int =
|
||||
proc getLatestBlock*(): tuple[blockNumber: int, baseFee: string] =
|
||||
let response = getBlockByNumber("latest").parseJson()
|
||||
if not response.hasKey("result"):
|
||||
return -1
|
||||
if response.hasKey("result"):
|
||||
let blockNumber = parseInt($fromHex(Stuint[256], response["result"]["number"].getStr))
|
||||
let baseFee = $fromHex(Stuint[256], response["result"]{"baseFeePerGas"}.getStr)
|
||||
return (blockNumber, baseFee)
|
||||
return (-1, "")
|
||||
|
||||
return parseInt($fromHex(Stuint[256], response["result"]["number"].getStr))
|
||||
proc getLatestBlockNumber*(self: WalletModel): int = getLatestBlock()[0]
|
||||
|
||||
proc checkPendingTransactions*(self: WalletModel) =
|
||||
let latestBlockNumber = self.getLatestBlockNumber()
|
||||
proc checkPendingTransactions*(self: WalletModel, latestBlockNumber: int) =
|
||||
if latestBlockNumber == -1:
|
||||
return
|
||||
|
||||
|
@ -133,10 +138,10 @@ proc estimateTokenGas*(self: WalletModel, source, to, assetAddress, value: strin
|
|||
|
||||
result = contract.methods["transfer"].estimateGas(tx, transfer, success)
|
||||
|
||||
proc sendTransaction*(source, to, value, gas, gasPrice, password: string, success: var bool, data = ""): string =
|
||||
proc sendTransaction*(source, to, value, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas, maxFeePerGas, password: string, success: var bool, data = ""): string =
|
||||
var tx = transactions.buildTransaction(
|
||||
parseAddress(source),
|
||||
eth2Wei(parseFloat(value), 18), gas, gasPrice, data
|
||||
eth2Wei(parseFloat(value), 18), gas, gasPrice, isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas, data
|
||||
)
|
||||
|
||||
if to != "":
|
||||
|
@ -146,7 +151,7 @@ proc sendTransaction*(source, to, value, gas, gasPrice, password: string, succes
|
|||
if success:
|
||||
trackPendingTransaction(result, $source, $to, PendingTransactionType.WalletTransfer, "")
|
||||
|
||||
proc sendTokenTransaction*(source, to, assetAddress, value, gas, gasPrice, password: string, success: var bool): string =
|
||||
proc sendTokenTransaction*(source, to, assetAddress, value, gas, gasPrice: string, isEIP1559Enabled: bool, maxPriorityFeePerGas, maxFeePerGas, password: string, success: var bool): string =
|
||||
var
|
||||
transfer: Transfer
|
||||
contract: Erc20Contract
|
||||
|
@ -158,7 +163,8 @@ proc sendTokenTransaction*(source, to, assetAddress, value, gas, gasPrice, passw
|
|||
transfer,
|
||||
contract,
|
||||
gas,
|
||||
gasPrice
|
||||
gasPrice,
|
||||
isEIP1559Enabled, maxPriorityFeePerGas, maxFeePerGas
|
||||
)
|
||||
|
||||
result = contract.methods["transfer"].send(tx, transfer, password, success)
|
||||
|
@ -217,6 +223,37 @@ proc newAccount*(self: WalletModel, walletType: string, derivationPath: string,
|
|||
updateBalance(account, self.getDefaultCurrency())
|
||||
account
|
||||
|
||||
proc maxPriorityFeePerGas*(self: WalletModel):string =
|
||||
let response = status_wallet.maxPriorityFeePerGas().parseJson()
|
||||
if response.hasKey("result"):
|
||||
return $fromHex(Stuint[256], response["result"].getStr)
|
||||
else:
|
||||
error "Error obtaining max priority fee per gas", error=response
|
||||
raise newException(StatusGoException, "Error obtaining max priority fee per gas")
|
||||
|
||||
proc suggestFees*(self: WalletModel):JsonNode =
|
||||
let response = status_wallet.suggestFees().parseJson()
|
||||
if response.hasKey("result"):
|
||||
return response["result"].getElems()[0]
|
||||
else:
|
||||
error "Error obtaining suggested fees", error=response
|
||||
raise newException(StatusGoException, "Error obtaining suggested fees")
|
||||
|
||||
proc cmpUint256(x, y: Uint256): int =
|
||||
if x > y: 1
|
||||
elif x == y: 0
|
||||
else: -1
|
||||
|
||||
proc feeHistory*(self: WalletModel, n:int):seq[Uint256] =
|
||||
let response = status_wallet.feeHistory(101).parseJson()
|
||||
if response.hasKey("result"):
|
||||
for it in response["result"]["baseFeePerGas"]:
|
||||
result.add(fromHex(Stuint[256], it.getStr))
|
||||
result.sort(cmpUint256)
|
||||
else:
|
||||
error "Error obtaining fee history", error=response
|
||||
raise newException(StatusGoException, "Error obtaining fee history")
|
||||
|
||||
proc initAccounts*(self: WalletModel) =
|
||||
self.tokens = status_tokens.getVisibleTokens()
|
||||
let accounts = status_wallet.getWalletAccounts()
|
||||
|
@ -354,23 +391,6 @@ proc getTransfersByAddress*(self: WalletModel, address: string, toBlock: Uint256
|
|||
proc validateMnemonic*(self: WalletModel, mnemonic: string): string =
|
||||
result = status_wallet.validateMnemonic(mnemonic).parseJSON()["error"].getStr
|
||||
|
||||
proc getGasPricePredictions*(): GasPricePrediction =
|
||||
if status_settings.getCurrentNetwork() != NetworkType.Mainnet:
|
||||
# TODO: what about other chains like xdai?
|
||||
return GasPricePrediction(safeLow: 1.0, standard: 2.0, fast: 3.0, fastest: 4.0)
|
||||
let secureSSLContext = newContext()
|
||||
let client = newHttpClient(sslContext = secureSSLContext)
|
||||
try:
|
||||
let url: string = fmt"https://etherchain.org/api/gasPriceOracle"
|
||||
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||
let response = client.request(url)
|
||||
result = Json.decode(response.body, GasPricePrediction)
|
||||
except Exception as e:
|
||||
echo "error getting gas price predictions"
|
||||
echo e.msg
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
proc checkRecentHistory*(self: WalletModel, addresses: seq[string]): string =
|
||||
result = status_wallet.checkRecentHistory(addresses)
|
||||
|
||||
|
@ -406,3 +426,35 @@ proc getOpenseaCollections*(address: string): string =
|
|||
|
||||
proc getOpenseaAssets*(address: string, collectionSlug: string, limit: int): string =
|
||||
result = status_wallet.getOpenseaAssets(address, collectionSlug, limit)
|
||||
|
||||
proc getGasPrice*(self: WalletModel): string =
|
||||
let response = status_wallet.getGasPrice().parseJson
|
||||
if response.hasKey("result"):
|
||||
return $fromHex(Stuint[256], response["result"].getStr)
|
||||
else:
|
||||
error "Error obtaining max priority fee per gas", error=response
|
||||
raise newException(StatusGoException, "Error obtaining gas price")
|
||||
|
||||
|
||||
proc setLatestBaseFee*(self: WalletModel, latestBaseFee: string) =
|
||||
self.latestBaseFee = latestBaseFee
|
||||
|
||||
proc getLatestBaseFee*(self: WalletModel): string =
|
||||
result = self.latestBaseFee
|
||||
|
||||
proc isEIP1559Enabled*(self: WalletModel, blockNumber: int):bool =
|
||||
let networkId = status_settings.getCurrentNetworkDetails().config.networkId
|
||||
let activationBlock = case status_settings.getCurrentNetworkDetails().config.networkId:
|
||||
of 3: 10499401 # Ropsten
|
||||
of 4: 8897988 # Rinkeby
|
||||
of 5: 5062605 # Goerli
|
||||
of 1: 12965000 # Mainnet
|
||||
else: -1
|
||||
if activationBlock > -1 and blockNumber >= activationBlock:
|
||||
result = true
|
||||
else:
|
||||
result = false
|
||||
self.eip1559Enabled = result
|
||||
|
||||
proc isEIP1559Enabled*(self: WalletModel): bool =
|
||||
result = self.eip1559Enabled
|
||||
|
|
Loading…
Reference in New Issue