# Offchain NFTs
## Mission
NFTs are great, but they are a privacy nightmare.
Lets build a system that allows us to own, sell and buy NFTs, but without every transaction being public and observable.
## Problems of todays NFTs
* They are owned by wallets, which expose personal, financial information
* Every buy and sell operation is readable by everyone
* For any given NFT, everybody can lookup the current owner
* For any given owner address, all NFTs belonging to that owner can be looked up
## What are Offchain NFTs?
### Overview
Offchain-NFTs are here to solve the problems mentioned above. They provide a way of proving ownership over a piece of art or data without revealing any personal data to uninvolved parties.
In essence they are self-issued KILT credentials stating the ownership over an embedded NFT. They also include a list of all previous owners and the proofs, that the previous owner transfered the NFT to the new owner. This way the NFT is fully self-contained and can be stored off-chain within the wallet of the user.
### CType
KILT credentials of a specific kind are always formatted according to a specific CType which is just a JSON schema.
The Offchain-NFT CType looks like this:
```json
{
"title": "Offchain-NFT",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "This is a jsonschema for off-chain NFTs",
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "The id of the NFT"
},
"creator": { "allOf": [
{ "$ref": "#/$defs/DID" },
{ "description": "The creator of the NFT" }
]},
"owner": { "allOf": [
{ "$ref": "#/$defs/DID" },
{ "description": "The current owner of the NFT" }
]},
"history": {
"type": "array",
"items": { "$ref": "#/$defs/HistoryEntry" },
"description": "The owner history of the NFT"
},
"data": { "allOf": [
{ "$ref": "#/$defs/NFTData" },
{ "description": "The data of the NFT" }
]}
},
"$defs": {
"DID": {
"type": "string",
"description": "A DID"
},
"Signature": {
"type": "string",
"description": "A hex encoded DID signature"
},
"NFTData": {
"type": "object",
"description": "free-form object but should contain something useful"
},
"Timestamp": {
"type": "string",
"format": "date-time",
"description": "A timestamp in ISO 8601 format"
},
"HistoryEntry": {
"type": "object",
"additionalProperties": false,
"properties": {
"owner": { "allOf": [
{ "$ref": "#/$defs/DID" },
{ "description": "The owner of the NFT" }
]},
"timestamp": { "allOf": [
{ "$ref": "#/$defs/Timestamp" },
{ "description": "The timestamp of the ownership change" }
]},
"signature": { "allOf": [
{ "$ref": "#/$defs/Signature" },
{ "description": "The old owners signature over (old_root_hash || new_owner || timestamp)" }
]}
}
}
}
}
```
### Examples
```yaml
# A freshly minted NFT
---
id: 629eb8bd-ca81-4861-865c-08d6adf8d5cf
creator: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
owner: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
history: []
data:
collection: 0aff6865bed3a66b-GLMRPNKS
symbol: GLMR_PUNKS
transferable: 1
sn: '00000123'
metadata:
externalUri: ipfs://QmWXS22cTJUysGW7JhdxtNzCT8CwWfHkHBspqd2tF9vMHe/1248.png
dna: db32e67133feb5524d0a0b80d9719554eebe0aff
# ...
# Same NFT, but transfered to another owner
---
id: 629eb8bd-ca81-4861-865c-08d6adf8d5cf
creator: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
owner: did:kilt:4wwcvyyv8qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWaB
history:
- owner: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
timestamp: '2020-01-01T00:00:00Z'
signature: '0xdeadbeaf'
data:
collection: 0aff6865bed3a66b-GLMRPNKS
symbol: GLMR_PUNKS
transferable: 1
sn: '00000123'
metadata:
externalUri: ipfs://QmWXS22cTJUysGW7JhdxtNzCT8CwWfHkHBspqd2tF9vMHe/1248.png
dna: db32e67133feb5524d0a0b80d9719554eebe0aff
# ...
```
## Lifecycle
This section will describe how different actions with offchain NFTs can be implemented.
### Minting
Minting can be done by anyone and is completely permissionless. Those are the steps:
* create the NFT metadata containing for example a collection id a serial number and a link to an image.
* the image can also be embedded directly (in base64 encoded format for example, or raw SVG) since we are **not** going to store this metadata on chain.
* you can follow RMRK v1/v2 NFT structure or anything else
* create an attestation on the KILT blockchain with the CType from above to initially proof that you created the NFT
* write the attestation to chain and store the resulting credential
### Transfer
To transfer a NFT to another DID the current owner has to add a signature to the `history` property of the NFT proving that the current owner is willing to do the transfer. The new NFT can now be written to chain by the new owner and the old owner revokes the old attestation (but doesn't delete it).
### Example
Creator of the NFT: `did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ`
Receiver of the NFT: `did:kilt:4wwcvyyv8qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWaB`
Original NFT:
```yaml
---
id: 629eb8bd-ca81-4861-865c-08d6adf8d5cf
creator: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
owner: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
history: []
data:
collection: 0aff6865bed3a66b-GLMRPNKS
symbol: GLMR_PUNKS
transferable: 1
sn: '00000123'
metadata:
externalUri: ipfs://QmWXS22cTJUysGW7JhdxtNzCT8CwWfHkHBspqd2tF9vMHe/1248.png
```
Root hash of original NFT
```
0x012345678901234567890123456789
```
Now the creator decides to send the NFT to the receiver DID
Sell-off-Signature therefore looks like this:
```
did_sign($OLD_ROOT_HASH + $RECEIVER_DID + $TIMESTAMP) = 0xdeadbeaf
```
The creator now constructs the new NFT with a new owner and itself added to the history:
```yaml
---
id: 629eb8bd-ca81-4861-865c-08d6adf8d5cf
creator: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
owner: did:kilt:4wwcvyyv8qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWaB
history:
- owner: did:kilt:4mWbsvYn7qmPkXJYXhXCvLQpQ9kJLxNnKq2GEiGJwfWQQ
timestamp: '2020-01-01T00:00:00Z'
signature: '0xdeadbeaf'
data:
collection: 0aff6865bed3a66b-GLMRPNKS
symbol: GLMR_PUNKS
transferable: 1
sn: '00000123'
metadata:
externalUri: ipfs://QmWXS22cTJUysGW7JhdxtNzCT8CwWfHkHBspqd2tF9vMHe/1248.png
```
Root hash of new NFT
```
0x098765432109876543210987654321
```
The new NFT is now passed to the receiver which will then write the new root hash to the chain.
Finally the old owner revokes the original attestation
### Verify
At every point in time the owner can verify if the NFT is authentic by verifying the sell-off-signature chain.
Lets say the NFT was sold N times.
The current owner can easily compute the root hash of the NFT after N-1 sales, by removing the last entry in the history and setting the owner property to the value found in the history entry.
If the signature over the old root hash, the new owner and the timestamp from the history entry is valid and the old root hash points to a revoked attestation, we know that the N'th sale was valid.
This process can now be continued until N == 0 at which point we only need to make sure that the initial root hash of the NFT was written to chain by the creator or another trusted entity and is revoked.
## Market
Since the NFTs doesn't live on-chain creating a decentralized market is non-trivial.
Additionally when the owner wants to sell a NFT, the receiving of the compensation, the writing of the new attestation and the revoking of the old attestation should happen atomically.
Therefore we will need an escrow service where users can create an offer to buy a NFT and send the required tokens into the escrow service.
If the owner decides to accept the offer, he sends a presigned TX which revokes the old attestation and the correct sell-off-signature to the escrow service.
The escrow service now constructs and submits a `batchAll` call to revoke the old attestation, write the new attestation and send the compensation to the seller.
The new a attestation can be written either directly by the escrow service or through an previously setup proxy which allows the escrow service to write the new attestation on behalf of the buyer.
Such a market can either be run by a trusted entity or eventually as a fat contract on phala network because with that we could submit extrinsics to the kilt chain and also confidentially create buy and sell offers.
## Wrapped NFTs
Off-chain NFTs can be used to wrap any other NFT to enhance their privacy properties.
To do so users can send their NFTs to the escrow service (either trusted entity or a smart/fat contract) which will then mint a new off-chain NFT with the user as owner.
The wrapped NFT can now be traded in a privacy preserving way.
At some point the current owner of the wrapped NFT can unwrap the NFT at the escrow service to get the original transferred back to their wallet.