Add Signer.populateTransaction()
This commit is contained in:
parent
4acc6ef45c
commit
6bd3e27e38
|
@ -11,3 +11,6 @@ export results
|
||||||
export stint
|
export stint
|
||||||
export upraises
|
export upraises
|
||||||
export address
|
export address
|
||||||
|
|
||||||
|
type
|
||||||
|
EthersError* = object of IOError
|
||||||
|
|
|
@ -11,7 +11,7 @@ type
|
||||||
Contract* = ref object of RootObj
|
Contract* = ref object of RootObj
|
||||||
provider: Provider
|
provider: Provider
|
||||||
address: Address
|
address: Address
|
||||||
ContractError* = object of IOError
|
ContractError* = object of EthersError
|
||||||
|
|
||||||
template raiseContractError(message: string) =
|
template raiseContractError(message: string) =
|
||||||
raise newException(ContractError, message)
|
raise newException(ContractError, message)
|
||||||
|
|
|
@ -16,7 +16,7 @@ type
|
||||||
JsonRpcSigner* = ref object of Signer
|
JsonRpcSigner* = ref object of Signer
|
||||||
provider: JsonRpcProvider
|
provider: JsonRpcProvider
|
||||||
address: ?Address
|
address: ?Address
|
||||||
JsonRpcProviderError* = object of IOError
|
JsonRpcProviderError* = object of EthersError
|
||||||
|
|
||||||
template raiseProviderError(message: string) =
|
template raiseProviderError(message: string) =
|
||||||
raise newException(JsonRpcProviderError, message)
|
raise newException(JsonRpcProviderError, message)
|
||||||
|
|
|
@ -26,17 +26,25 @@ func fromJson*(json: JsonNode, name: string, result: var Address) =
|
||||||
# UInt256
|
# UInt256
|
||||||
|
|
||||||
func `%`*(integer: UInt256): JsonNode =
|
func `%`*(integer: UInt256): JsonNode =
|
||||||
%toHex(integer)
|
%("0x" & toHex(integer))
|
||||||
|
|
||||||
func fromJson*(json: JsonNode, name: string, result: var UInt256) =
|
func fromJson*(json: JsonNode, name: string, result: var UInt256) =
|
||||||
result = UInt256.fromHex(json.getStr())
|
result = UInt256.fromHex(json.getStr())
|
||||||
|
|
||||||
# Transaction
|
# Transaction
|
||||||
|
|
||||||
func `%`*(tx: Transaction): JsonNode =
|
func `%`*(transaction: Transaction): JsonNode =
|
||||||
result = %{ "to": %tx.to, "data": %tx.data }
|
result = %{ "to": %transaction.to, "data": %transaction.data }
|
||||||
if sender =? tx.sender:
|
if sender =? transaction.sender:
|
||||||
result["from"] = %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
|
# BlockTag
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,10 @@ import ./provider
|
||||||
export basics
|
export basics
|
||||||
|
|
||||||
type Signer* = ref object of RootObj
|
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.} =
|
method provider*(signer: Signer): Provider {.base.} =
|
||||||
doAssert false, "not implemented"
|
doAssert false, "not implemented"
|
||||||
|
@ -28,3 +32,27 @@ method estimateGas*(signer: Signer,
|
||||||
|
|
||||||
method getChainId*(signer: Signer): Future[UInt256] {.base.} =
|
method getChainId*(signer: Signer): Future[UInt256] {.base.} =
|
||||||
signer.provider.getChainId()
|
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
|
sender*: ?Address
|
||||||
to*: Address
|
to*: Address
|
||||||
data*: seq[byte]
|
data*: seq[byte]
|
||||||
|
nonce*: ?UInt256
|
||||||
|
chainId*: ?UInt256
|
||||||
|
gasPrice*: ?UInt256
|
||||||
|
gasLimit*: ?UInt256
|
||||||
|
|
||||||
func `$`*(transaction: Transaction): string =
|
func `$`*(transaction: Transaction): string =
|
||||||
result = "("
|
result = "("
|
||||||
|
@ -12,4 +16,12 @@ func `$`*(transaction: Transaction): string =
|
||||||
result &= "from: " & $sender & ", "
|
result &= "from: " & $sender & ", "
|
||||||
result &= "to: " & $transaction.to & ", "
|
result &= "to: " & $transaction.to & ", "
|
||||||
result &= "data: 0x" & $transaction.data.toHex
|
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 &= ")"
|
result &= ")"
|
||||||
|
|
|
@ -4,15 +4,21 @@ import pkg/ethers
|
||||||
|
|
||||||
randomize()
|
randomize()
|
||||||
|
|
||||||
proc example*(_: type Address): Address =
|
proc example*[N](_: type array[N, byte]): array[N, byte] =
|
||||||
var address: array[20, byte]
|
var a: array[N, byte]
|
||||||
for b in address.mitems:
|
for b in a.mitems:
|
||||||
b = rand(byte)
|
b = rand(byte)
|
||||||
Address.init(address)
|
a
|
||||||
|
|
||||||
proc example*(_: type seq[byte]): seq[byte] =
|
proc example*(_: type seq[byte]): seq[byte] =
|
||||||
let length = rand(0..<20)
|
let length = rand(0..<20)
|
||||||
newSeqWith(length, rand(byte))
|
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 =
|
proc example*(_: type Transaction): Transaction =
|
||||||
Transaction(to: Address.example, data: seq[byte].example)
|
Transaction(to: Address.example, data: seq[byte].example)
|
||||||
|
|
|
@ -38,3 +38,38 @@ suite "JsonRpcSigner":
|
||||||
let signer = provider.getSigner()
|
let signer = provider.getSigner()
|
||||||
let chainId = await signer.getChainId()
|
let chainId = await signer.getChainId()
|
||||||
check chainId == 31337.u256 # hardhat chain id
|
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