diff --git a/skills/afp-product-builder/SKILL.md b/skills/afp-product-builder/SKILL.md new file mode 100644 index 0000000..0d03a63 --- /dev/null +++ b/skills/afp-product-builder/SKILL.md @@ -0,0 +1,179 @@ +--- +name: afp-product-builder +description: Build valid AFP products by composing on-chain PredictionProductV1, extended metadata DAG, and API-backed resolution rules. Use when creating or validating AFP product JSON for Forecastathon, selecting public data sources, or preparing IPFS pinning and registration. +compatibility: Requires Python 3.11+, afp-sdk, internet access, and an IPFS pinning endpoint (Filebase or IPFS node). +metadata: + author: autonity + version: "0.1.0" +--- + +# AFP Product Builder + +Use this skill to produce a complete, valid product specification for the +Forecastathon competition, including extended metadata that can be pinned to +IPFS as a DAG-CBOR encoded DAG. + +## Inputs to gather first + +- Network: bakerloo or mainnet. +- Builder address (Forecastathon-registered). +- Product type: time series (scalar) or event (binary or ternary). +- Data source: public API URL and fields to extract. +- Start time and earliest FSP submission time (UTC ISO 8601). +- Min and max price band (unleveraged products only). +- Symbol and description. + +## Forecastathon constraints (must enforce) + +- Required contract addresses: + - Bakerloo: oracleAddress `0x72EeD9f7286292f119089F56e3068a3A931FCD49`, + collateralAsset `0xDEfAaC81a079533Bf2fb004c613cc2870cF0A5b5`. + - Mainnet: oracleAddress `0x06CaDDDf6CC08048596aE051c8ce644725219C73`, + collateralAsset `0xAE2C6c29F6403fDf5A31e74CC8bFd1D75a3CcB8d`. +- Builder must be a registered Forecastathon participant. +- startTime must be at least two full working days after PR submission. +- Products are unleveraged and must define minPrice and maxPrice. +- Extended metadata must be pinned on IPFS and conform to standard schemas. +- API source must be valid and freely accessible. + +## Build the on-chain product (PredictionProductV1) + +Use camelCase field names inside `product`: + +- product.base.metadata.builder (EVM checksum address) +- product.base.metadata.symbol (A-Z0-9, 1-16 chars) +- product.base.metadata.description +- product.base.oracleSpec.oracleAddress (required per network) +- product.base.oracleSpec.fsvDecimals, fspAlpha, fspBeta, fsvCalldata +- product.base.collateralAsset (required per network) +- product.base.startTime (UTC ISO 8601, Z) +- product.base.pointValue +- product.base.priceDecimals +- product.base.extendedMetadata (CID, filled after pinning) +- product.expirySpec.earliestFSPSubmissionTime (UTC ISO 8601, Z) +- product.expirySpec.tradeoutInterval +- product.minPrice < product.maxPrice + +## Build extended metadata (snake_case) + +Extended metadata fields are `outcome_space`, `outcome_point`, `oracle_config`, +and `oracle_fallback`. These are pinned on IPFS and referenced by CID. + +### Outcome space and point + +Time series (scalar): + +- outcome_space: OutcomeSpaceTimeSeries + - fsp_type: "scalar" + - description + - base_case.condition and base_case.fsp_resolution + - edge_cases (optional) + - units, source_name, source_uri + - frequency: daily, weekly, fortnightly, semimonthly, monthly, quarterly, yearly + - history_api_spec (optional for backing data) +- outcome_point: OutcomePointTimeSeries + - fsp_type: "scalar" + - observation.reference_date and observation.release_date (YYYY-MM-DD) + +Event (binary or ternary): + +- outcome_space: OutcomeSpace + - fsp_type: "binary" or "ternary" + - description + - base_case.condition and base_case.fsp_resolution + - edge_cases (optional) +- outcome_point: OutcomePointEvent + - fsp_type: "binary" or "ternary" + - outcome (specific candidate or yes/no outcome) + +Template variables in conditions must resolve against outcome_point fields. +Example: `{outcome}` for events, `{observation.release_date}` for time series. + +### Oracle config + +Manual resolution: + +- oracle_config: OracleConfig + - description + - project_url (optional) + +API-backed resolution: + +- oracle_config: OracleConfigPrototype1 + - description + - project_url (optional) + - evaluation_api_spec: ApiSpecJSONPath + - spec_variant: "product-fsv" + - url, date_path, value_path + - date_format_type: iso_8601 | unix_timestamp | custom + - timestamp_scale if unix timestamps are ms + - auth_param_location: none | query | header + - auth_param_name / auth_param_prefix if needed + +For time series, `history_api_spec` (OutcomeSpaceTimeSeries) should use +`spec_variant: "underlying-history"` when you include it. + +### Oracle fallback + +- oracle_fallback.fallback_time must be at least 7 days after + earliestFSPSubmissionTime. +- oracle_fallback.fallback_fsp must be within [minPrice, maxPrice]. + +## API spec guidance + +- Use JSONPath strings that select parallel arrays for dates and values. +- Prefer APIs that return ISO 8601 dates or UNIX timestamps to avoid custom + parsing. +- Verify the URL is reachable (HEAD request succeeds). +- Prefer no-auth or free-access endpoints for Forecastathon. +- See references/api-sources.md for vetted examples. + +## IPFS pinning (DAG-CBOR required) + +Extended metadata must be pinned as a DAG-CBOR DAG, not a single JSON blob. +The AFP SDK handles this automatically by encoding and pinning each component +and the root DAG: + +```python +import afp + +app = afp.AFP( + rpc_url=AUTONITY_RPC_URL, + authenticator=afp.PrivateKeyAuthenticator(PRIVATE_KEY), + ipfs_api_url=IPFS_API_URL, + ipfs_api_key=IPFS_API_KEY, +) +product_api = app.Product() + +spec = product_api.validate(product_dict) +pinned = product_api.pin(spec) # builds and pins DAG-CBOR +``` + +If you build the DAG manually, ensure you store it as dag-cbor (e.g., +`ipfs dag put` default store codec) and include schema CIDs for each component. + +## Validation and output + +1) Use `Product.validate` or `Product.validate_json` to enforce schemas. +2) Ensure business rules and Forecastathon constraints pass. +3) Use `Product.dump_json` for canonical JSON output. + +```python +import afp + +product_api = afp.AFP(...).Product() +spec = product_api.validate(product_dict) +pinned = product_api.pin(spec) +product_json = product_api.dump_json(pinned) +``` + +## Deliverables + +- Complete product JSON with `extendedMetadata` CID filled in. +- Clear explanation of outcome_space resolution and data source. +- Validation checklist confirmation (see references/validation-checklist.md). + +## References + +- Public API options and JSONPath examples: references/api-sources.md +- Validation checklist and constraints: references/validation-checklist.md diff --git a/skills/afp-product-builder/references/api-sources.md b/skills/afp-product-builder/references/api-sources.md new file mode 100644 index 0000000..2a70f30 --- /dev/null +++ b/skills/afp-product-builder/references/api-sources.md @@ -0,0 +1,103 @@ +# Public API sources (examples) + +Each example below includes a sample endpoint and a matching ApiSpecJSONPath +snippet. Verify the endpoint response and terms of use before finalizing. + +## Open-Meteo (weather, no key for non-commercial) + +Sample endpoint: + +`https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&daily=temperature_2m_max&timezone=UTC` + +ApiSpecJSONPath: + +```json +{ + "standard": "JSONPath", + "spec_variant": "underlying-history", + "url": "https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&daily=temperature_2m_max&timezone=UTC", + "date_path": "$.daily.time[*]", + "value_path": "$.daily.temperature_2m_max[*]", + "date_format_type": "iso_8601", + "timezone": "UTC" +} +``` + +Notes: +- Returns ISO 8601 dates in `daily.time`. +- Ensure daily arrays align (same length). + +## Climate Monitor (climate, public) + +Sample endpoint: + +`https://climatemonitor.info/api/public/v1/co2/monthly_gl` + +ApiSpecJSONPath: + +```json +{ + "standard": "JSONPath", + "spec_variant": "underlying-history", + "url": "https://climatemonitor.info/api/public/v1/co2/monthly_gl", + "date_path": "$.data.readings[*].label", + "value_path": "$.data.readings[*].value", + "date_format_type": "iso_8601", + "timezone": "UTC" +} +``` + +Notes: +- Response is JSend; data lives under `data.readings`. +- Labels are ISO 8601 dates in the example payloads. + +## World Bank Indicators API (economic, public) + +Sample endpoint: + +`https://api.worldbank.org/v2/country/USA/indicator/FP.CPI.TOTL?format=json&per_page=1000` + +ApiSpecJSONPath: + +```json +{ + "standard": "JSONPath", + "spec_variant": "underlying-history", + "url": "https://api.worldbank.org/v2/country/USA/indicator/FP.CPI.TOTL?format=json&per_page=1000", + "date_path": "$[1][*].date", + "value_path": "$[1][*].value", + "date_format_type": "custom", + "date_format_custom": "%Y", + "timezone": "UTC", + "max_pages": 1 +} +``` + +Notes: +- The `date` field is a year string (YYYY). Use a custom format. +- Set `per_page` high to avoid pagination and keep `max_pages` small. + +## CoinGecko Market Chart (crypto, public with rate limits) + +Sample endpoint: + +`https://api.coingecko.com/api/v3/coins/bitcoin/market_chart?vs_currency=usd&days=90` + +ApiSpecJSONPath: + +```json +{ + "standard": "JSONPath", + "spec_variant": "underlying-history", + "url": "https://api.coingecko.com/api/v3/coins/bitcoin/market_chart?vs_currency=usd&days=90", + "date_path": "$.prices[*][0]", + "value_path": "$.prices[*][1]", + "date_format_type": "unix_timestamp", + "timestamp_scale": 1000, + "timezone": "UTC" +} +``` + +Notes: +- Timestamps are milliseconds since epoch. +- Check current API terms and rate limits; free access may be limited. diff --git a/skills/afp-product-builder/references/validation-checklist.md b/skills/afp-product-builder/references/validation-checklist.md new file mode 100644 index 0000000..df492a3 --- /dev/null +++ b/skills/afp-product-builder/references/validation-checklist.md @@ -0,0 +1,46 @@ +# Validation checklist + +Use this list to preflight a product before pinning or PR submission. + +## Forecastathon rules + +- Builder address is registered for Forecastathon. +- startTime is at least two full working days after PR submission. +- Correct oracleAddress and collateralAsset for the selected network. +- Only one environment per PR (bakerloo or mainnet). +- Product is unleveraged with explicit minPrice and maxPrice. +- Extended metadata is pinned on IPFS as DAG-CBOR. +- API source is reachable and freely accessible. + +## On-chain product (PredictionProductV1) + +- minPrice < maxPrice. +- startTime < earliestFSPSubmissionTime. +- builder and contract addresses are valid checksummed EVM addresses. +- symbol matches required pattern if time series. + +## Extended metadata + +- outcome_space.fsp_type matches outcome_point.fsp_type. +- outcome_space.base_case and edge_cases have non-empty condition and + fsp_resolution strings. +- Template variables in conditions resolve against outcome_point. +- oracle_fallback.fallback_time >= earliestFSPSubmissionTime + 7 days. +- oracle_fallback.fallback_fsp within [minPrice, maxPrice]. +- OracleConfig and ApiSpec fields are present when used. + +## Time series symbol format + +Symbols encode the release date and frequency. Month codes: +F=Jan, G=Feb, H=Mar, J=Apr, K=May, M=Jun, N=Jul, Q=Aug, U=Sep, V=Oct, +X=Nov, Z=Dec. + +Patterns (prefix is 1-8 uppercase letters): + +- daily: PREFIXDDMYY (DD day, M month code, YY year) +- weekly/fortnightly: PREFIXWW WYY (ISO week number) +- semimonthly: PREFIX1MYY or PREFIX2MYY (1 = days 1-15, 2 = days 16+) +- monthly/quarterly: PREFIXMYY +- yearly: PREFIXYYYY + +Release date in outcome_point must match the symbol date encoding.