Add Signer.populateTransaction()
This commit is contained in:
parent
4acc6ef45c
commit
6bd3e27e38
|
@ -11,3 +11,6 @@ export results
|
|||
export stint
|
||||
export upraises
|
||||
export address
|
||||
|
||||
type
|
||||
EthersError* = object of IOError
|
||||
|
|
|
@ -11,7 +11,7 @@ type
|
|||
Contract* = ref object of RootObj
|
||||
provider: Provider
|
||||
address: Address
|
||||
ContractError* = object of IOError
|
||||
ContractError* = object of EthersError
|
||||
|
||||
template raiseContractError(message: string) =
|
||||
raise newException(ContractError, message)
|
||||
|
|
|
@ -16,7 +16,7 @@ type
|
|||
JsonRpcSigner* = ref object of Signer
|
||||
provider: JsonRpcProvider
|
||||
address: ?Address
|
||||
JsonRpcProviderError* = object of IOError
|
||||
JsonRpcProviderError* = object of EthersError
|
||||
|
||||
template raiseProviderError(message: string) =
|
||||
raise newException(JsonRpcProviderError, message)
|
||||
|
|
|
@ -26,17 +26,25 @@ func fromJson*(json: JsonNode, name: string, result: var Address) =
|
|||
# UInt256
|
||||
|
||||
func `%`*(integer: UInt256): JsonNode =
|
||||
%toHex(integer)
|
||||
%("0x" & toHex(integer))
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var UInt256) =
|
||||
result = UInt256.fromHex(json.getStr())
|
||||
|
||||
# Transaction
|
||||
|
||||
func `%`*(tx: Transaction): JsonNode =
|
||||
result = %{ "to": %tx.to, "data": %tx.data }
|
||||
if sender =? tx.sender:
|
||||
func `%`*(transaction: Transaction): JsonNode =
|
||||
result = %{ "to": %transaction.to, "data": %transaction.data }
|
||||
if sender =? transaction.sender:
|
||||
result["from"] = %sender
|
||||
if nonce =? transaction.nonce:
|
||||
result["nonce"] = %nonce
|
||||
if chainId =? transaction.chainId:
|
||||
result["chainId"] = %chainId
|
||||
if gasPrice =? transaction.gasPrice:
|
||||
result["gasPrice"] = %gasPrice
|
||||
if gasLimit =? transaction.gasLimit:
|
||||
result["gas"] = %gasLimit
|
||||
|
||||
# BlockTag
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ import ./provider
|
|||
export basics
|
||||
|
||||
type Signer* = ref object of RootObj
|
||||
type SignerError* = object of EthersError
|
||||
|
||||
template raiseSignerError(message: string) =
|
||||
raise newException(SignerError, message)
|
||||
|
||||
method provider*(signer: Signer): Provider {.base.} =
|
||||
doAssert false, "not implemented"
|
||||
|
@ -28,3 +32,27 @@ method estimateGas*(signer: Signer,
|
|||
|
||||
method getChainId*(signer: Signer): Future[UInt256] {.base.} =
|
||||
signer.provider.getChainId()
|
||||
|
||||
method populateTransaction*(signer: Signer,
|
||||
transaction: Transaction):
|
||||
Future[Transaction] {.base, async.} =
|
||||
|
||||
if sender =? transaction.sender and sender != await signer.getAddress():
|
||||
raiseSignerError("from address mismatch")
|
||||
if chainId =? transaction.chainId and chainId != await signer.getChainId():
|
||||
raiseSignerError("chain id mismatch")
|
||||
|
||||
var populated = transaction
|
||||
|
||||
if transaction.sender.isNone:
|
||||
populated.sender = some(await signer.getAddress())
|
||||
if transaction.nonce.isNone:
|
||||
populated.nonce = some(await signer.getTransactionCount(BlockTag.pending))
|
||||
if transaction.chainId.isNone:
|
||||
populated.chainId = some(await signer.getChainId())
|
||||
if transaction.gasPrice.isNone:
|
||||
populated.gasPrice = some(await signer.getGasPrice())
|
||||
if transaction.gasLimit.isNone:
|
||||
populated.gasLimit = some(await signer.estimateGas(populated))
|
||||
|
||||
return populated
|
||||
|
|
|
@ -5,6 +5,10 @@ type Transaction* = object
|
|||
sender*: ?Address
|
||||
to*: Address
|
||||
data*: seq[byte]
|
||||
nonce*: ?UInt256
|
||||
chainId*: ?UInt256
|
||||
gasPrice*: ?UInt256
|
||||
gasLimit*: ?UInt256
|
||||
|
||||
func `$`*(transaction: Transaction): string =
|
||||
result = "("
|
||||
|
@ -12,4 +16,12 @@ func `$`*(transaction: Transaction): string =
|
|||
result &= "from: " & $sender & ", "
|
||||
result &= "to: " & $transaction.to & ", "
|
||||
result &= "data: 0x" & $transaction.data.toHex
|
||||
if nonce =? transaction.nonce:
|
||||
result &= ", nonce: 0x" & $nonce.toHex
|
||||
if chainId =? transaction.chainId:
|
||||
result &= ", chainId: " & $chainId
|
||||
if gasPrice =? transaction.gasPrice:
|
||||
result &= ", gasPrice: 0x" & $gasPrice.toHex
|
||||
if gasLimit =? transaction.gasLimit:
|
||||
result &= ", gasLimit: 0x" & $gasLimit.toHex
|
||||
result &= ")"
|
||||
|
|
|
@ -4,15 +4,21 @@ import pkg/ethers
|
|||
|
||||
randomize()
|
||||
|
||||
proc example*(_: type Address): Address =
|
||||
var address: array[20, byte]
|
||||
for b in address.mitems:
|
||||
proc example*[N](_: type array[N, byte]): array[N, byte] =
|
||||
var a: array[N, byte]
|
||||
for b in a.mitems:
|
||||
b = rand(byte)
|
||||
Address.init(address)
|
||||
a
|
||||
|
||||
proc example*(_: type seq[byte]): seq[byte] =
|
||||
let length = rand(0..<20)
|
||||
newSeqWith(length, rand(byte))
|
||||
|
||||
proc example*(_: type Address): Address =
|
||||
Address.init(array[20, byte].example)
|
||||
|
||||
proc example*(_: type UInt256): UInt256 =
|
||||
UInt256.fromBytesBE(array[32, byte].example)
|
||||
|
||||
proc example*(_: type Transaction): Transaction =
|
||||
Transaction(to: Address.example, data: seq[byte].example)
|
||||
|
|
|
@ -38,3 +38,38 @@ suite "JsonRpcSigner":
|
|||
let signer = provider.getSigner()
|
||||
let chainId = await signer.getChainId()
|
||||
check chainId == 31337.u256 # hardhat chain id
|
||||
|
||||
test "can populate missing fields in a transaction":
|
||||
let signer = provider.getSigner()
|
||||
let transaction = Transaction.example
|
||||
let populated = await signer.populateTransaction(transaction)
|
||||
check !populated.sender == await signer.getAddress()
|
||||
check !populated.gasPrice == await signer.getGasPrice()
|
||||
check !populated.nonce == await signer.getTransactionCount(BlockTag.pending)
|
||||
check !populated.gasLimit == await signer.estimateGas(transaction)
|
||||
check !populated.chainId == await signer.getChainId()
|
||||
|
||||
test "populate does not overwrite existing fields":
|
||||
let signer = provider.getSigner()
|
||||
var transaction = Transaction.example
|
||||
transaction.sender = some await signer.getAddress()
|
||||
transaction.nonce = some UInt256.example
|
||||
transaction.chainId = some await signer.getChainId()
|
||||
transaction.gasPrice = some UInt256.example
|
||||
transaction.gasLimit = some UInt256.example
|
||||
let populated = await signer.populateTransaction(transaction)
|
||||
check populated == transaction
|
||||
|
||||
test "populate fails when sender does not match signer address":
|
||||
let signer = provider.getSigner()
|
||||
var transaction = Transaction.example
|
||||
transaction.sender = accounts[1].some
|
||||
expect SignerError:
|
||||
discard await signer.populateTransaction(transaction)
|
||||
|
||||
test "populate fails when chain id does not match":
|
||||
let signer = provider.getSigner()
|
||||
var transaction = Transaction.example
|
||||
transaction.chainId = 0xdeadbeef.u256.some
|
||||
expect SignerError:
|
||||
discard await signer.populateTransaction(transaction)
|
||||
|
|
Loading…
Reference in New Issue