Current section
Files
Jump to
Current section
Files
lib/sutra/cardano/blueprint/common.ex
defmodule Sutra.Cardano.Blueprint.Common do
@moduledoc """
Common CIP-57 Blueprint schemas for standard Cardano types.
This module provides predefined schemas for Cardano standard library types,
allowing consistent encoding/decoding of data structures like OutputReference,
Address, Credential, etc.
## Usage
These schemas can be used with `Blueprint.encode/2` and `Blueprint.decode/2`:
alias Sutra.Cardano.Blueprint
alias Sutra.Cardano.Blueprint.Common
# Encode an output reference
oref = %{
constructor: "OutputReference",
fields: %{
"transaction_id" => <<...>>,
"output_index" => 0
}
}
{:ok, encoded} = Blueprint.encode(oref, Common.output_reference())
# Decode plutus data
{:ok, decoded} = Blueprint.decode(plutus_data, Common.output_reference())
## Available Schemas
The following standard Cardano types are available:
- `bytes/0` - Raw bytes (ByteArray)
- `integer/0` - Integer
- `policy_id/0` - Policy ID (28 bytes)
- `asset_name/0` - Asset name (bytes)
- `transaction_id/0` - Transaction ID (32 bytes)
- `output_reference/0` - Output Reference (tx_id, index)
- `credential/0` - Credential (VerificationKey or Script)
- `stake_credential/0` - Optional stake credential
- `address/0` - Full Cardano address
- `value/0` - Lovelace value (integer)
- `datum/0` - Datum (none, hash, or inline)
- `output/0` - Transaction output
## Integration with defdata/defenum
These schemas can be referenced from modules using `defdata` and `defenum` macros
by storing the schema as a module attribute and using `Blueprint.encode/2` in `to_plutus/1`.
"""
alias Sutra.Cardano.Address
# ============================================================================
# Primitive Types
# ============================================================================
@doc "Schema for raw bytes (ByteArray)"
def bytes do
%{"dataType" => "bytes", "title" => "ByteArray"}
end
@doc "Schema for integer"
def integer do
%{"dataType" => "integer", "title" => "Int"}
end
# ============================================================================
# Asset Types
# ============================================================================
@doc "Schema for Policy ID (28-byte hash)"
def policy_id do
%{"dataType" => "bytes", "title" => "PolicyId"}
end
@doc "Schema for Asset Name"
def asset_name do
%{"dataType" => "bytes", "title" => "AssetName"}
end
# ============================================================================
# Transaction Types
# ============================================================================
@doc "Schema for Transaction ID (32-byte hash)"
def transaction_id do
%{"dataType" => "bytes", "title" => "TransactionId"}
end
@doc "Schema for Output Reference (transaction_id, output_index)"
def output_reference do
%{
"title" => "OutputReference",
"dataType" => "constructor",
"index" => 0,
"fields" => [
%{"title" => "transaction_id", "dataType" => "bytes"},
%{"title" => "output_index", "dataType" => "integer"}
]
}
end
# ============================================================================
# Credential Types
# ============================================================================
@doc "Schema for Credential (VerificationKey or Script)"
def credential do
%{
"title" => "Credential",
"anyOf" => [
%{
"title" => "VerificationKey",
"dataType" => "constructor",
"index" => 0,
"fields" => [
%{"title" => "hash", "dataType" => "bytes"}
]
},
%{
"title" => "Script",
"dataType" => "constructor",
"index" => 1,
"fields" => [
%{"title" => "hash", "dataType" => "bytes"}
]
}
]
}
end
@doc "Schema for optional Stake Credential"
def stake_credential do
%{
"title" => "Option$StakeCredential",
"anyOf" => [
%{
"title" => "None",
"dataType" => "constructor",
"index" => 1,
"fields" => []
},
%{
"title" => "Some",
"dataType" => "constructor",
"index" => 0,
"fields" => [
%{
"title" => "value",
"anyOf" => [
%{
"title" => "Inline",
"dataType" => "constructor",
"index" => 0,
"fields" => [credential()]
},
%{
"title" => "Pointer",
"dataType" => "constructor",
"index" => 1,
"fields" => [
%{"title" => "slot", "dataType" => "integer"},
%{"title" => "tx_index", "dataType" => "integer"},
%{"title" => "cert_index", "dataType" => "integer"}
]
}
]
}
]
}
]
}
end
# ============================================================================
# Address Types
# ============================================================================
@doc "Schema for Cardano Address"
def address do
%{
"title" => "Address",
"dataType" => "constructor",
"index" => 0,
"fields" => [
%{"title" => "payment_credential"} |> Map.merge(credential()),
%{"title" => "stake_credential"} |> Map.merge(stake_credential())
]
}
end
# ============================================================================
# Value Types
# ============================================================================
@doc "Schema for Lovelace value (simple integer)"
def lovelace do
%{"dataType" => "integer", "title" => "Lovelace"}
end
@doc "Schema for asset quantity in a multi-asset value"
def asset_quantity do
%{
"title" => "AssetQuantity",
"dataType" => "list",
"items" => [
policy_id(),
%{
"dataType" => "map",
"keys" => asset_name(),
"values" => integer()
}
]
}
end
# ============================================================================
# Datum Types
# ============================================================================
@doc "Schema for Datum (NoDatum, DatumHash, or InlineDatum)"
def datum do
%{
"title" => "Datum",
"anyOf" => [
%{
"title" => "NoDatum",
"dataType" => "constructor",
"index" => 0,
"fields" => []
},
%{
"title" => "DatumHash",
"dataType" => "constructor",
"index" => 1,
"fields" => [
%{"title" => "hash", "dataType" => "bytes"}
]
},
%{
"title" => "InlineDatum",
"dataType" => "constructor",
"index" => 2,
"fields" => [
%{"title" => "Data", "description" => "Any Plutus data."}
]
}
]
}
end
# ============================================================================
# Script Reference Types
# ============================================================================
@doc "Schema for optional Script Reference"
def script_reference do
%{
"title" => "Option$ScriptRef",
"anyOf" => [
%{
"title" => "None",
"dataType" => "constructor",
"index" => 1,
"fields" => []
},
%{
"title" => "Some",
"dataType" => "constructor",
"index" => 0,
"fields" => [
%{"title" => "script", "dataType" => "bytes"}
]
}
]
}
end
# ============================================================================
# Output Types
# ============================================================================
@doc "Schema for Transaction Output"
def output do
%{
"title" => "Output",
"dataType" => "constructor",
"index" => 0,
"fields" => [
Map.put(address(), "title", "address"),
%{"title" => "value", "dataType" => "integer"},
Map.put(datum(), "title", "datum"),
Map.put(script_reference(), "title", "reference_script")
]
}
end
# ============================================================================
# Input Types
# ============================================================================
@doc "Schema for Transaction Input"
def input do
%{
"title" => "Input",
"dataType" => "constructor",
"index" => 0,
"fields" => [
Map.put(output_reference(), "title", "output_reference"),
Map.put(output(), "title", "output")
]
}
end
# ============================================================================
# Time Types
# ============================================================================
@doc "Schema for POSIX time (milliseconds since epoch)"
def posix_time do
%{"dataType" => "integer", "title" => "POSIXTime"}
end
# ============================================================================
# Helper to get all available schemas
# ============================================================================
@doc "Returns a map of all available schema names to their schemas"
def all do
%{
bytes: bytes(),
integer: integer(),
policy_id: policy_id(),
asset_name: asset_name(),
transaction_id: transaction_id(),
output_reference: output_reference(),
credential: credential(),
stake_credential: stake_credential(),
address: Address,
lovelace: lovelace(),
asset_quantity: asset_quantity(),
datum: datum(),
script_reference: script_reference(),
output: output(),
input: input(),
posix_time: posix_time()
}
end
@doc "Get a schema by name (atom)"
def get(name) when is_atom(name) do
Map.get(all(), name)
end
end