Skip to content

Commit

Permalink
chore: Initial Oracle Datum Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
bhatt-deep committed Jul 26, 2024
0 parents commit 6155160
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Tests

on:
push:
branches: ["main"]
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: aiken-lang/[email protected]
with:
version: v1.0.29-alpha

- run: aiken fmt --check
- run: aiken check -D
- run: aiken build
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Aiken compilation artifacts
artifacts/
# Aiken's project working directory
build/
# Aiken's default documentation export
docs/
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Oracle Integration in Aiken

This repository demonstrates how to integrate oracles into Cardano smart contracts using the Aiken programming language. It includes an oracle datum module and will feature example implementations, such as a betting contract, to showcase practical applications of oracle data in Cardano dApps.

## Project Structure

- `lib/`: Houses the core oracle integration module.
- `oracle_datum.ak`: Contains the oracle datum definitions, functions, and associated tests.
- `types.ak`: Defines common types used across the project.
- `validators/`: Will contain the validator scripts, including the betting contract (to be implemented).
- `aiken.toml`: Project configuration file.
- `README.md`: This file, providing project overview and instructions.

## Oracle Datum Module

The oracle datum module in `lib/oracle_datum.ak` provides:

- Definitions for oracle data structures
- Functions for handling oracle data
- Tests for the oracle datum functionality

All tests for the oracle datum are included directly in the `oracle_datum.ak` file, allowing for easy reference and maintenance alongside the implementation.

## Building

To build the project, run:

```sh
aiken build
```

This will compile all Aiken modules and generate the necessary Plutus scripts.

## Testing

Since tests are included in the `oracle_datum.ak` file, you can run them using:

```sh
aiken check
```

To run tests matching a specific string (e.g., "oracle"), use:

```sh
aiken check -m oracle
```

## Usage

To use the oracle integration in your own project:

1. Copy the `lib/oracle_datum.ak` file to your project's `lib/` directory.
2. Import the oracle module in your validator:

```aiken
use oracle_datum
validator {
fn example(datum: Data, redeemer: Data, context: Data) -> Bool {
// Use oracle functions here
// e.g., oracle_datum.get_price(datum)
}
}
```

## Documentation

Generate HTML documentation for the project:

```sh
aiken docs
```

## Contributing

We welcome contributions! Please feel free to submit a Pull Request.
15 changes: 15 additions & 0 deletions aiken.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file was generated by Aiken
# You typically do not need to edit this file

[[requirements]]
name = "aiken-lang/stdlib"
version = "1.9.0"
source = "github"

[[packages]]
name = "aiken-lang/stdlib"
version = "1.9.0"
requirements = []
source = "github"

[etags]
16 changes: 16 additions & 0 deletions aiken.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name = "charli3/betting_contract"
version = "0.0.0"
compiler = "v1.0.29-alpha"
plutus = "v2"
license = "Apache-2.0"
description = "A betting contract using Charli3 price feeds"

[repository]
user = "charli3"
project = "betting_contract"
platform = "github"

[[dependencies]]
name = "aiken-lang/stdlib"
version = "1.9.0"
source = "github"
168 changes: 168 additions & 0 deletions lib/oracle_datum.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use aiken/cbor
use aiken/hash.{Blake2b_224, Hash}
use aiken/pairs
use aiken/transaction/credential.{Script}

pub type PriceMap =
Pairs<Int, Int>

pub type PriceData {
SharedData
ExtendedData
GenericData { price_map: PriceMap }
}

pub type OracleDatum {
price_data: PriceData,
}

pub type ValidatorHash =
Hash<Blake2b_224, Script>

// Function to get the price from PriceData
pub fn get_price(price_data: PriceData) -> Int {
when price_data is {
GenericData { price_map } ->
when pairs.get_first(price_map, 0) is {
Some(price) -> price
None -> fail @"Price not found"
}
_ -> fail @"Invalid PriceData variant"
}
}

