Skip to content

Commit db142e4

Browse files
committed
[iain feat] release 1.0.0-rc0 of nft-hooks
0 parents  commit db142e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+15556
-0
lines changed

.circleci/config.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
env_defaults: &env_defaults
2+
working_directory: ~
3+
docker:
4+
- image: circleci/node:14.15.1
5+
6+
version: 2.1
7+
jobs:
8+
prepare:
9+
<<: *env_defaults
10+
steps:
11+
- checkout
12+
13+
# Download and cache dependencies
14+
- restore_cache:
15+
keys:
16+
- v1.0-dependencies-{{ checksum "yarn.lock" }}
17+
# fallback to using the latest cache if no exact match is found
18+
- v1.0-dependencies-
19+
20+
- node/install-packages:
21+
pkg-manager: yarn
22+
23+
- run: yarn install
24+
25+
- run: yarn add jest-junit -W
26+
27+
- save_cache:
28+
paths:
29+
- node_modules
30+
key: v1.0-dependencies-{{ checksum "yarn.lock" }}
31+
32+
- persist_to_workspace:
33+
root: .
34+
paths:
35+
- node_modules
36+
37+
test:
38+
<<: *env_defaults
39+
steps:
40+
- checkout
41+
- attach_workspace:
42+
at: .
43+
- run:
44+
command: yarn run jest --ci --testResultsProcessor="jest-junit" --coverage --coverageDirectory ~/coverage
45+
name: Run Tests
46+
environment:
47+
JEST_JUNIT_OUTPUT_DIR: "~/reports/nft-hooks"
48+
- store_test_results: {path: "~/reports/nft-hooks"}
49+
- store_artifacts: {path: "~/coverage"}
50+
build:
51+
<<: *env_defaults
52+
steps:
53+
- checkout
54+
- attach_workspace:
55+
at: .
56+
- run:
57+
command: yarn run build
58+
name: Build Typescript Package
59+
60+
orbs:
61+
node: circleci/[email protected]
62+
workflows:
63+
test:
64+
jobs:
65+
- prepare:
66+
pre-steps:
67+
- run:
68+
command: echo "registry=https://registry.npmjs.org/" > ~/.npmrc && echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> ~/.npmrc
69+
- build:
70+
requires:
71+
- prepare
72+
- test:
73+
requires:
74+
- build
75+
76+

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
dist/
2+
graph-schemas/*.graphql
3+
yarn-error.log
4+
node_modules/
5+
.DS_Store
6+
.husky

.husky/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
_

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
9+
## [1.0.0-rc0] - 2021-05-07
10+
11+
### RC0 Public Release
12+
13+
* Initial public RC release
14+
* Added base 3 hooks to interact with individual NFTs
15+
* Supports Zora auction contracts and zNFTs for the time being
16+
* Normalizes and fetches currency information from Uniswap
17+
* Uses batching and caching for repeatable data requests
18+
* Loads NFT Metadata and Content for each record

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Zora Labs, Inc
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## @zoralabs/nft-hooks
2+
3+
Simple React hooks to load Zora NFT data. Includes on-chain data, NFT metadata, and tools for fetching NFT content if needed.
4+
5+
Put together, these power implementations of the zNFT protocol on any website.
6+
7+
This library consists of a data fetch class and associated React hooks to load NFT data is an easy, efficient manner. The API both batches and caches requests, meaning you can use the hooks across a page without needing to worry about significant performance penalties.
8+
9+
10+
Install:
11+
```
12+
yarn add @zoralabs/nft-hooks
13+
```
14+
15+
Then you can import and use the hooks in your react application:
16+
17+
```
18+
import {useNFT, useNFTMetadata} from "@zoralabs/nft-hooks";
19+
20+
function MyNFT() {
21+
const {data} = useNFT("20");
22+
const {metadata} = useNFTMetadata(data && data.metadataURI);
23+
24+
return (
25+
<div>
26+
<h3>{metadata.title}</h3>
27+
<p>{metadata.description}</p>
28+
<p>Owned by: {data.owner.id}</p>
29+
</div>
30+
);
31+
}
32+
```
33+
34+
### All hooks:
35+
36+
| Hook | Usage |
37+
| -- | -- |
38+
| [useNFT](docs/useNFT.md) | Fetches on-chain NFT data |
39+
| [useNFTMetadata](docs/useNFTMetadata.md) | Fetches NFT metadata from a URL |
40+
| [useNFTContent](docs/useNFTContent.md) | Fetches text content from server for rendering from content URL |
41+
42+
43+
### Development:
44+
45+
1. `git clone https://github.com/ourzora/nft-hooks`
46+
2. `cd nft-hooks`
47+
3. `npm i -g yarn` if you don't have yarn installed
48+
4. `yarn`
49+
5. `yarn run test` test your code
50+
51+
Pull requests and tickets are accepted for issues and improvements
52+
to this library.

codegen.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"generates": {
3+
"./src/graph-queries/zora-types.d.ts": {
4+
"schema": "./graph-schemas/zora.graphql",
5+
"documents": "./src/graph-queries/zora.ts",
6+
"plugins": ["typescript", "typescript-operations"]
7+
},
8+
"./src/graph-queries/uniswap-types.d.ts": {
9+
"schema": "./graph-schemas/uniswap.graphql",
10+
"documents": "./src/graph-queries/uniswap.ts",
11+
"plugins": ["typescript", "typescript-operations"]
12+
}
13+
}
14+
}

docs/useNFT.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
This hook fetches data found on the blockchain from the given zNFT. The only argument for the hook is the NFT id. To fetch data for zNFTs on other networks, use the `NFTFetchConfiguration` wrapper component to set the correct network for loading the NFT data.
2+
The main types are within the result `data.nft` object. This object contains the on-chain NFT data itself. The pricing information can be found in `data.pricing` which corresponds to data on-chain for Zora's perpetual zNFT auctions along with the reserve auction functionality.
3+
4+
```ts
5+
import {useNFT} from "@zoralabs/nft-hooks";
6+
7+
type NFTDataType = {
8+
nft: {
9+
id: string, // ID of zNFT
10+
owner: {id: string}, // Address of owner
11+
creator: {id: string}, // Address of creator
12+
metadataURI: string, // URI of metadata for zNFT
13+
metadataHash: string, // sha256 hash for metadata for zNFT
14+
contentURI: string, // URI of content described by metadata
15+
contentHash: string, // sha256 hash of content
16+
},
17+
18+
pricing: {
19+
perpetual: {
20+
bids: { // empty array if no bids
21+
id: string;
22+
createdAtTimestamp: string;
23+
bidder: { id: string };
24+
pricing: PricingInfo;
25+
}[],
26+
ask?: {
27+
id: string,
28+
createdAtTimestamp: string;
29+
pricing: PricingInfo;
30+
};
31+
},
32+
reserve?: {
33+
auctionCurrency: CurrencyInformation;
34+
id: string;
35+
tokenId: string;
36+
status: "Pending" | "Active" | "Canceled" | "Finished";
37+
firstBidTime: string;
38+
duration: string;
39+
expectedEndTimestamp: string;
40+
createdAtTimestamp: string;
41+
finalizedAtTimestamp: string;
42+
},
43+
};
44+
45+
// Current/ongoing auction information synthesized from pricing data
46+
auction: {
47+
highestBid: {
48+
pricing: PricingInfo;
49+
placedBy: string;
50+
placedAt: string;
51+
};
52+
current: {
53+
auctionType: "reserve" | "perpetual";
54+
endingAt?: string;
55+
likelyHasEnded: boolean; // If an auction ended but has not been finalized this will be true.
56+
reserveMet: boolean;
57+
reservePrice?: PricingInfo;
58+
};
59+
};
60+
};
61+
62+
export type PricingInfo = {
63+
currency: CurrencyInformation;
64+
amount: string; // Amount as raw bignumber
65+
prettyAmount: string; // Amount as a normalized BigDecimal value
66+
computedValue?: PricingInfoValue; // Computed value in USD and ETH (available from Uniswap API call)
67+
};
68+
69+
type CurrencyInformation = {
70+
id: string, // Blockchain address of currency. If ETH currency, will be 0x0000000000000000000000000000000000000000
71+
name: string, // Name of currency
72+
symbol: string, // Symbol of currency
73+
decimals: number, // Decimals for currency
74+
};
75+
76+
77+
type useNFT = (id: string) => {
78+
loading: boolean;
79+
error?: string; // undefined if no error, string if error
80+
chainNFT?: NFTDataType; // undefined in error or loading states
81+
}
82+
83+
// Example with usage:
84+
const {chainNFT, loading} = useNFT("2");
85+
```
86+
87+
Alternatively, the same information can be fetched using the base MediaFetchAgentfor server-side or non-react use:
88+
89+
```ts
90+
import {MediaFetchAgent, Networks} from "@zoralabs/nft-hooks";
91+
92+
// Be careful making multiple instances of the fetch agent
93+
// Each instance contains a different request cache.
94+
const fetchAgent = new MediaFetchAgent(Networks.MAINNET);
95+
96+
// Get result from the server
97+
const result = await fetchAgent.loadNFTData("2");
98+
// result type is NFTDataType
99+
```

docs/useNFTContent.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
This hook makes a request to fetch metadata from IPFS retrieved from the zNFT `metadataURI`.
2+
3+
Most IPFS servers allow remote JSON fetches, including all Zora NFTs.
4+
There is a chance this request could fail when the server does not allow cross-origin requests.
5+
Requests are set with a 15 second timeout to allow showing the user an error message instead of an
6+
indefinite loader.
7+
8+
Hook result type:
9+
```ts
10+
// This is a union type meaning one or another in typescript.
11+
// Use the (media.type) to determine which type was returned from the hook.
12+
type MediaContentType =
13+
| {
14+
uri: string; // URI of content to render, used for media
15+
type: 'uri', // Shows that no content was downloaded, should only render
16+
mimeType: string // mime type string from metadata for rendering
17+
}
18+
| {
19+
text: string; // Text of result
20+
type: 'text', // Shows that text content to render was downloaded
21+
mimeType: string // mime type string from metadata for rendering
22+
};
23+
24+
type useNFTContentType = {
25+
loading: boolean; // If loading from the server
26+
error?: string; // Error returned from network request error or timeout
27+
content?: MediaContentType; // MediaConetentType shown above;
28+
}
29+
```
30+
31+
To use the hook, simply pass in the `contentURI` from the zNFT on-chain data and `mimeType` from the NFT Metadata.
32+
33+
If you do not have access to `mimeType` from the metadata or do not wish to retrieve the metadata, the `mimeType` can be omitted with a small performance impact.
34+
35+
Content returned from this hook is _not_ cached, each time the hook is used the content is fetched.
36+
37+
```ts
38+
import {useNFTContent} from "@zoralabs/nft-hooks";
39+
40+
const MyMediaData = ({uri: string, mimeType: string}) => {
41+
const {error, loading, metadata} = useNFTContent(uri, mimeType);
42+
43+
if (error) {
44+
return <div>Error fetching content</div>;
45+
}
46+
47+
if (loading) {
48+
return <div>loading...</div>;
49+
}
50+
51+
if (content.type === 'text') {
52+
return <div>{content.text}</div>;
53+
}
54+
if (content.mimeType.startsWith("audio")) {
55+
return <audio src={content.uri} />;
56+
}
57+
if (content.mimeType.startsWith("video")) {
58+
return <video src={content.uri} />;
59+
}
60+
if (content.mimeType.startsWith("image")) {
61+
return <img src={content.uri} />;
62+
}
63+
return <div>unknown: {content.mimeType}</div>;
64+
}
65+
```
66+
67+
68+
Alternatively, the same information can be fetched using the base `MediaFetchAgent` for server-side or non-react use:
69+
```ts
70+
import {MediaFetchAgent} from "@zoralabs/nft-hooks";
71+
72+
// Get result from the server
73+
const result = await MediaFetchAgent.fetchContent("https://ipfs.io/ipfs/METADATA_HASH", "application/json");
74+
// result type is MediaContentType
75+
```

0 commit comments

Comments
 (0)