Get Real-Time Weather Data with the Sui Weather Oracle

Integrate weather data into an app, or use its unpredictability as a seed for randomness.

Get Real-Time Weather Data with the Sui Weather Oracle

The new Sui Weather Oracle gives builders weather data for over 1,000 cities around the world and serves as a unique randomness generator, suitable for games and betting apps requiring a trustworthy random result. Consisting of a Sui-based smart contract and a backend service pulling weather data from the OpenWeather API, anyone can integrate weather data into their app.

The Sui Weather Oracle provides information such as temperature, humidity, and wind for any of its supported cities. The oracle can be used by other smart contracts or applications that need reliable and decentralized weather information for various purposes, such as travel, insurance, agriculture, gambling, or gaming.

weather page for Paris France from OpenWeather
The Sui Weather Oracle enables weather tracking and other apps using OpenWeather API data.

However, using weather for its randomness presents an intriguing use case. Weather is one of the most unpredictable and complex phenomena in nature. Weather forecasters do an admirable job of predicting rain or sunny skies, for the most part, but the minutiae, such as specific temperatures or wind speeds, is beyond the current science to nail down. 

As a fundamental concept in many fields of science, mathematics, and engineering, randomness can be used to generate secure cryptographic keys, test hypotheses, or simulate complex systems. However, generating true randomness is not easy, as most physical or computational processes are deterministic or biased in some way. Using weather data as an oracle input provides random outputs that are uniformly distributed and independent of any previous output.

Oracle data in apps

Oracles on Sui and other blockchains serve as conduits to offchain data, including sports scores, stock prices, and weather. Displaying this data directly in an app, the most obvious use, lets builders create stock portfolio management tools, weather trackers, and real-time football rankings. Data usage in this manner can become more complex, of course, with apps using sports scores to inform fantasy leagues as one example. 

Games may be based directly on oracle data. For example, an oracle can provide the odds and outcomes of various wagers for contests such as sports matches, political elections, or lottery draws. The players can then place bets on the outcomes based on the odds provided by the oracle. 

Things get more interesting when builders use oracles offering unpredictable data to generate randomness. For example, an oracle can use real-world events, such as weather data, sports results, or stock prices, to create random outcomes for a game. Alternatively, an oracle can use cryptographic methods, such as hash functions or digital signatures, to produce random numbers that are secure and provable. The fact that the oracle data comes from a verifiable source independent from the app makes the randomness outcomes trustworthy.

Weather data can be used as a seed for randomness to determine the results of character actions in a game. (Image source: RPGMaker)

Randomness based on unpredictable data from an oracle could be incorporated into game mechanics. For example, in a roleplaying game, an oracle can determine the success or failure of a character's action, such as casting a spell, hacking a computer, or persuading an NPC. The oracle can also affect the game world, such as changing the weather, spawning enemies, or triggering events. The game can use the oracle's data to create a fair and consistent randomness that is not influenced by the game developer or the player.

The Sui Weather Oracle

Oracles will enable a new generation of apps on Sui, bridging real-world data to its high-performance platform. Given the accessibility of the OpenWeather API and the usefulness of its data, a weather oracle makes for a good addition to Sui. 

Use cases

The Sui Weather Oracle is useful for many applications that require weather data, such as:

  • Randomness: Weather data can be used as a source of randomness for various purposes, such as generating random numbers, selecting winners, or creating unique NFTs. For example, a random number generator. can use the temperature, humidity, or wind speed of a specific location at a specific time as a seed.
  • Betting and gaming: Apps can use Sui Weather Oracle data to enable weather prediction wagers, weather-themed games, or weather-based rewards. For example, a game could let users bet on a city's weather, or an app could offer users NFTs based on the weather of different locations.
  • Other use cases: Apps for insurance, travel, education, or research could use weather data. For example, apps could factor in weather data to calculate the risk of natural disasters, plan a travel itinerary, teach students about weather patterns, or aid in setting up scientific experiments.

Structure

The Sui Weather Oracle, delivering up-to-date weather information for over 1,000 cities across the globe, consists of three components: an external service, an internal service, and a smart contract. The external service, the OpenWeather API, provides current weather data from various sources. The internal service is the weather oracle back-end, which fetches the weather data from the OpenWeather API every 10 minutes and updates the weather conditions for each city. The smart contract is the Sui Weather Oracle contract, which stores the weather data on the Sui blockchain and lets users access it in a secure and transparent manner. Users can also leverage the weather data for various decentralized applications that rely on the weather, such as gaming.

