Skip to content
Merged
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
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ include mx/test.mk
include mx/deploy.mk
include mx/upgrade.mk
include mx/tokens.mk
include mx/oracle.mk

.PHONY: all help clean

Expand Down Expand Up @@ -38,6 +39,10 @@ help:
@printf '%s\n' ' make test-tokens-with-faucet NETWORK=testnet SOURCE=alice LONG_CODE=TWBTC SHORT_CODE=TUSDC'
@printf '%s\n' ' make faucet-claim-market NETWORK=testnet SOURCE=alice TO=alice'
@printf '%s\n' ''
@printf '%s\n' 'Oracle / keeper:'
@printf '%s\n' ' ORACLE=C... TOKEN=C... MIN_PRICE=5e32 MAX_PRICE=5e32 make submit-prices NETWORK=testnet SOURCE=alice'
@printf '%s\n' ' make grant-keeper KEEPER=alice NETWORK=testnet SOURCE=admin'
@printf '%s\n' ''
@printf '%s\n' 'Run make help-mx for the longer operator guide.'

clean:
Expand Down
32 changes: 32 additions & 0 deletions mx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,35 @@ That mints `10.0000000` TWBTC.

For mainnet, do not use `token-bootstrap` for real assets. Deploy or look up the
existing SAC for the real Stellar asset and configure markets with that address.

## Oracle — submitting prices

The keeper calls `set_prices_simple` on the oracle contract before each execution.
All four env vars below are **required** — the script exits non-zero immediately if
any are unset. There is no placeholder fallback.

```sh
export ORACLE=C... # deployed oracle contract address
export TOKEN=C... # token contract address to price
export MIN_PRICE=500000000000000000000000000000000 # FLOAT_PRECISION (10^30) scaled
export MAX_PRICE=500500000000000000000000000000000
make submit-prices NETWORK=testnet SOURCE=alice
```

`SOURCE` must be a key that holds the `ORDER_KEEPER` role in `role_store`.
Grant the role with:

```sh
make grant-keeper KEEPER=alice NETWORK=testnet SOURCE=admin
```

Where `SOURCE` (admin) holds the `ROLE_ADMIN` role and `KEEPER` is the key being
promoted. Both default to `alice` so a single-operator testnet setup works
without extra flags.

You can also call the script directly:

```sh
ORACLE=C... TOKEN=C... MIN_PRICE=... MAX_PRICE=... \
bash scripts/submit_prices.sh testnet alice
```
52 changes: 52 additions & 0 deletions mx/oracle.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Oracle / keeper price-submission workflows.
#
# All targets read ORACLE, TOKEN, MIN_PRICE, and MAX_PRICE from the environment
# or from .deployed/<network>.env. They NEVER fall back to placeholder values;
# if a required variable is unset the script exits non-zero immediately.
#
# The SOURCE key must hold the ORDER_KEEPER role. Grant it with:
# make grant-keeper SOURCE=admin KEEPER=alice NETWORK=testnet

.PHONY: submit-prices grant-keeper

# Submit a single token price through the oracle's set_prices_simple entrypoint.
#
# Required env vars (no defaults):
# ORACLE Deployed oracle contract address.
# TOKEN Token contract address to price.
# MIN_PRICE Minimum price, FLOAT_PRECISION-scaled integer (10^30).
# MAX_PRICE Maximum price, FLOAT_PRECISION-scaled integer (10^30).
#
# Example:
# ORACLE=C... TOKEN=C... MIN_PRICE=500000000000000000000000000000000 \
# MAX_PRICE=500500000000000000000000000000000 \
# make submit-prices NETWORK=testnet SOURCE=alice
submit-prices: preflight
@test -n "$(ORACLE)" || { printf '%s\n' 'ORACLE is not set. Export the deployed oracle address.'; exit 1; }
@test -n "$(TOKEN)" || { printf '%s\n' 'TOKEN is not set. Export the token contract address.'; exit 1; }
@test -n "$(MIN_PRICE)" || { printf '%s\n' 'MIN_PRICE is not set.'; exit 1; }
@test -n "$(MAX_PRICE)" || { printf '%s\n' 'MAX_PRICE is not set.'; exit 1; }
ORACLE="$(ORACLE)" TOKEN="$(TOKEN)" MIN_PRICE="$(MIN_PRICE)" MAX_PRICE="$(MAX_PRICE)" \
bash scripts/submit_prices.sh "$(NETWORK)" "$(SOURCE)"

