Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 34 additions & 20 deletions target_chains/ton/contracts/contracts/Pyth.fc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "common/governance_actions.fc";
#include "common/gas.fc";
#include "common/op.fc";
#include "common/error_handling.fc";
#include "./Wormhole.fc";

cell store_price(int price, int conf, int expo, int publish_time) {
Expand Down Expand Up @@ -369,33 +370,46 @@ cell create_price_feed_cell_chain(tuple price_feeds) {
}

() parse_price_feed_updates(int msg_value, slice update_data_slice, slice price_ids_slice, int min_publish_time, int max_publish_time, slice sender_address, slice target_address, slice custom_payload) impure {
load_data();
try {
load_data();

;; Load price_ids tuple
int price_ids_len = price_ids_slice~load_uint(8);
tuple price_ids = empty_tuple();
repeat(price_ids_len) {
int price_id = price_ids_slice~load_uint(256);
price_ids~tpush(price_id);
}

;; Load price_ids tuple
int price_ids_len = price_ids_slice~load_uint(8);
tuple price_ids = empty_tuple();
repeat(price_ids_len) {
int price_id = price_ids_slice~load_uint(256);
price_ids~tpush(price_id);
tuple price_feeds = parse_price_feeds_from_data(msg_value, update_data_slice, price_ids, min_publish_time, max_publish_time, false);
send_price_feeds_response(price_feeds, msg_value, OP_PARSE_PRICE_FEED_UPDATES,
sender_address, target_address, custom_payload);
} catch (_, error_code) {
;; Handle any unexpected errors
emit_error(error_code, OP_PARSE_PRICE_FEED_UPDATES,
sender_address, begin_cell().store_slice(custom_payload).end_cell());
}

tuple price_feeds = parse_price_feeds_from_data(msg_value, update_data_slice, price_ids, min_publish_time, max_publish_time, false);
send_price_feeds_response(price_feeds, msg_value, OP_PARSE_PRICE_FEED_UPDATES, sender_address, target_address, custom_payload);
}

() parse_unique_price_feed_updates(int msg_value, slice update_data_slice, slice price_ids_slice, int publish_time, int max_staleness, slice sender_address, slice target_address, slice custom_payload) impure {
load_data();
try {
load_data();

;; Load price_ids tuple
int price_ids_len = price_ids_slice~load_uint(8);
tuple price_ids = empty_tuple();
repeat(price_ids_len) {
int price_id = price_ids_slice~load_uint(256);
price_ids~tpush(price_id);
}

;; Load price_ids tuple
int price_ids_len = price_ids_slice~load_uint(8);
tuple price_ids = empty_tuple();
repeat(price_ids_len) {
int price_id = price_ids_slice~load_uint(256);
price_ids~tpush(price_id);
tuple price_feeds = parse_price_feeds_from_data(msg_value, update_data_slice, price_ids, publish_time, publish_time + max_staleness, true);
send_price_feeds_response(price_feeds, msg_value, OP_PARSE_UNIQUE_PRICE_FEED_UPDATES, sender_address, target_address, custom_payload);
} catch (_, error_code) {
;; Handle any unexpected errors
emit_error(error_code, OP_PARSE_UNIQUE_PRICE_FEED_UPDATES,
sender_address, begin_cell().store_slice(custom_payload).end_cell());
}

tuple price_feeds = parse_price_feeds_from_data(msg_value, update_data_slice, price_ids, publish_time, publish_time + max_staleness, true);
send_price_feeds_response(price_feeds, msg_value, OP_PARSE_UNIQUE_PRICE_FEED_UPDATES, sender_address, target_address, custom_payload);
}

() update_price_feeds(int msg_value, slice data) impure {
Expand Down
44 changes: 44 additions & 0 deletions target_chains/ton/contracts/contracts/common/error_handling.fc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "op.fc";
#include "errors.fc";
#include "constants.fc";

() emit_error(int error_code, int op, slice sender_address, cell custom_payload) impure inline {
;; Create error message cell with context
cell msg = begin_cell()
.store_uint(OP_RESPONSE_ERROR, 32)
.store_uint(error_code, 32)
.store_uint(op, 32)
.store_ref(custom_payload)
.end_cell();

;; Send error response back to sender
var msg = begin_cell()
.store_uint(0x18, 6) ;; nobounce
.store_slice(sender_address) ;; to_addr
.store_coins(0) ;; value
.store_uint(1, MSG_SERIALIZE_BITS) ;; msg header
.store_ref(msg) ;; error info
.end_cell();

send_raw_message(msg, 64);
}

() emit_success(slice sender_address, cell result, cell custom_payload) impure inline {
;; Create success message cell
cell msg = begin_cell()
.store_uint(OP_RESPONSE_SUCCESS, 32)
.store_ref(result) ;; Result data
.store_ref(custom_payload) ;; Original custom payload
.end_cell();

;; Send success response
var msg = begin_cell()
.store_uint(0x18, 6) ;; nobounce
.store_slice(sender_address) ;; to_addr
.store_coins(0) ;; value
.store_uint(1, MSG_SERIALIZE_BITS) ;; msg header
.store_ref(msg) ;; success info
.end_cell();

send_raw_message(msg, 64);
}
4 changes: 4 additions & 0 deletions target_chains/ton/contracts/contracts/common/op.fc
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ const int OP_EXECUTE_GOVERNANCE_ACTION = 3;
const int OP_UPGRADE_CONTRACT = 4;
const int OP_PARSE_PRICE_FEED_UPDATES = 5;
const int OP_PARSE_UNIQUE_PRICE_FEED_UPDATES = 6;

;; Response op codes
const int OP_RESPONSE_SUCCESS = 0x10001;
const int OP_RESPONSE_ERROR = 0x10002;
99 changes: 90 additions & 9 deletions target_chains/ton/contracts/tests/PythTest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1247,13 +1247,40 @@ describe("PythTest", () => {
CUSTOM_PAYLOAD
);

// Verify transaction success and message count
// Verify transaction success but error response sent
expect(result.transactions).toHaveTransaction({
from: deployer.address,
to: pythTest.address,
success: false,
exitCode: 2002, // ERROR_INVALID_MAGIC
success: true,
});

// Find the error response message - it's in the second transaction's outMessages
const errorTx = result.transactions[1]; // The PythTest contract transaction
expect(errorTx.outMessages.values().length).toBeGreaterThan(0);

const errorMessage = errorTx.outMessages.values()[0];
expect(errorMessage).toBeDefined();

const cs = errorMessage.body.beginParse();

// Verify error response format
const op = cs.loadUint(32);
expect(op).toBe(0x10002); // OP_RESPONSE_ERROR

const errorCode = cs.loadUint(32);
expect(errorCode).toBe(2002); // ERROR_INVALID_MAGIC

const originalOp = cs.loadUint(32);
expect(originalOp).toBe(5); // OP_PARSE_PRICE_FEED_UPDATES

// Verify custom payload is preserved
const customPayloadCell = cs.loadRef();
const customPayloadSlice = customPayloadCell.beginParse();
expect(
Buffer.from(
customPayloadSlice.loadBuffer(CUSTOM_PAYLOAD.length)
).toString("hex")
).toBe(CUSTOM_PAYLOAD.toString("hex"));
});

it("should fail to parse price feed updates within range", async () => {
Expand All @@ -1272,13 +1299,40 @@ describe("PythTest", () => {
CUSTOM_PAYLOAD
);

// Verify transaction success and message count
// Verify transaction success but error response sent
expect(result.transactions).toHaveTransaction({
from: deployer.address,
to: pythTest.address,
success: false,
exitCode: 2020, // ERROR_PRICE_FEED_NOT_FOUND_WITHIN_RANGE
success: true,
});

// Find the error response message - it's in the second transaction's outMessages
const errorTx = result.transactions[1]; // The PythTest contract transaction
expect(errorTx.outMessages.values().length).toBeGreaterThan(0);

const errorMessage = errorTx.outMessages.values()[0];
expect(errorMessage).toBeDefined();

const cs = errorMessage.body.beginParse();

// Verify error response format
const op = cs.loadUint(32);
expect(op).toBe(0x10002); // OP_RESPONSE_ERROR

const errorCode = cs.loadUint(32);
expect(errorCode).toBe(2020); // ERROR_PRICE_FEED_NOT_FOUND_WITHIN_RANGE

const originalOp = cs.loadUint(32);
expect(originalOp).toBe(6); // OP_PARSE_UNIQUE_PRICE_FEED_UPDATES

// Verify custom payload is preserved
const customPayloadCell = cs.loadRef();
const customPayloadSlice = customPayloadCell.beginParse();
expect(
Buffer.from(
customPayloadSlice.loadBuffer(CUSTOM_PAYLOAD.length)
).toString("hex")
).toBe(CUSTOM_PAYLOAD.toString("hex"));
});

it("should fail to parse unique price feed updates", async () => {
Expand All @@ -1297,13 +1351,40 @@ describe("PythTest", () => {
CUSTOM_PAYLOAD
);

// Verify transaction success and message count
// Verify transaction success but error response sent
expect(result.transactions).toHaveTransaction({
from: deployer.address,
to: pythTest.address,
success: false,
exitCode: 2020, // ERROR_PRICE_FEED_NOT_FOUND_WITHIN_RANGE
success: true,
});

// Find the error response message - it's in the second transaction's outMessages
const errorTx = result.transactions[1]; // The PythTest contract transaction
expect(errorTx.outMessages.values().length).toBeGreaterThan(0);

const errorMessage = errorTx.outMessages.values()[0];
expect(errorMessage).toBeDefined();

const cs = errorMessage.body.beginParse();

// Verify error response format
const op = cs.loadUint(32);
expect(op).toBe(0x10002); // OP_RESPONSE_ERROR

const errorCode = cs.loadUint(32);
expect(errorCode).toBe(2020); // ERROR_PRICE_FEED_NOT_FOUND_WITHIN_RANGE

const originalOp = cs.loadUint(32);
expect(originalOp).toBe(6); // OP_PARSE_UNIQUE_PRICE_FEED_UPDATES

// Verify custom payload is preserved
const customPayloadCell = cs.loadRef();
const customPayloadSlice = customPayloadCell.beginParse();
expect(
Buffer.from(
customPayloadSlice.loadBuffer(CUSTOM_PAYLOAD.length)
).toString("hex")
).toBe(CUSTOM_PAYLOAD.toString("hex"));
});

it("should successfully parse price feed updates in price ids order", async () => {
Expand Down
Loading