Earning storage rebates

The Sui Weather Oracle mitigates the cost of storing its real-time data on the network through the storage rebate mechanism. Sui supports onchain storage in its infrastructure and tokenomics model by adding a storage fee to its gas fees. The fund created from storage fees helps reimburse network operators for maintaining the hardware to store data and process transactions. As a means of keeping the quantity of onchain data under control, deleting onchain data renders a storage fund rebate. 

The Sui Weather Oracle stores and updates the weather data on the blockchain. The admin pays a one-time fee to create the initial list of CityWeatherOracle objects, and receives a rebate for updating the weather data for each city. The rebate is proportional to the amount of data updated, and the frequency of updates. This way, the admin can store and update the weather data on the blockchain at a low cost.

Leveraging Sui Object Display

The Sui Weather Oracle uses the Sui Object Display standard to dynamically update the icon for each city based on the current weather condition, showing rain clouds or a sun, for example. The Sui Object Display standard is a template engine that allows the onchain management of the offchain display for any type of object. It uses a template string that can be substituted with the data of the object, such as the weather ID of the city. The offchain display is handled by a back-end service that serves an icon for each one of the supported cities to the Sui explorer. The icon is chosen from a predefined set of icons that represent different weather conditions, such as sunny, cloudy, rainy, and snowy.

various weather icons
The Sui Object Display standard can be leveraged to serve icons representing different weather conditions. (Image by coolvector on Freepik)

Sui Weather Oracle smart contract 

The Sui Weather Oracle smart contract provides real-time and historical weather data for over 1,000 locations around the world and supports minting weather NFTs based on the weather data of a city. The smart contract also uses the Sui Object Display standard, enabling the dynamic and customizable display of the weather data and the weather NFTs on the blockchain. The smart contract has four main functions: add_city, remove_city, update, and mint

The oracle::weather module defines the following:

AdminCap, a struct that represents the administrator capability of the oracle owner.

struct AdminCap has key, store { id: UID }

WEATHER, a struct that sets up a Publisher.

struct WEATHER has drop {}

WeatherOracle, a struct, represents the oracle itself. It has fields id, address, name, and description that store the oracle's identifier, owner's address, name, and description respectively.

struct WeatherOracle has key {
    id: UID,
    address: address,
    name: String,
    description: String,
}

CityWeatherOracle, a struct that represents the weather data for a specific city. It has fields id, geoname_id, name, country, latitude, positive_latitude, longitude, positive_longitude, weather_id, temp, pressure, humidity, visibility, wind_speed, wind_deg, wind_gust, clouds, and dt that store the city's unique ID, geoname ID, name, country, latitude, longitude, weather ID, temperature, pressure, humidity, visibility, wind speed, wind direction, wind gust, cloudiness, and timestamp respectively.

struct CityWeatherOracle has key, store {
    id: UID,
    geoname_id: u32,
    name: String,
    country: String,
    latitude: u32,
    positive_latitude: bool,
    longitude: u32,
    positive_longitude: bool,
    weather_id: u16,
    temp: u32,
    pressure: u32,
    humidity: u8,
    visibility: u16,
    wind_speed: u16,
    wind_deg: u16,
    wind_gust: Option<u16>,
    clouds: u8,
    dt: u32
}

An init function that initializes the contract during the deployment, creating a new instance of WeatherOracle and sharing it publicly, and creating a new instance of AdminCap and transferring it to the sender.

fun init(otw: WEATHER, ctx: &mut TxContext) {
    package::claim_and_keep(otw, ctx);

    let cap = AdminCap { id: object::new(ctx) };
    transfer::share_object(WeatherOracle {
        id: object::new(ctx),
        address: tx_context::sender(ctx),
        name: string::utf8(b"SuiMeteo"),
        description: string::utf8(b"A weather oracle for posting weather updates (temperature, pressure, humidity, visibility, wind metrics and cloud state) for major cities around the world. Currently the data is fetched from https://openweathermap.org. SuiMeteo provides the best available information, but it does not guarantee its accuracy, completeness, reliability, suitability, or availability. Use it at your own risk and discretion."),
    });
    transfer::public_transfer(cap, tx_context::sender(ctx));
}

The add_city public function, which lets the owner of the AdminCap add a new city to the oracle service by providing its geoname_ID, name, country, latitude, and longitude. The function creates a new instance of CityWeatherOracle with default weather data and adds it to the oracle as a dynamic field, using the geoname_ID as the key.