# Grant ORDER_KEEPER role to a key so it can call set_prices_simple.
#
# Required:
# KEEPER Name of the Stellar key to promote (default: alice).
#
# Example:
# make grant-keeper SOURCE=alice KEEPER=alice NETWORK=testnet
KEEPER ?= alice
grant-keeper: preflight
@test -f "$(DEPLOY_ENV)" || { printf 'Missing %s. Run make deploy-all first.\n' "$(DEPLOY_ENV)"; exit 1; }
source "$(DEPLOY_ENV)"
keeper_addr="$$(stellar keys address "$(KEEPER)")"
stellar contract invoke \
--id "$$ROLE_STORE" \
--source "$(SOURCE)" \
--network "$(NETWORK)" \
-- grant_role \
--caller "$$(stellar keys address "$(SOURCE)")" \
--account "$$keeper_addr" \
--role "$(ORDER_KEEPER_ROLE)"
printf 'Granted ORDER_KEEPER to %s (%s)\n' "$(KEEPER)" "$$keeper_addr"
82 changes: 82 additions & 0 deletions scripts/submit_prices.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,86 @@
#!/usr/bin/env bash
# scripts/submit_prices.sh — Submit a single token price to the SO4.market oracle.
#
# Usage:
# TOKEN=<contract_id> MIN_PRICE=<i128> MAX_PRICE=<i128> \
# bash scripts/submit_prices.sh [NETWORK] [SOURCE_KEY]
#
# Required env vars (no defaults — script fails loudly if unset):
# ORACLE Address of the deployed oracle contract.
# TOKEN Address of the token whose price you are submitting.
# MIN_PRICE Minimum price (integer, scaled to the protocol's FLOAT_PRECISION).
# MAX_PRICE Maximum price (integer, scaled to the protocol's FLOAT_PRECISION).
#
# Optional:
# NETWORK testnet (default) | mainnet | local
# SOURCE_KEY Stellar key name of an authorized order-keeper (default: alice)
#
# The caller (SOURCE_KEY) must hold the ORDER_KEEPER role in role_store before
# this script will succeed. Register a keeper with:
# make grant-keeper SOURCE=admin KEEPER=alice NETWORK=testnet
#
# Example:
# ORACLE=C... TOKEN=C... MIN_PRICE=500000000000000000000000000000000 \
# MAX_PRICE=500500000000000000000000000000000 \
# bash scripts/submit_prices.sh testnet alice

set -euo pipefail

# ── Args ──────────────────────────────────────────────────────────────────────
NETWORK="${1:-testnet}"
SOURCE="${2:-alice}"

# ── Colours ───────────────────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; NC='\033[0m'

die() { printf "${RED}✖ %s${NC}\n" "$*" >&2; exit 1; }
ok() { printf " ${GREEN}✔${NC} %s\n" "$*"; }

# ── Validate required env vars ────────────────────────────────────────────────
# All four must be set externally. No placeholders, no fallbacks.

[[ -v ORACLE && -n "${ORACLE}" ]] || \
die "ORACLE is not set. Export the deployed oracle contract address:\n export ORACLE=C..."

[[ -v TOKEN && -n "${TOKEN}" ]] || \
die "TOKEN is not set. Export the token contract address you want to price:\n export TOKEN=C..."

[[ -v MIN_PRICE && -n "${MIN_PRICE}" ]] || \
die "MIN_PRICE is not set. Set the minimum price (FLOAT_PRECISION-scaled integer)."

[[ -v MAX_PRICE && -n "${MAX_PRICE}" ]] || \
die "MAX_PRICE is not set. Set the maximum price (FLOAT_PRECISION-scaled integer)."

# Sanity-check that min ≤ max so we catch configuration errors before hitting
# the contract's InvalidPrice guard.
if (( MIN_PRICE > MAX_PRICE )); then
die "MIN_PRICE ($MIN_PRICE) is greater than MAX_PRICE ($MAX_PRICE)."
fi

# ── Preflight ─────────────────────────────────────────────────────────────────
command -v stellar >/dev/null 2>&1 || \
die "stellar CLI not found. Install: cargo install stellar-cli --features opt"

stellar keys address "$SOURCE" >/dev/null 2>&1 || \
die "Key '$SOURCE' not found. Run: stellar keys generate --global $SOURCE --network $NETWORK"

# ── Submit price ──────────────────────────────────────────────────────────────
printf "${CYAN}▸${NC} Submitting price for %s on %s via %s\n" "$TOKEN" "$NETWORK" "$SOURCE" >&2
printf " min=%s max=%s\n" "$MIN_PRICE" "$MAX_PRICE" >&2

# set_prices_simple(caller, prices: Vec<TokenPrice>)
# TokenPrice is a struct with fields: token (Address), min (i128), max (i128).
# The Stellar CLI encodes a Vec of structs as repeated --prices args with
# a JSON-like struct value.
stellar contract invoke \
--id "$ORACLE" \
--source "$SOURCE" \
--network "$NETWORK" \
-- set_prices_simple \
--caller "$(stellar keys address "$SOURCE")" \
--prices "[{\"token\":\"$TOKEN\",\"min\":$MIN_PRICE,\"max\":$MAX_PRICE}]"

ok "price submitted token=$TOKEN network=$NETWORK"
set -e

# submit_prices.sh
Expand Down
Loading