Handle multiple return types on a contract call
This commit is contained in:
parent
78115cdd4b
commit
9ef6c08072
|
@ -52,10 +52,13 @@ proc createTransaction(contract: Contract,
|
|||
let data = @selector & AbiEncoder.encode(parameters)
|
||||
Transaction(to: contract.address, data: data)
|
||||
|
||||
proc decodeResponse(T: type, bytes: seq[byte]): T =
|
||||
without decoded =? AbiDecoder.decode(bytes, (T,)):
|
||||
proc decodeResponse(T: type, multiple: static bool, bytes: seq[byte]): T =
|
||||
when multiple:
|
||||
without decoded =? AbiDecoder.decode(bytes, T):
|
||||
raiseContractError "unable to decode return value as " & $T
|
||||
return decoded[0]
|
||||
return decoded
|
||||
else:
|
||||
return decodeResponse((T,), true, bytes)[0]
|
||||
|
||||
proc call(contract: Contract,
|
||||
function: string,
|
||||
|
@ -68,10 +71,11 @@ proc call(contract: Contract,
|
|||
function: string,
|
||||
parameters: tuple,
|
||||
ReturnType: type,
|
||||
returnMultiple: static bool,
|
||||
blockTag = BlockTag.latest): Future[ReturnType] {.async.} =
|
||||
let transaction = createTransaction(contract, function, parameters)
|
||||
let response = await contract.provider.call(transaction, blockTag)
|
||||
return decodeResponse(ReturnType, response)
|
||||
return decodeResponse(ReturnType, returnMultiple, response)
|
||||
|
||||
proc send(contract: Contract, function: string, parameters: tuple):
|
||||
Future[?TransactionResponse] {.async.} =
|
||||
|
@ -102,22 +106,29 @@ func isConstant(procedure: NimNode): bool =
|
|||
return true
|
||||
false
|
||||
|
||||
func isMultipleReturn(returnType: NimNode): bool =
|
||||
(returnType.kind == nnkPar and returnType.len > 1) or
|
||||
(returnType.kind == nnkTupleConstr) or
|
||||
(returnType.kind == nnkTupleTy)
|
||||
|
||||
func addContractCall(procedure: var NimNode) =
|
||||
let contract = procedure[3][1][0]
|
||||
let function = $basename(procedure[0])
|
||||
let parameters = getParameterTuple(procedure)
|
||||
let returntype = procedure[3][0]
|
||||
let returnType = procedure[3][0]
|
||||
let returnMultiple = returnType.isMultipleReturn.newLit
|
||||
|
||||
func call: NimNode =
|
||||
if returntype.kind == nnkEmpty:
|
||||
if returnType.kind == nnkEmpty:
|
||||
quote:
|
||||
await call(`contract`, `function`, `parameters`)
|
||||
else:
|
||||
quote:
|
||||
return await call(`contract`, `function`, `parameters`, `returntype`)
|
||||
return await call(
|
||||
`contract`, `function`, `parameters`, `returnType`, `returnMultiple`)
|
||||
|
||||
func send: NimNode =
|
||||
if returntype.kind == nnkEmpty:
|
||||
if returnType.kind == nnkEmpty:
|
||||
quote:
|
||||
discard await send(`contract`, `function`, `parameters`)
|
||||
else:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import ./testJsonRpcProvider
|
||||
import ./testJsonRpcSigner
|
||||
import ./testContracts
|
||||
import ./testReturns
|
||||
import ./testEvents
|
||||
|
||||
{.warning[UnusedImport]:off.}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import pkg/asynctest
|
||||
import pkg/ethers
|
||||
import ./hardhat
|
||||
|
||||
type
|
||||
TestReturns = ref object of Contract
|
||||
Static = (UInt256, UInt256)
|
||||
Dynamic = (string, UInt256)
|
||||
|
||||
suite "Contract return values":
|
||||
|
||||
var contract: TestReturns
|
||||
var provider: JsonRpcProvider
|
||||
var snapshot: JsonNode
|
||||
|
||||
setup:
|
||||
provider = JsonRpcProvider.new("ws://localhost:8545")
|
||||
snapshot = await provider.send("evm_snapshot")
|
||||
let deployment = readDeployment()
|
||||
contract = TestReturns.new(!deployment.address(TestReturns), provider)
|
||||
|
||||
teardown:
|
||||
discard await provider.send("evm_revert", @[snapshot])
|
||||
|
||||
test "handles static size structs":
|
||||
proc getStatic(contract: TestReturns): Static {.contract, pure.}
|
||||
proc getStatics(contract: TestReturns): (Static, Static) {.contract, pure.}
|
||||
check (await contract.getStatic()) == (1.u256, 2.u256)
|
||||
check (await contract.getStatics()) == ((1.u256, 2.u256), (3.u256, 4.u256))
|
||||
|
||||
test "handles dynamic size structs":
|
||||
proc getDynamic(contract: TestReturns): Dynamic {.contract, pure.}
|
||||
proc getDynamics(contract: TestReturns): (Dynamic, Dynamic) {.contract, pure.}
|
||||
check (await contract.getDynamic()) == ("1", 2.u256)
|
||||
check (await contract.getDynamics()) == (("1", 2.u256), ("3", 4.u256))
|
||||
|
||||
test "handles mixed dynamic and static size structs":
|
||||
proc getDynamicAndStatic(contract: TestReturns): (Dynamic, Static) {.contract, pure.}
|
||||
check (await contract.getDynamicAndStatic()) == (("1", 2.u256), (3.u256, 4.u256))
|
||||
|
||||
test "handles return type that is a tuple with a single element":
|
||||
proc getDynamic(contract: TestReturns): (Dynamic,) {.contract, pure.}
|
||||
check (await contract.getDynamic()) == (("1", 2.u256),)
|
||||
|
||||
test "handles parentheses around return type":
|
||||
proc getDynamic(contract: TestReturns): (Dynamic) {.contract, pure.}
|
||||
check (await contract.getDynamic()) == ("1", 2.u256)
|
||||
|
||||
test "handles return type that is an explicit tuple":
|
||||
proc getDynamics(contract: TestReturns): tuple[a, b: Dynamic] {.contract, pure.}
|
||||
let values = await contract.getDynamics()
|
||||
check values.a == ("1", 2.u256)
|
||||
check values.b == ("3", 4.u256)
|
|
@ -0,0 +1,45 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
contract TestReturns {
|
||||
struct StaticStruct {
|
||||
uint256 a;
|
||||
uint256 b;
|
||||
}
|
||||
struct DynamicStruct {
|
||||
string a;
|
||||
uint256 b;
|
||||
}
|
||||
|
||||
function getStatic() external pure returns (StaticStruct memory) {
|
||||
return StaticStruct(1, 2);
|
||||
}
|
||||
|
||||
function getDynamic() external pure returns (DynamicStruct memory) {
|
||||
return DynamicStruct("1", 2);
|
||||
}
|
||||
|
||||
function getStatics()
|
||||
external
|
||||
pure
|
||||
returns (StaticStruct memory, StaticStruct memory)
|
||||
{
|
||||
return (StaticStruct(1, 2), StaticStruct(3, 4));
|
||||
}
|
||||
|
||||
function getDynamics()
|
||||
external
|
||||
pure
|
||||
returns (DynamicStruct memory, DynamicStruct memory)
|
||||
{
|
||||
return (DynamicStruct("1", 2), DynamicStruct("3", 4));
|
||||
}
|
||||
|
||||
function getDynamicAndStatic()
|
||||
external
|
||||
pure
|
||||
returns (DynamicStruct memory, StaticStruct memory)
|
||||
{
|
||||
return (DynamicStruct("1", 2), StaticStruct(3, 4));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = async ({ deployments, getNamedAccounts }) => {
|
||||
const { deployer } = await getNamedAccounts();
|
||||
await deployments.deploy("TestReturns", { from: deployer });
|
||||
};
|
||||
|
||||
module.exports.tags = ["TestReturns"];
|
Loading…
Reference in New Issue