public fun add_city(
    _: &AdminCap, 
    oracle: &mut WeatherOracle, 
    geoname_id: u32,
    name: String,
    country: String,
    latitude: u32,
    positive_latitude: bool,
    longitude: u32,
    positive_longitude: bool,
    ctx: &mut TxContext
) {
    dof::add(&mut oracle.id, geoname_id, 
        CityWeatherOracle {
            id: object::new(ctx),
            geoname_id,
            name, 
            country, 
            latitude, 
            positive_latitude, 
            longitude, 
            positive_longitude,
            weather_id: 0,
            temp: 0,
            pressure: 0,
            humidity: 0,
            visibility: 0,
            wind_speed: 0,
            wind_deg: 0,
            wind_gust: option::none(),
            clouds: 0,
            dt: 0
        }
    );
}

The remove_city public function, which lets the owner of the AdminCap remove an existing city from the oracle service by providing its geoname_ID. The function removes the corresponding instance of CityWeatherOracle from the oracle's dynamic fields and deletes the object.

public fun remove_city(_: &AdminCap, oracle: &mut WeatherOracle, geoname_id: u32) {
    let CityWeatherOracle { id, geoname_id: _, name: _, country: _, latitude: _, positive_latitude: _, longitude: _, positive_longitude: _, weather_id: _, temp: _, pressure: _, humidity: _, visibility: _, wind_speed: _, wind_deg: _, wind_gust: _, clouds: _, dt: _ } = dof::remove(&mut oracle.id, geoname_id);
    object::delete(id);
}

The update public function, which allows the oracle owner to update the weather data for an existing city by providing its geoname_ID and the new weather data. The function mutates the corresponding instance of CityWeatherOracle with the new weather data.

public fun update(
    _: &AdminCap,
    oracle: &mut WeatherOracle,
    geoname_id: u32,
    weather_id: u16,
    temp: u32,
    pressure: u32,
    humidity: u8,
    visibility: u16,
    wind_speed: u16,
    wind_deg: u16,
    wind_gust: Option<u16>,
    clouds: u8,
    dt: u32
) {
    let city_weather_oracle_mut = dof::borrow_mut<u32, CityWeatherOracle>(&mut oracle.id, geoname_id);
    city_weather_oracle_mut.weather_id = weather_id;
    city_weather_oracle_mut.temp = temp;
    city_weather_oracle_mut.pressure = pressure;
    city_weather_oracle_mut.humidity = humidity;
    city_weather_oracle_mut.visibility = visibility;
    city_weather_oracle_mut.wind_speed = wind_speed;
    city_weather_oracle_mut.wind_deg = wind_deg;
    city_weather_oracle_mut.wind_gust = wind_gust;
    city_weather_oracle_mut.clouds = clouds;
    city_weather_oracle_mut.dt = dt;
}

Integrating the Sui Weather Oracle

Using the Sui Weather Oracle in a Move project requires adding it as a dependency in the project's Move.toml file:

[package]
name = "..."version = "..."

[dependencies]
Sui = { git = "<https://github.com/MystenLabs/sui.git>", subdir = "crates/sui-framework/packages/sui-framework", rev = "..." }
oracle = { git = "<https://github.com/MystenLabs/apps>", subdir = "weather-oracle", rev = "db04fbd17d6ba91ade45c32f609b949fb47d209b" }

[addresses]
...
oracle = "0x8378b3bd39931aa74a6aa3a820304c1109d327426e4275183ed0b797eb6660a8"

Creating this dependency lets builders import the oracle::weather module into Move code and make use of the Weather Oracle and its functions. The Sui Weather Oracle provides real-time weather data, such as temperature, humidity, and wind speed, for different cities around the world. The city_weather_oracle_temp function retrieves the temperature of a city in Kelvin multiplied by 1,000, given its geoname_ID.

For example, the following code retrieves the current temperature of Paris, France (2988507):

use oracle::weather::{WeatherOracle};

fun get_temp(weather_oracle: &WeatherOracle): u32 {
    let geoname_id = 2988507; // Paris, France
    oracle::weather::city_weather_oracle_temp(weather_oracle, geoname_id)
}

Bridging data to Sui

The rise of big data over the last few decades has created vast repositories representing all manner of real world phenomenon and activity, from ocean currents to traffic flows in Manhattan. Responsible use of this data results in apps that make a positive impact on people's lives, whether it's helping predict forest fire danger or making popular concert tickets available in a fair distribution. 

The spread of oracles on Sui will increase the relevance of apps to people's daily lives, making the network an indispensable part of modern digital infrastructure.