Skip to content

Commit 54da50c

Browse files
authored
fix(target_chains/ton): fix traling header size interpretation & compute fee calculation (#2082)
* fix trailing header size * address audit comments
1 parent 24be287 commit 54da50c

File tree

6 files changed

+46
-13
lines changed

6 files changed

+46
-13
lines changed

contract_manager/src/contracts/ton.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { TokenQty } from "../token";
55
import { DataSource } from "@pythnetwork/xc-admin-common";
66
import { Address, OpenedContract } from "@ton/ton";
77
import {
8-
BASE_UPDATE_PRICE_FEEDS_FEE,
8+
calculateUpdatePriceFeedsFee,
99
PythContract,
1010
} from "@pythnetwork/pyth-ton-js";
1111

@@ -237,7 +237,7 @@ export class TonPriceFeedContract extends PriceFeedContract {
237237
await contract.sendUpdatePriceFeeds(
238238
sender,
239239
vaa,
240-
BASE_UPDATE_PRICE_FEEDS_FEE + BigInt(fee)
240+
calculateUpdatePriceFeedsFee(BigInt(fee)) + BigInt(fee)
241241
);
242242
}
243243

target_chains/ton/contracts/contracts/Pyth.fc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ slice read_and_verify_header(slice data) {
2626
throw_if(ERROR_INVALID_MINOR_VERSION, minor_version < MINIMUM_ALLOWED_MINOR_VERSION);
2727
int trailing_header_size = data~load_uint(8);
2828
;; skip trailing headers
29-
data~skip_bits(trailing_header_size);
29+
data~skip_bits(trailing_header_size * 8);
3030
int update_type = data~load_uint(8);
3131
throw_unless(ERROR_INVALID_UPDATE_DATA_TYPE, update_type == WORMHOLE_MERKLE_UPDATE_TYPE);
3232
return data;
@@ -172,7 +172,10 @@ int parse_pyth_payload_in_wormhole_vm(slice payload) impure {
172172

173173
int num_updates = cs~load_uint(8);
174174
int update_fee = single_update_fee * num_updates;
175-
int compute_fee = get_compute_fee(WORKCHAIN, UPDATE_PRICE_FEEDS_GAS);
175+
int compute_fee = get_compute_fee(
176+
WORKCHAIN,
177+
UPDATE_PRICE_FEEDS_BASE_GAS + (UPDATE_PRICE_FEEDS_PER_UPDATE_GAS * num_updates)
178+
);
176179
throw_unless(ERROR_INSUFFICIENT_GAS, msg_value >= compute_fee);
177180
int remaining_msg_value = msg_value - compute_fee;
178181

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
int get_compute_fee(int workchain, int gas_used) asm(gas_used workchain) "GETGASFEE";
22

3-
;; 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
4-
const int UPDATE_PRICE_FEEDS_GAS = 390000;
3+
;; 1 update: 262,567 gas
4+
;; 2 updates: 347,791 (+85,224)
5+
;; 3 updates: 431,504 (+83,713)
6+
;; 4 updates: 514,442 (+82,938)
7+
;; 5 updates: 604,247 (+89,805)
8+
;; 6 updates: 683,113 (+78,866)
9+
;; 10 updates: 947,594
10+
;; Upper bound gas increase per additional update: ~90,000
11+
;; Base cost (1 update): ~262,567 gas
12+
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
13+
const UPDATE_PRICE_FEEDS_PER_UPDATE_GAS = 90000; ;; Per update cost

target_chains/ton/contracts/tests/PythTest.spec.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
serialize,
2929
} from "@wormhole-foundation/sdk-definitions";
3030
import { mocks } from "@wormhole-foundation/sdk-definitions/testing";
31-
import { BASE_UPDATE_PRICE_FEEDS_FEE } from "@pythnetwork/pyth-ton-js";
31+
import { calculateUpdatePriceFeedsFee } from "@pythnetwork/pyth-ton-js";
3232

3333
const TIME_PERIOD = 60;
3434
const PRICE = new Price({
@@ -375,7 +375,7 @@ describe("PythTest", () => {
375375

376376
const updateData = Buffer.from(HERMES_BTC_ETH_UPDATE, "hex");
377377

378-
const result = await pythTest.sendUpdatePriceFeeds(
378+
let result = await pythTest.sendUpdatePriceFeeds(
379379
deployer.getSender(),
380380
updateData,
381381
toNano("0.1") // Insufficient gas
@@ -387,6 +387,19 @@ describe("PythTest", () => {
387387
success: false,
388388
exitCode: 3000, // ERROR_INSUFFICIENT_GAS
389389
});
390+
391+
result = await pythTest.sendUpdatePriceFeeds(
392+
deployer.getSender(),
393+
updateData,
394+
calculateUpdatePriceFeedsFee(1n) // Send enough gas for 1 update instead of 2
395+
);
396+
397+
expect(result.transactions).toHaveTransaction({
398+
from: deployer.address,
399+
to: pythTest.address,
400+
success: false,
401+
exitCode: 3000, // ERROR_INSUFFICIENT_GAS
402+
});
390403
});
391404

392405
it("should fail to update price feeds with insufficient fee", async () => {
@@ -403,7 +416,7 @@ describe("PythTest", () => {
403416
const result = await pythTest.sendUpdatePriceFeeds(
404417
deployer.getSender(),
405418
updateData,
406-
BASE_UPDATE_PRICE_FEEDS_FEE + BigInt(insufficientFee)
419+
calculateUpdatePriceFeedsFee(2n) + BigInt(insufficientFee)
407420
);
408421

409422
// Check that the transaction did not succeed

target_chains/ton/sdk/js/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { HermesClient } from "@pythnetwork/hermes-client";
2424
import {
2525
PythContract,
2626
PYTH_CONTRACT_ADDRESS_TESTNET,
27+
calculateUpdatePriceFeedsFee,
2728
} from "@pythnetwork/pyth-ton-js";
2829

2930
const BTC_PRICE_FEED_ID =
@@ -73,7 +74,7 @@ async function main() {
7374
await contract.sendUpdatePriceFeeds(
7475
provider.sender(key.secretKey),
7576
updateData,
76-
BASE_UPDATE_PRICE_FEEDS_FEE + BigInt(updateFee)
77+
calculateUpdatePriceFeedsFee(1n) + BigInt(updateFee)
7778
);
7879
console.log("Price feeds updated successfully.");
7980

target_chains/ton/sdk/js/src/index.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ import { ContractProvider } from "@ton/ton";
1313
export const PYTH_CONTRACT_ADDRESS_TESTNET =
1414
"EQDwGkJmcj7MMmWAHmhldnY-lAKI6hcTQ2tAEcapmwCnztQU";
1515
// This is defined in target_chains/ton/contracts/common/gas.fc
16-
export const UPDATE_PRICE_FEEDS_GAS = 390000n;
16+
export const UPDATE_PRICE_FEEDS_BASE_GAS = 300000n;
17+
export const UPDATE_PRICE_FEEDS_PER_UPDATE_GAS = 90000n;
1718
// Current settings in basechain are as follows: 1 unit of gas costs 400 nanotons
1819
export const GAS_PRICE_FACTOR = 400n;
19-
export const BASE_UPDATE_PRICE_FEEDS_FEE =
20-
UPDATE_PRICE_FEEDS_GAS * GAS_PRICE_FACTOR;
2120

2221
export interface DataSource {
2322
emitterChain: number;
@@ -318,3 +317,11 @@ export function parseGuardianSetKeys(cell: Cell): string[] {
318317
parseCell(cell);
319318
return keys;
320319
}
320+
321+
export function calculateUpdatePriceFeedsFee(numUpdates: bigint) {
322+
return (
323+
(UPDATE_PRICE_FEEDS_BASE_GAS +
324+
UPDATE_PRICE_FEEDS_PER_UPDATE_GAS * numUpdates) *
325+
GAS_PRICE_FACTOR
326+
);
327+
}

0 commit comments

Comments
 (0)