I am trying to call quoteExactOutput() from Uniswap V3 QuoterV2 with dart.
This quoteExactOutput() function is not a view function. Therefore, what I want is to call the function without sending a transaction to the Ethereum blockchain (only to simulate the call).
What I expect from calling that function is to get the equivalent WETH amount of UNI token, e.g., 1.0 WETH is equal to 286.48961882910004 UNI.
Here is what I tried to call quoteExactOutput:
import 'dart:convert';
import 'dart:io';
import 'package:token_price_uniswap/utils/constant.dart';
import 'package:web3dart/web3dart.dart';
import 'package:http/http.dart';
Future<DeployedContract> loadContract() async {
final contract = DeployedContract(
ContractAbi.fromJson(uniswapV3QuoterV2Abi, "UniswapV3Quoter2"),
EthereumAddress.fromHex(uniswapV3QuoterV2Address));
return contract;
}
Future<String> sendTx(
String funcname,
List<dynamic> args,
Web3Client ethClient,
String privateKey,
) async {
EthPrivateKey credentials = EthPrivateKey.fromHex(privateKey);
DeployedContract contract = await loadContract();
final ethFunction = contract.function(funcname);
final result = await ethClient.sendTransaction(
credentials,
Transaction.callContract(
contract: contract,
function: ethFunction,
parameters: args,
),
chainId: null,
fetchChainIdFromNetworkId: true);
return result;
}
Future<List<dynamic>> fetchFromContract(
String funcName, List<dynamic> args, Web3Client ethClient) async {
final contract = await loadContract();
final calledFunction = contract.function(funcName);
final result = ethClient.call(
contract: contract,
function: calledFunction,
params: args,
);
return result;
}
Future<List> getWeth(Web3Client ethClient) async {
List<dynamic> result = await fetchFromContract("WETH9", [], ethClient);
print("called Uniswap V3 WETH");
return result;
}
Future<List<dynamic>> quoteExactOutput(
Web3Client ethClient,
List<int> path,
String amountIn,
) async {
final amountInBigInt = BigInt.parse(amountIn);
final params = [
path,
amountInBigInt, // Convert BigInt to int
];
print("params: $path");
final result = await fetchFromContract('quoteExactOutput', params, ethClient);
return result;
}
Future<void> main(List<String> arguments) async {
Web3Client? ethClient;
Client? httpClient;
httpClient = Client();
ethClient = Web3Client(rpcUrl, httpClient);
String wethAmount = '1000000000000000000'; // 1 WETH
final path = [uniswapTokenAddress, 3000, wethTokenAddress];
// call quote exact input
final encodedPath = encode(path);
var result = await quoteExactOutput(ethClient, encodedPath, wethAmount);
print("1 WETH is equal to ${result[0]} UNI");
}
However, I got this error:
Unhandled exception:
RPCError: got code -32000 with msg "execution reverted".
#0 JsonRPC.call (package:web3dart/json_rpc.dart:71:7)
<asynchronous suspension>
#1 Web3Client.makeRPCCall (package:web3dart/src/core/client.dart:53:20)
<asynchronous suspension>
#2 Web3Client.call (package:web3dart/src/core/client.dart:408:27)
<asynchronous suspension>
#3 quoteExactOutput(package:token_price_uniswap/services/uniswap_v3_functions.dart:108:18)
<asynchronous suspension>
#4 main (file:///D:/Eight/Programs/token_price_uniswap/bin/token_price_uniswap.dart:40:7)
<asynchronous suspension>
I suspect I made the path parameter incorrectly. What I know is in Solidity, we can create the path argument by using abi.encodePacked(). However, I do not know how to create the path with similar abi.encodePacked() function version in dart.
However, I tried with Python and it was success. Here is the code with Python (tutorial from here)
from web3 import Web3
import eth_abi.packed
import json
# setup our account and chain connection - we will use infura here
rpc_endpoint = "https://mainnet.infura.io/v3/INFURA_ID" #
web3 = Web3(Web3.HTTPProvider(rpc_endpoint))
# some addresses first
UNISWAP_v3_QUOTER2_ADDRESS = "0x61fFE014bA17989E743c5F6cB21bF9697530B21e"
UNISWAP_TOKEN_ADDRESS = "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"
WETH_TOKEN_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
# Load abi
f = open('./assets/uniswap_v3_quoter2.json')
UNISWAP_V3_QUOTER2_ABI = json.load(f)
# load the contracts
quoter2_contract = web3.eth.contract(address=UNISWAP_v3_QUOTER2_ADDRESS, abi=UNISWAP_V3_QUOTER2_ABI)
# prepare the function call parameters
path = eth_abi.packed.encode_packed(['address','uint24','address'], [WETH_TOKEN_ADDRESS, 3000, UNISWAP_TOKEN_ADDRESS])
amount_to_buy_for = 1 * 10**18
# call quote exact output
amount_out, sqrtPriceX96After, initializedTicksCrossed, gasEstimate = quoter2_contract.functions.quoteExactOutput(path, amount_to_buy_for).call()
print(f"for {amount_to_buy_for/10**18} WETH you would get {amount_out/10**18} UNI token")
output:
for 1.0 UNI token you have to pay 284.3230485269558 WETH
It turned out the problem was in the encode function in the dart. This is the code that equivalent to
abi.encodePacked(address token0, uint24 fee, address token1)to generate the path needed fromquoteExactOutput()function with dart:to see whether the output the same with solidity, we can check it by using the following function: