diff --git a/contract_manager/src/contracts/ton.ts b/contract_manager/src/contracts/ton.ts index 94f1822d4e..4ef19687b1 100644 --- a/contract_manager/src/contracts/ton.ts +++ b/contract_manager/src/contracts/ton.ts @@ -5,7 +5,7 @@ import { TokenQty } from "../token"; import { DataSource } from "@pythnetwork/xc-admin-common"; import { Address, OpenedContract } from "@ton/ton"; import { - BASE_UPDATE_PRICE_FEEDS_FEE, + calculateUpdatePriceFeedsFee, PythContract, } from "@pythnetwork/pyth-ton-js"; @@ -237,7 +237,7 @@ export class TonPriceFeedContract extends PriceFeedContract { await contract.sendUpdatePriceFeeds( sender, vaa, - BASE_UPDATE_PRICE_FEEDS_FEE + BigInt(fee) + calculateUpdatePriceFeedsFee(BigInt(fee)) + BigInt(fee) ); } diff --git a/target_chains/ton/contracts/contracts/Pyth.fc b/target_chains/ton/contracts/contracts/Pyth.fc index 5380a249da..463a392c3f 100644 --- a/target_chains/ton/contracts/contracts/Pyth.fc +++ b/target_chains/ton/contracts/contracts/Pyth.fc @@ -26,7 +26,7 @@ slice read_and_verify_header(slice data) { throw_if(ERROR_INVALID_MINOR_VERSION, minor_version < MINIMUM_ALLOWED_MINOR_VERSION); int trailing_header_size = data~load_uint(8); ;; skip trailing headers - data~skip_bits(trailing_header_size); + data~skip_bits(trailing_header_size * 8); int update_type = data~load_uint(8); throw_unless(ERROR_INVALID_UPDATE_DATA_TYPE, update_type == WORMHOLE_MERKLE_UPDATE_TYPE); return data; @@ -172,7 +172,10 @@ int parse_pyth_payload_in_wormhole_vm(slice payload) impure { int num_updates = cs~load_uint(8); int update_fee = single_update_fee * num_updates; - int compute_fee = get_compute_fee(WORKCHAIN, UPDATE_PRICE_FEEDS_GAS); + int compute_fee = get_compute_fee( + WORKCHAIN, + UPDATE_PRICE_FEEDS_BASE_GAS + (UPDATE_PRICE_FEEDS_PER_UPDATE_GAS * num_updates) + ); throw_unless(ERROR_INSUFFICIENT_GAS, msg_value >= compute_fee); int remaining_msg_value = msg_value - compute_fee; diff --git a/target_chains/ton/contracts/contracts/common/gas.fc b/target_chains/ton/contracts/contracts/common/gas.fc index 0eeb90d285..541e3eaa8b 100644 --- a/target_chains/ton/contracts/contracts/common/gas.fc +++ b/target_chains/ton/contracts/contracts/common/gas.fc @@ -1,4 +1,13 @@ int get_compute_fee(int workchain, int gas_used) asm(gas_used workchain) "GETGASFEE"; -;; The actual gas used for the transaction is 350166 but we add ~10% (385182.6) and round up (390000) to be on the safe side because the amount of gas used can vary based on the current state of the blockchain -const int UPDATE_PRICE_FEEDS_GAS = 390000; +;; 1 update: 262,567 gas +;; 2 updates: 347,791 (+85,224) +;; 3 updates: 431,504 (+83,713) +;; 4 updates: 514,442 (+82,938) +;; 5 updates: 604,247 (+89,805) +;; 6 updates: 683,113 (+78,866) +;; 10 updates: 947,594 +;; Upper bound gas increase per additional update: ~90,000 +;; Base cost (1 update): ~262,567 gas +const UPDATE_PRICE_FEEDS_BASE_GAS = 300000; ;; Base cost + 10% safety margin rounded up because the amount of gas used can vary based on the current state of the blockchain +const UPDATE_PRICE_FEEDS_PER_UPDATE_GAS = 90000; ;; Per update cost diff --git a/target_chains/ton/contracts/tests/PythTest.spec.ts b/target_chains/ton/contracts/tests/PythTest.spec.ts index bd571bf59c..fad4c21a8f 100644 --- a/target_chains/ton/contracts/tests/PythTest.spec.ts +++ b/target_chains/ton/contracts/tests/PythTest.spec.ts @@ -28,7 +28,7 @@ import { serialize, } from "@wormhole-foundation/sdk-definitions"; import { mocks } from "@wormhole-foundation/sdk-definitions/testing"; -import { BASE_UPDATE_PRICE_FEEDS_FEE } from "@pythnetwork/pyth-ton-js"; +import { calculateUpdatePriceFeedsFee } from "@pythnetwork/pyth-ton-js"; const TIME_PERIOD = 60; const PRICE = new Price({ @@ -375,7 +375,7 @@ describe("PythTest", () => { const updateData = Buffer.from(HERMES_BTC_ETH_UPDATE, "hex"); - const result = await pythTest.sendUpdatePriceFeeds( + let result = await pythTest.sendUpdatePriceFeeds( deployer.getSender(), updateData, toNano("0.1") // Insufficient gas @@ -387,6 +387,19 @@ describe("PythTest", () => { success: false, exitCode: 3000, // ERROR_INSUFFICIENT_GAS }); + + result = await pythTest.sendUpdatePriceFeeds( + deployer.getSender(), + updateData, + calculateUpdatePriceFeedsFee(1n) // Send enough gas for 1 update instead of 2 + ); + + expect(result.transactions).toHaveTransaction({ + from: deployer.address, + to: pythTest.address, + success: false, + exitCode: 3000, // ERROR_INSUFFICIENT_GAS + }); }); it("should fail to update price feeds with insufficient fee", async () => { @@ -403,7 +416,7 @@ describe("PythTest", () => { const result = await pythTest.sendUpdatePriceFeeds( deployer.getSender(), updateData, - BASE_UPDATE_PRICE_FEEDS_FEE + BigInt(insufficientFee) + calculateUpdatePriceFeedsFee(2n) + BigInt(insufficientFee) ); // Check that the transaction did not succeed diff --git a/target_chains/ton/sdk/js/README.md b/target_chains/ton/sdk/js/README.md index 696d1739ca..3fa4c45d96 100644 --- a/target_chains/ton/sdk/js/README.md +++ b/target_chains/ton/sdk/js/README.md @@ -24,6 +24,7 @@ import { HermesClient } from "@pythnetwork/hermes-client"; import { PythContract, PYTH_CONTRACT_ADDRESS_TESTNET, + calculateUpdatePriceFeedsFee, } from "@pythnetwork/pyth-ton-js"; const BTC_PRICE_FEED_ID = @@ -73,7 +74,7 @@ async function main() { await contract.sendUpdatePriceFeeds( provider.sender(key.secretKey), updateData, - BASE_UPDATE_PRICE_FEEDS_FEE + BigInt(updateFee) + calculateUpdatePriceFeedsFee(1n) + BigInt(updateFee) ); console.log("Price feeds updated successfully."); diff --git a/target_chains/ton/sdk/js/src/index.ts b/target_chains/ton/sdk/js/src/index.ts index 4ab7b021a3..0145f799ed 100644 --- a/target_chains/ton/sdk/js/src/index.ts +++ b/target_chains/ton/sdk/js/src/index.ts @@ -13,11 +13,10 @@ import { ContractProvider } from "@ton/ton"; export const PYTH_CONTRACT_ADDRESS_TESTNET = "EQDwGkJmcj7MMmWAHmhldnY-lAKI6hcTQ2tAEcapmwCnztQU"; // This is defined in target_chains/ton/contracts/common/gas.fc -export const UPDATE_PRICE_FEEDS_GAS = 390000n; +export const UPDATE_PRICE_FEEDS_BASE_GAS = 300000n; +export const UPDATE_PRICE_FEEDS_PER_UPDATE_GAS = 90000n; // Current settings in basechain are as follows: 1 unit of gas costs 400 nanotons export const GAS_PRICE_FACTOR = 400n; -export const BASE_UPDATE_PRICE_FEEDS_FEE = - UPDATE_PRICE_FEEDS_GAS * GAS_PRICE_FACTOR; export interface DataSource { emitterChain: number; @@ -318,3 +317,11 @@ export function parseGuardianSetKeys(cell: Cell): string[] { parseCell(cell); return keys; } + +export function calculateUpdatePriceFeedsFee(numUpdates: bigint) { + return ( + (UPDATE_PRICE_FEEDS_BASE_GAS + + UPDATE_PRICE_FEEDS_PER_UPDATE_GAS * numUpdates) * + GAS_PRICE_FACTOR + ); +}