Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
18 changes: 18 additions & 0 deletions lazer/cardano/.github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Continuous Integration

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

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aiken-lang/setup-aiken@v1
with:
version: v1.1.21
- run: aiken fmt --check
- run: aiken check -D
- run: aiken build
Binary file not shown.
8 changes: 8 additions & 0 deletions lazer/cardano/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Aiken compilation artifacts
artifacts/
# Aiken's project working directory
build/
# Aiken's default documentation export
docs/
# Parameterized blueprint output (local)
plutus.applied.json
Binary file added lazer/cardano/.gitignore:Zone.Identifier
Binary file not shown.
70 changes: 70 additions & 0 deletions lazer/cardano/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Iron Piggy · Iron Pig

**Iron Pig** is a savings vault on **Cardano**: you deposit **ADA** and allowed stablecoins; the contract only releases funds when the **USD value** of what's locked — based on the ADA price provided by **Pyth** and the stablecoins rules — **reaches the goal** you set when creating the vault. Nobody can change that rule afterwards; only you can withdraw once it is met.

![Iron Pig — PythFlow pitch (still from video)](public/iron-pig-pitch-poster.jpg)

**PythFlow pitch:** [Watch on YouTube](https://www.youtube.com/watch?v=j5q2i8WTVcg) · [local copy (MP4)](public/IronPig%20by%20PythFlow%20PUBLI-PITCH%20v1.mp4)

**PythFlow litepaper (English, PDF):** [`PythFLOW_Litepaper_EN.pdf`](public/PythFLOW_Litepaper_EN.pdf)

This repository is the **first example** of Pyth oracle usage in the demo ecosystem: contract in **Aiken**, ready for **preprod**, with tests covering deposit, failed withdrawal, and successful withdrawal.

---

## Pyth Integration

In **Iron Piggy**, **Pyth Network** is the oracle layer for real-time market data.

Flow nodes can:

- **Read** price feeds from Pyth
- **Define** conditions based on that data
- **Incorporate** oracle logic into the contract behavior

**Typical flow:**

1. Start from a blank canvas
2. Add and configure nodes manually or with the help of the AI agent
3. Use nodes that integrate Pyth data where needed
4. **Generate** smart contract logic from the node graph
5. **Submit** the transaction to deploy the contract

_Iron Pig fits in steps 4–5: the rule "total value ≥ USD goal" is exactly the kind of condition that Pyth allows to materialize on-chain in a verifiable way._

---

## Tech Stack

| Layer | Role |
| ---------------- | -------------------------------------------------------------- |
| **Pyth Network** | Market prices (e.g. ADA/USD) that feed the unlock logic |
| **Aiken** | Iron Pig contract, on-chain tests and blueprint for deployment |
| **MeshJS** | Transaction building and signing on the client (UI / web flow) |
| **AI Agent** | Node orchestration and flow design assistance in PythFlow |

---

## Development (minimum)

You need [Aiken](https://aiken-lang.org) **v1.1.21+** (see `aiken.toml`).

```bash
aiken fmt --check && aiken check -D && aiken build
```

- Contract and tests: `validators/iron_pig.ak` · shared logic: `lib/iron_pig_logic.ak`
- **Scripts** (`scripts/`) and example **environment variables** (`env.preprod.example`) target **preprod** (`TESTNET_MAGIC=1`).
- The contract assumes an **oracle datum** in demo format aligned with Pyth; once the official payload exists on preprod, the datum shape and reading will be swapped — the core idea (Pyth policy + feed + price) remains.

Apache-2.0 (see `aiken.toml`).

---

## Team

- Lisandro Faure
- Santiago Amaya
- Luciano Bianco
- Facundo Couto
- Juan Garcia Carballo
Binary file added lazer/cardano/README.md:Zone.Identifier
Binary file not shown.
22 changes: 22 additions & 0 deletions lazer/cardano/aiken/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copy to .env.preprod and fill in (never commit secrets).

# Preprod
export TESTNET_MAGIC=1

# cardano-cli + local node
export CARDANO_NODE_SOCKET_PATH=/path/to/preprod/node.socket

# Optional: Blockfrost preproj for UTxO queries / composition helpers
# export BLOCKFROST_PROJECT_ID=preprod...

# Keys (example paths)
# export PAYMENT_SKEY=payment.skey
# export PAYMENT_ADDR=addr_test1...

# Pyth (preprod) — replace with values from official Pyth × Cardano docs
# export PYTH_POLICY_ID=...
# export PYTH_FEED_ADA_USD=... # 32-byte feed id as hex string

# Stablecoins you allow in the vault parameter (must match minted test tokens)
# export STABLE_POLICY_ID=...
# export STABLE_ASSET_NAME_HEX=55534443 # e.g. ASCII "USDC" as hex
Binary file added lazer/cardano/aiken/.env.example:Zone.Identifier
Binary file not shown.
Binary file added lazer/cardano/aiken/.env:Zone.Identifier
Binary file not shown.
8 changes: 8 additions & 0 deletions lazer/cardano/aiken/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Aiken compilation artifacts
.env
# Aiken's project working directory
build/
# Aiken's default documentation export
docs/
# Parameterized blueprint output (local)
plutus.applied.json
Binary file added lazer/cardano/aiken/.gitignore:Zone.Identifier
Binary file not shown.
27 changes: 27 additions & 0 deletions lazer/cardano/aiken/aiken.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This file was generated by Aiken
# You typically do not need to edit this file

[[requirements]]
name = "aiken-lang/stdlib"
version = "v3.0.0"
source = "github"

[[requirements]]
name = "pyth-network/pyth-lazer-cardano"
version = "main"
source = "github"

[[packages]]
name = "aiken-lang/stdlib"
version = "v3.0.0"
requirements = []
source = "github"

[[packages]]
name = "pyth-network/pyth-lazer-cardano"
version = "main"
requirements = []
source = "github"

[etags]
"pyth-network/pyth-lazer-cardano@main" = [{ secs_since_epoch = 1774214192, nanos_since_epoch = 775187264 }, "a46dacd97a22eb07feeaf966d48c3116c8249ddc836705656e3135cea285bcfc"]
Binary file added lazer/cardano/aiken/aiken.lock:Zone.Identifier
Binary file not shown.
23 changes: 23 additions & 0 deletions lazer/cardano/aiken/aiken.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name = "iron-piggy/iron-piggy"
version = "0.0.0"
compiler = "v1.1.21"
plutus = "v3"
license = "Apache-2.0"
description = "Iron Pig — Pyth-aware ADA + stable vault demo (preprod-oriented)"

[repository]
user = "iron-piggy"
project = "iron-piggy"
platform = "github"

[[dependencies]]
name = "aiken-lang/stdlib"
version = "v3.0.0"
source = "github"

[[dependencies]]
name = "pyth-network/pyth-lazer-cardano"
version = "main"
source = "github"

[config]
Binary file added lazer/cardano/aiken/aiken.toml:Zone.Identifier
Binary file not shown.
22 changes: 22 additions & 0 deletions lazer/cardano/aiken/env.preprod.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copy to .env.preprod and fill in (never commit secrets).

# Preprod
export TESTNET_MAGIC=1

# cardano-cli + local node
export CARDANO_NODE_SOCKET_PATH=/path/to/preprod/node.socket

# Optional: Blockfrost preproj for UTxO queries / composition helpers
# export BLOCKFROST_PROJECT_ID=preprod...

# Keys (example paths)
# export PAYMENT_SKEY=payment.skey
# export PAYMENT_ADDR=addr_test1...

# Pyth (preprod) — replace with values from official Pyth × Cardano docs
# export PYTH_POLICY_ID=...
# export PYTH_FEED_ADA_USD=... # 32-byte feed id as hex string

# Stablecoins you allow in the vault parameter (must match minted test tokens)
# export STABLE_POLICY_ID=...
# export STABLE_ASSET_NAME_HEX=55534443 # e.g. ASCII "USDC" as hex
Binary file not shown.
106 changes: 106 additions & 0 deletions lazer/cardano/aiken/lib/iron_pig_logic.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//// Iron Pig — shared types & valuation helpers for the vault validator.
////
//// ## Units
//// - **micro-USD**: 1 USD = 1_000_000 micro-USD (fixed-point, off-chain convention).
//// - **Stablecoins**: each ledger unit of a configured `(PolicyId, AssetName)` counts as **1 micro-USD**
//// (demo simplification; map decimals off-chain for real USDC/iUSD).
//// - **ADA / Pyth Lazer**: uses `pyth.get_updates` to read `(price, exponent)` for the ADA/USD feed.
//// On-chain: `micro_usd_for_ada = lovelace * price * 10^exponent`.

use aiken/collection/list
use aiken/crypto.{VerificationKeyHash}
use cardano/address.{Address}
use cardano/assets.{
AssetName, PolicyId, Value, ada_asset_name, ada_policy_id, flatten,
merge, quantity_of, zero,
}
use cardano/transaction.{Output}

/// Vault rules (set at lock time, preserved on every `Deposit`).
pub type IronPigDatum {
/// Target portfolio value in micro-USD.
goal_micro_usd: Int,
/// Only this key hash may sign `Withdraw` once the goal is met.
owner: VerificationKeyHash,
/// Pyth deployment policy ID (locates the "Pyth State" NFT in reference inputs).
pyth_policy_id: PolicyId,
/// Numeric Pyth Lazer feed ID (e.g. 16 for ADA/USD).
ada_usd_feed_id: Int,
}

pub type PigRedeemer {
Deposit
Withdraw
}

/// 10^e for small non-negative e (sufficient for typical Pyth exponents).
fn pow10_pos(e: Int) -> Int {
if e <= 0 {
1
} else {
10 * pow10_pos(e - 1)
}
}

/// Micro-USD contributed by locked ADA given Pyth price + exponent.
pub fn ada_value_micro_usd(lovelace: Int, price: Int, expo: Int) -> Int {
let n = lovelace * price
if expo >= 0 {
n * pow10_pos(expo)
} else {
n / pow10_pos(-expo)
}
}

/// Sum configured stable balances; **1 token unit == 1 micro-USD** (demo).
pub fn stable_micro_usd(
value: Value,
stables: List<(PolicyId, AssetName)>,
) -> Int {
list.foldr(
stables,
0,
fn(pair, acc) {
let (p, n) = pair
acc + quantity_of(value, p, n)
},
)
}

pub fn vault_total_micro_usd(
value: Value,
stables: List<(PolicyId, AssetName)>,
ada_price: Int,
ada_expo: Int,
) -> Int {
let ada_qty = quantity_of(value, ada_policy_id, ada_asset_name)
let ada_part = ada_value_micro_usd(ada_qty, ada_price, ada_expo)
let stable_part = stable_micro_usd(value, stables)
ada_part + stable_part
}

/// Every asset present in `inner` must appear in `outer` with >= quantity.
pub fn value_covers(inner: Value, outer: Value) -> Bool {
flatten(inner)
|> list.all(
fn(triple) {
let (p, n, q) = triple
quantity_of(outer, p, n) >= q
},
)
}

/// Sum of `value` on all outputs paid to `recipient`.
pub fn value_paid_to(outputs: List<Output>, recipient: Address) -> Value {
list.foldr(
outputs,
zero,
fn(o, acc) {
if o.address == recipient {
merge(acc, o.value)
} else {
acc
}
},
)
}
Binary file not shown.
1 change: 1 addition & 0 deletions lazer/cardano/aiken/params/stable_assets.empty.hex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
80
Binary file not shown.
Binary file not shown.
Loading