// Function to get the timestamp from PriceData
pub fn get_timestamp(price_data: PriceData) -> Int {
when price_data is {
GenericData { price_map } ->
when pairs.get_first(price_map, 1) is {
Some(timestamp) -> timestamp
None -> fail @"Timestamp not found"
}
_ -> fail @"Invalid PriceData variant"
}
}

// Function to get the expiry from PriceData
pub fn get_expiry(price_data: PriceData) -> Int {
when price_data is {
GenericData { price_map } ->
when pairs.get_first(price_map, 2) is {
Some(expiry) -> expiry
None -> fail @"Expiry not found"
}
_ -> fail @"Invalid PriceData variant"
}
}

// Function to check if the oracle data is valid (not expired)
pub fn is_oracle_valid(oracle_datum: OracleDatum, current_time: Int) -> Bool {
current_time <= get_expiry(oracle_datum.price_data)
}

// Function to get the price from OracleDatum
pub fn get_oracle_price(oracle_datum: OracleDatum) -> Int {
get_price(oracle_datum.price_data)
}

// Function to get the timestamp from OracleDatum
pub fn get_oracle_timestamp(oracle_datum: OracleDatum) -> Int {
get_timestamp(oracle_datum.price_data)
}

// Test for get_price()
test create_and_get_price() {
let datum =
OracleDatum {
price_data: GenericData(
[Pair(0, 2723), Pair(1, 1720056807332), Pair(2, 1720078407332)],
),
}
get_price(datum.price_data) == 2723 && get_oracle_price(datum) == 2723
}

// Test for get_timestamp()
test create_and_get_timestamp() {
let datum =
OracleDatum {
price_data: GenericData(
[Pair(0, 2723), Pair(1, 1720056807332), Pair(2, 1720078407332)],
),
}
get_timestamp(datum.price_data) == 1720056807332 && get_oracle_timestamp(
datum,
) == 1720056807332
}

// Test for get_expiry()
test create_and_get_expiry() {
let datum =
OracleDatum {
price_data: GenericData(
[Pair(0, 2723), Pair(1, 1720056807332), Pair(2, 1720078407332)],
),
}
get_expiry(datum.price_data) == 1720078407332
}

// Test for is_oracle_valid()
test is_oracle_valid_test() {
let datum =
OracleDatum {
price_data: GenericData(
[Pair(0, 2723), Pair(1, 1720056807332), Pair(2, 1720078407332)],
),
}
is_oracle_valid(datum, 1720056807332) == True && is_oracle_valid(
datum,
1720078407333,
) == False
}

test oracle_datum_diagnostic() {
let datum =
OracleDatum {
price_data: GenericData(
[Pair(0, 490850), Pair(1, 1707172554600), Pair(2, 1707176154600)],
),
}
let expected_diagnostic =
@"121([_ 123([_ {_ 0: 490850, 1: 1707172554600, 2: 1707176154600 }])])"

cbor.diagnostic(datum) == expected_diagnostic
}

test oracle_datum_serialise() {
let datum =
OracleDatum {
price_data: GenericData(
[Pair(0, 490850), Pair(1, 1707172554600), Pair(2, 1707176154600)],
),
}
let expected_cbor =
#"d8799fd87b9fa3001a00077d62011b0000018d7b69e768021b0000018d7ba0d5e8ffff"

cbor.serialise(datum) == expected_cbor
}

test oracle_datum_getters() {
let datum =
OracleDatum {
price_data: GenericData(
[Pair(0, 2723), Pair(1, 1720056807332), Pair(2, 1720078407332)],
),
}

get_oracle_price(datum) == 2723 && get_oracle_timestamp(datum) == 1720056807332 && get_expiry(
datum.price_data,
) == 1720078407332
}

test valid_price_data() {
let valid_price_data =
GenericData([Pair(0, 100), Pair(1, 1720056807332), Pair(2, 1720078407332)])

let result = get_price(valid_price_data)

result == 100
}

0 comments on commit 6155160

Please sign in to comment.