afp-product-builder
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, collateralAsset0xDEfAaC81a079533Bf2fb004c613cc2870cF0A5b5. - Mainnet: oracleAddress
0x06CaDDDf6CC08048596aE051c8ce644725219C73, collateralAsset0xAE2C6c29F6403fDf5A31e74CC8bFd1D75a3CcB8d.
- Bakerloo: oracleAddress
- 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:
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
- Use
Product.validateorProduct.validate_jsonto enforce schemas. - Ensure business rules and Forecastathon constraints pass.
- Use
Product.dump_jsonfor canonical JSON output.
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
extendedMetadataCID 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