nft-did-resolver
Version:
DID Resolver for the NFT method
209 lines (152 loc) • 5.91 kB
Markdown
# NFT DID Resolver
> NFT is a DID method that uses the Ceramic network to resolve DID documents for NFTs
> See [CIP-94](https://github.com/ceramicnetwork/CIP/blob/main/CIPs/CIP-94/CIP-94.md)
## Getting started
This implementation is still a prototype. Contributions are welcome!
To use a package, you would need to provide three subgraph endpoints for every network you are going to use:
one for blocks, one for ERC721 tokens, another for ERC1155 tokens. You would also need to provide a `skew` that
is a time (in milliseconds) within which a latest block is considered valid. Usually it is a typical block time.
### Installation
```
$ npm install nft-did-resolver
```
### Usage
```typescript
import { getResolver } from 'nft-did-resolver'
import type { NftResolverConfig } from 'nft-did-resolver'
import { Resolver } from 'did-resolver'
import Ceramic from '@ceramicnetwork/http-client'
const ceramic = new Ceramic() // connects to localhost:7007 by default
const config: NftResolverConfig = {
ceramic,
chains: {
'eip155:1': {
blocks: 'https://api.thegraph.com/subgraphs/name/yyong1010/ethereumblocks',
skew: 15000,
assets: {
erc721: 'https://api.thegraph.com/subgraphs/name/sunguru98/mainnet-erc721-subgraph',
erc1155: 'https://api.thegraph.com/subgraphs/name/sunguru98/mainnet-erc1155-subgraph',
},
},
'eip155:4': {
blocks: 'https://api.thegraph.com/subgraphs/name/mul53/rinkeby-blocks',
skew: 15000,
assets: {
erc721: 'https://api.thegraph.com/subgraphs/name/sunguru98/erc721-rinkeby-subgraph',
erc1155: 'https://api.thegraph.com/subgraphs/name/sunguru98/erc1155-rinkeby-subgraph',
},
},
},
}
// getResolver will return an object with a key/value pair of { 'nft': resolver }
// where resolver is a function used by the generic did resolver.
const nftResolver = getResolver(config)
const didResolver = Resolver(nftResolver)
const erc721result = await didResolver.resolve(
'did:nft:eip155:1_erc721:0xb300a43751601bd54ffee7de35929537b28e1488_2'
)
const erc1155result = await didResolver.resolve(
'did:nft:eip155:1_erc1155:0x06eb48572a2ef9a3b230d69ca731330793b65bdc_1'
)
console.log(erc721result, erc1155result)
```
`chains` field in config has [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md) chain identifiers as keys.
Each such `chain` is expected to contain endpoints to ERC721 and/or ERC1155 subgraphs under `assets` field.
Both ERC721 and ERC1155 are supported. Feel free to specify either one or both.
The resolver supports the following networks by default:
- Ethereum mainnet (`eip155:1`),
- Ethereum Rinkeby (`eip155:4`),
- Polygon (formerly Matic) (`eip155:137`).
If you use one of those, you do not have to provide `chains` field.
## Testing
```
$ npm test
```
## Custom Subgraphs
You may specify custom subgraph URLs in the configuration object as shown above in [usage](#usage).
**Note**: custom subgraphs must conform to the below schemas at a _minimum_ for assets to be resolved properly.
**Note**: At the moment, only ERC721 and ERC1155 asset namespaces are supported. However, CAIP2 chains beside ETH,
for instance xDAI, with support for those namespaces _are_ supported, as long as the subgraph schema is the same.
### ERC721:
```gql
type Token {
id: ID!
contract: TokenContract!
owner: Owner!
...
}
type TokenContract {
id: ID!
tokens: [Token!]!
...
}
type Owner {
id: ID!
tokens: [Token!]!
...
}
```
### ERC1155:
```gql
type Account {
id: ID!
balances: [Balance!]!
...
}
type TokenRegistry {
id: ID!
tokens: [Token!]!
...
}
type Token {
id: ID!
registry: TokenRegistry!
identifier: BigInt!
balances: [Balance!]!
...
}
type Balance {
id: ID!
token: Token!
account: Account!
...
}
```
For more information on writing schemas for GraphProtocol, check out [their documentation](https://thegraph.com/docs/define-a-subgraph#defining-entities).
## DID Specs
The token DIDs are prefixed with `did:nft:`, and the latter half is a modified CAIP format.
**ERC721** ([CAIP-22](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/CAIP-22.md))
DID: `did:nft:{chainNamespace}:{chainReference}_erc721:{contractAddress}_{tokenId}`
CAIP-22: `{chainNamespace}:{chainReference}/erc721:{contractAddress}/{tokenId}`
**ERC1155** ([CAIP-29](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/CAIP-29.md))
DID: `did:nft:{chainNamespace}:{chainReference}_erc1155:{contractAddress}_{tokenId}`
CAIP-29: `{chainNamespace}:{chainReference}/erc1155:{contractAddress}/{tokenId}`
### Conversions
**DID->CAIP**
```
const caip = did.substr(8).replace(/_/g, '/')
```
**CAIP->DID**
```
const did = `did:nft:${caip.replace(/\//g, '_')
```
There are helpers that help you with the conversion:
```typescript
import { caipToDid, didToCaip, createNftDidUrl } from 'nft-did-resolver'
import { AssetId } from 'caip'
// CAIP -> DID URL
const didUrl = createNftDidUrl({
chainId: 'eip155:1',
namespace: 'erc721',
contract: '0x1234567891234567891234567891234596351156'
tokenId: '1',
})
// If you use `caip` library in your app, consider using sister `caipToDid` function to convert `AssetId` to NFT DID URL.
// DID URL -> CAIP
const assetId1 = didToCaip(didUrl) // eip155:1/erc721:0x1234567891234567891234567891234596351156/1
const assetId2 = didToCaip(didUrlWithTimestamp) // eip155:1/erc721:0x1234567891234567891234567891234596351156/1
```
## Contributing
We are happy to accept small and large contributions. Make sure to check out the [Ceramic specifications](https://github.com/ceramicnetwork/specs) for details of how the protocol works.
## License
Apache-2.0 OR MIT