Skip to content

Commit

Permalink
fix: higher call gas estimate
Browse files Browse the repository at this point in the history
  • Loading branch information
wsdt committed Aug 17, 2024
1 parent 0894dfa commit 4703c3e
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 144 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/contract-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,4 @@ jobs:

- name: Run Forge tests
run: forge test -vvv
working-directory: ./contracts

- name: Display full logs
if: always()
run: cat local_env.log
working-directory: ./contracts
295 changes: 156 additions & 139 deletions backend/offchain/token-price.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
import Web3 from "web3";
import Web3, {DataFormat, FMT_NUMBER} from "web3";
import axios from "axios";
import "dotenv/config";
import {
OffchainParameter,
parseOffchainParameter,
parseRequest,
selector,
OffchainParameter,
parseOffchainParameter,
parseRequest,
selector,
} from "./utils";

const web3 = new Web3();

export async function offchainTokenPrice(params: OffchainParameter) {
const parsedParams = parseOffchainParameter(params);
const request = parseRequest(parsedParams);

try {
// Tokensymbol was encoded with a string in the smart-contract
const tokenSymbol = web3.eth.abi.decodeParameter(
"string",
request["reqBytes"]
) as string;
const tokenPrice = (await getTokenPrice(tokenSymbol)).toString();
console.log("token price: ", tokenPrice);

// Encode fetched token price as a string.
const encodedTokenPrice = web3.eth.abi.encodeParameter(
"string",
tokenPrice
);
console.log("ENCODED TOKEN PRICE = ", encodedTokenPrice);
return generateResponse(request, 0, encodedTokenPrice);
} catch (error: any) {
console.log("received error: ", error);
return generateResponse(request, 1, web3.utils.asciiToHex(error.message));
}
const parsedParams = parseOffchainParameter(params);
const request = parseRequest(parsedParams);

try {
// Tokensymbol was encoded with a string in the smart-contract
const tokenSymbol = web3.eth.abi.decodeParameter(
"string",
request["reqBytes"]
) as string;
const tokenPrice = (await getTokenPrice(tokenSymbol)).toString();
console.log("token price: ", tokenPrice);

// Encode fetched token price as a string.
const encodedTokenPrice = web3.eth.abi.encodeParameter(
"string",
tokenPrice
);
console.log("ENCODED TOKEN PRICE = ", encodedTokenPrice);
return generateResponse(request, 0, encodedTokenPrice);
} catch (error: any) {
console.log("received error: ", error);
return generateResponse(request, 1, web3.utils.asciiToHex(error.message));
}
}

/**
Expand All @@ -49,28 +49,28 @@ export async function offchainTokenPrice(params: OffchainParameter) {
* @throws {Error} - If the token is not found or if there is an issue with the API requests.
*/
export async function getTokenPrice(tokenSymbol: string): Promise<number> {
const headers = {
accept: "application/json",
"x-access-token": process.env.COINRANKING_API_KEY,
};

const coinListResponse = await axios.get(
"https://api.coinranking.com/v2/coins",
{ headers }
);
const token = coinListResponse.data.data.coins.find(
(c: any) => c.symbol === tokenSymbol
);

if (!token) {
throw new Error(`Token ${tokenSymbol} not found`);
}

const priceResponse = await axios.get(
`https://api.coinranking.com/v2/coin/${token.uuid}/price`,
{ headers }
);
return priceResponse.data.data.price;
const headers = {
accept: "application/json",
"x-access-token": process.env.COINRANKING_API_KEY,
};

const coinListResponse = await axios.get(
"https://api.coinranking.com/v2/coins",
{headers}
);
const token = coinListResponse.data.data.coins.find(
(c: any) => c.symbol === tokenSymbol
);

if (!token) {
throw new Error(`Token ${tokenSymbol} not found`);
}

const priceResponse = await axios.get(
`https://api.coinranking.com/v2/coin/${token.uuid}/price`,
{headers}
);
return priceResponse.data.data.price;
}

