How schemas limit smart contracts

The format of a schema

A complete schema consists of three parts: prefix list, class, predicate

Prefix list

A prefix is used to describe a resource effectively.

Without a prefix, one would need to use a complete URI to describe a resource. For example, to describe the entity "Person_001":

<http://relationlabs.ai/entity/Person_001>

In contrast, after a prefix is defined, we can use a shorter version to describe the same entity:

:Person_001

Full list of prefixes:

PREFIX : <http://relationlabs.ai/entity/>
PREFIX p: <http://relationlabs.ai/property/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

Please note that rdf: , xsd: and rdfs: are prefixes provided by W3C. Furthermore, : and p: are prefixes defined by the Relation Protocol to represent entities and properties.

Class

A class is an abstraction of an entity while an entity is an instance of a class. For example, to define an entity "Soul", we can first define a class "Soul". The rdf describes as follows:

:Soul a rdfs:Class . 

Predicate

A predicate (also called a property) is used to describe the relationship between subject resources and object resources. A predicate also needs to describe the object it takes. For example:

p:following a rdf:Property ;
    rdfs:domain :Soul ;
    rdfs:range :Soul .

This segment defined:

  • the predicate p:following is a Property.

  • p:following is a property of :Soul.

  • the values of p:following is :Soul.

The example of a schema

Take the needs of a Name Service as an example:

  • A user can register a domain name, namely to own it.

  • Users can set resolve records for the domain names they own, namely to resolve them to addresses they own.

To implement the above scenario, we need an entity "Soul" to represent a user's address, an entity "Domain" to represent a domain name, and predicates to describe the relationship between the two. There are two states that need to be described: "hold" means an address is holding a domain name; "resolved" means an address is resolved by a domain name.

Thus, we need the following classes and predicates in the schema:

  • Class

    • Soul

    • Name

  • Predicate

    • hold

    • resolved

    • profileURI

Based on the classes和predicates defined above, now we can form a complete schema:

PREFIX : <http://relationlabs.ai/entity/>
PREFIX p: <http://relationlabs.ai/property/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

###################
# class
###################
:Soul a rdfs:Class ;
    rdfs:label "Soul" ;
    rdfs:comment "A soul." .

:Name a rdfs:Class ;
    rdfs:label "Name" ;
    rdfs:comment "A name." .

###################
# predicate
###################
p:hold a rdf:Property ;
    rdfs:label "hold" ;
    rdfs:comment "A Soul to own a name." ;
    rdfs:domain :Soul ;
    rdfs:range :Name .

p:resolved a rdf:Property ;
    rdfs:label "resolved" ;
    rdfs:comment "The resolved name of a soul." ;
    rdfs:domain :Soul ;
    rdfs:range :Name .
    
p:profileURI a rdf:Property ;
    rdfs:label "profileURI" ;
    rdfs:comment "The profileURI of a soul." ;
    rdfs:domain :Soul ;
    rdfs:range xsd:string .

An RDF example of a address holding a domain name:

:Soul_0x0000000000000000000000000000000000000011 p:hold :Name_example.

An RDF example of a address being resolved by a domain name:

:Soul_0000000000000000000000000000000000000011 p:resolved :Name_resolve.

The limits to a contract by a schema

A schema defines the RDF data format to serve as the vocabulary of a contract's data model(Figure 4-2). That is why it needs to be consistent with the contract's data model so that the RDF data generated by the contract can be parsed correctly by the Graph Indexer.

For that, we need to pass the parameters schemaURI, className and Predicate to the contract during its initialization stage in order to turn the content defined by a schema into a contract-readable code structure. With a schemaURI, the content of a schema can be accessed. The className refers to the classes defined in the schema. The Predicate consists of the predicates and range defined by the schema.

The following is an example for passing parameters during a contract's initialization stage:

const name = 'Relation Name Service V1';
const symbol = 'SBT';
const baseURI = 'https://api.example.com/v1/';
const schemaURI = 'ar://PsqAxxDYdxfk4iYa4UpPam5vm8XaEyKco3rzYwZJ_4E';
const className = ["Domain"];
const predicates = [["hold", 3], ["resolved", 3], ["profileURI", 1]];
await semanticSBT.initialize(
        owner.address,
        name,
        symbol,
        baseURI,
        schemaURI,
        className,
        predicates);

We can see that the schemaURI is the transaction hash on Arweave. We can access the complete content of the schema directly via a Arweave gateway. The className and predicates should be consistent with the classes and predicates defined in the schema.

Last updated