RedStone
How RedStone oracles work with TON
RedStone oracles use an alternative design of providing oracle data to smart contracts. Instead of constantly persisting data on the contract's storage (by data providers), the information is brought on-chain only when needed (by end users). Until that moment data remains in the decentralized cache layer, which is powered by RedStone light cache gateways and streams data broadcasting protocol. Data is transferred to the contract by end users, who should attach signed data packages to their function invocations. The information integrity is verified on-chain through signature checking.
To learn more about RedStone oracles design go to the RedStone docs.
Documentation links
Smart contracts
price_manager.fc
- Sample oracle contract that consumes RedStone oracles data price_manager.fc written in FunC. It requires TVM Upgrade 2023.07.
initial data
As mentioned above, the data packages transferred to the contract are being verified by signature checking.
To be counted to achieve the signer_count_threshold
, the signer signing the passed data
should be one of the signers
passed in the initial data. There is also needed signer_count_threshold
to be
passed.
Due to the architecture of TON contracts, the initial data must convene with the contract's storage structure, which is constructed as below:
begin_cell()
.store_uint(signer_count_threshold, 8) /// number as passed below
.store_uint(timestamp, TIMESTAMP_BITS) /// initially 0 representing the epoch 0
.store_ref(signers) /// serialized tuple of values passed below
.end_cell();
The value of signers
should be passed as a serialized tuple
of int
s.
See tuple.
In the function parameters below, each feed_id
is a string encoded to int
which means, that's a value
consisting of hex-values of the particular letters in the string. For example:
'ETH'
as an int
is 0x455448
in hex or 4543560
in decimal, as 256*256*ord('E')+256*ord('T')+ord('H')
.
You can use: feed_id=hexlify(toUtf8Bytes(feed_string))
to convert particular values or
the endpoint.
The value of feed_ids
should be passed as a serialized tuple
of int
s.
The value payload
is packed from an array of bytes representing the serialized RedStone payload.
See TON RedStone payload packing section below, as well as the file constants.fc, containing all needed int
-length constants.
get_prices
(cell) get_prices_v2(cell data_feed_ids, cell payload) method_id;
The function process on-chain the payload
passed as an argument
and returns a cell
of aggregated values of each feed passed as an identifier inside feed_ids
.
Due to HTTP GET method length limitation in TON API v4, the function is written for TON API v2.
That are just a method_id
functions - they don't modify the contract's storage and don't consume TONs.
OP_REDSTONE_WRITE_PRICES
Regardless of the on-fly processing, there also exists a method for processing the payload
on-chain, but
saving/writing the aggregated values to the contract's storage. The values persist in the contract's storage and then can be read by using read_prices
function. The timestamp of data last saved/written to the contract is able to read by using the read_timestamp
function.
The method must be invoked as a TON internal message. The arguments of the message are:
- an
int
representing RedStone_Write_Prices name hashed by keccak256 as defined in constants.ts - a
cell
- ref representing thedata_feed_ids
as a serializedtuple
ofint
s.\ - a
cell
- ref representing the packed RedStone payload
int op = in_msg_body~load_uint(OP_NUMBER_BITS);
if (op == OP_REDSTONE_WRITE_PRICES) {
cell data_feeds_cell = in_msg_body~load_ref();
cell payload_cell = in_msg_body~load_ref();
// ...
}
That's an internal message - it consumes GAS and modifies the contract's storage, so must be paid by TONs.
See how it works on: https://ton-showroom.redstone.finance/