/**
Expand All @@ -88,93 +88,110 @@ export async function getTokenPrice(tokenSymbol: string): Promise<number> {
* @returns {object} - An object containing the success status, response payload, and signature.
* @throws {Error}
*/
export function generateResponse(
req: any,
errorCode: number,
respPayload: string
) {
if (!process.env.HC_HELPER_ADDR) {
throw new Error("HC_HELPER_ADDR not defined!");
}
const encodedResponse = web3.eth.abi.encodeParameters(
["address", "uint256", "uint32", "bytes"],
[req.srcAddr, req.srcNonce, errorCode, respPayload]
);
const putResponseCallData = web3.eth.abi.encodeParameters(
["bytes32", "bytes"],
[req.skey, encodedResponse]
);
const putResponseEncoded =
"0x" +
selector("PutResponse(bytes32,bytes)") +
putResponseCallData.slice(2);

const executeCallData = web3.eth.abi.encodeParameters(
["address", "uint256", "bytes"],
[
web3.utils.toChecksumAddress(process.env.HC_HELPER_ADDR),
0,
putResponseEncoded,
]
);
const executeEncoded =
"0x" +
selector("execute(address,uint256,bytes)") +
executeCallData.slice(2);

const gasEstimate = 705 * web3.utils.hexToBytes(respPayload).length + 170000;

const finalEncodedParameters = web3.eth.abi.encodeParameters(
[
"address",
"uint256",
"bytes32",
"bytes32",
"uint256",
"uint256",
"uint256",
"uint256",
"uint256",
"bytes32",
],
[
process.env.OC_HYBRID_ACCOUNT,
req.opNonce,
web3.utils.keccak256("0x"),
web3.utils.keccak256(executeEncoded),
gasEstimate,
0x10000,
0x10000,
0,
0,
web3.utils.keccak256("0x"),
]
);

const finalHash = web3.utils.keccak256(
web3.eth.abi.encodeParameters(
["bytes32", "address", "uint256"],
[
web3.utils.keccak256(finalEncodedParameters),
process.env.ENTRY_POINTS,
process.env.CHAIN_ID,
]
)
);

// Retrieve account from private key
const account = web3.eth.accounts.privateKeyToAccount(
process.env.OC_PRIVKEY!
);

// Sign the final hash
const signature = account.sign(finalHash);

console.log("Returning response payload:", respPayload);

return {
success: errorCode === 0,
response: respPayload,
signature: signature.signature,
};
export const generateResponse = async (
req: {
readonly srcAddr: string;
readonly reqBytes: string;
readonly srcNonce: bigint | number;
readonly skey: Uint8Array;
readonly opNonce: bigint | number
},
errorCode: number,
respPayload: string
) => {
if (!process.env.HC_HELPER_ADDR) {
throw new Error("HC_HELPER_ADDR not defined!");
}
const encodedResponse = web3.eth.abi.encodeParameters(
["address", "uint256", "uint32", "bytes"],
[req.srcAddr, req.srcNonce, errorCode, respPayload]
);
const putResponseCallData = web3.eth.abi.encodeParameters(
["bytes32", "bytes"],
[req.skey, encodedResponse]
);
const putResponseEncoded =
"0x" +
selector("PutResponse(bytes32,bytes)") +
putResponseCallData.slice(2);

const executeCallData = web3.eth.abi.encodeParameters(
["address", "uint256", "bytes"],
[
web3.utils.toChecksumAddress(process.env.HC_HELPER_ADDR),
0,
putResponseEncoded,
]
);
const executeEncoded =
"0x" +
selector("execute(address,uint256,bytes)") +
executeCallData.slice(2);

const gasEstimate = 705 * web3.utils.hexToBytes(respPayload).length + 170000;
let callGasEstimate: number = (await web3.eth.estimateGas({
from: process.env.ENTRY_POINTS,
to: req.srcAddr,
data: executeEncoded,
}, undefined, { number: FMT_NUMBER.NUMBER } as DataFormat)) as number

console.log("Call gas estimate: ", callGasEstimate)
if (gasEstimate > callGasEstimate) {
callGasEstimate = gasEstimate; // override with higher value
}
callGasEstimate *= 1.1 // + 10% buffer

const finalEncodedParameters = web3.eth.abi.encodeParameters(
[
"address",
"uint256",
"bytes32",
"bytes32",
"uint256",
"uint256",
"uint256",
"uint256",
"uint256",
"bytes32",
],
[
process.env.OC_HYBRID_ACCOUNT,
req.opNonce,
web3.utils.keccak256("0x"), // initcode
web3.utils.keccak256(executeEncoded),
callGasEstimate, // callGas
0x10000, // verificationGasLimit
0x10000, // preVerificationGas
0, // maxFeePerGas
0, // maxPriorityFeePerGas
web3.utils.keccak256("0x"), // paymasterAndData
]
);

const finalHash = web3.utils.keccak256(
web3.eth.abi.encodeParameters(
["bytes32", "address", "uint256"],
[
web3.utils.keccak256(finalEncodedParameters),
process.env.ENTRY_POINTS,
process.env.CHAIN_ID,
]
)
);

// Retrieve account from private key
const account = web3.eth.accounts.privateKeyToAccount(
process.env.OC_PRIVKEY!
);

// Sign the final hash
const signature = account.sign(finalHash);

console.log("Returning response payload:", respPayload);

return {
success: errorCode === 0,
response: respPayload,
signature: signature.signature,
};
}

0 comments on commit 4703c3e

Please sign in to comment.