Redstone
Redstone
Introduction
In this tutorial, we'll construct a stable price NFT marketplace on the zkSync network. Utilizing Hardhat for smart contract compilation and testing, ethers.js for contract interaction, and React for the frontend, we'll create a seamless NFT trading platform. A crucial component of our marketplace is the integration of RedStone Oracles to obtain reliable price data.
RedStone is a data ecosystem that delivers frequently updated, reliable and diverse data for your dApps and smart contracts.
- Data providers can avoid the requirement of continuous on-chain data delivery
- Allow end users to self-deliver signed Oracle data on-chain
- Use the decentralized Streamr network to deliver signed oracle data to the end users
- Use token incentives to motivate data providers to maintain data integrity and uninterrupted service
Prerequisites
- Knowledge Base: Familiarity with smart contracts, Hardhat,
ethers.js
, and React. - Environment Setup: Have Node.js installed.
- Tooling: This guide utilizes
hardhat
andethers.js
.
Step 1 — Setting Up the Development Environment
1. Clone the Repository:
git clone https://github.com/zkSync-Community-Hub/tutorials
2. Navigate to the Project Directory:
cd tutorials/tutorials/zkSync-RedStone-stable-price-marketplace-tutorial/code
3. Install Dependencies:
yarn install
4. Setup Local zkSync Node:
Set up a local zkSync node in a Dockerized setup following the instructions here.
Step 2 — Understanding the Contract Structure
ExampleNFT.sol
ExampleNFT
is a simplistic ERC721 contract with sequential token ID assignment, extending ERC721Enumerable
from OpenZeppelin for enhanced enumeration capability.
function mint() external {
_mint(msg.sender, nextTokenId);
nextTokenId++;
}
Marketplace.sol
Marketplace
facilitates NFT trading with basic order placement, cancellation, and buying functionality, adhering to the EIP-721 standard.
function postSellOrder(address nftContractAddress, uint256 tokenId, uint256 price) external {}
function cancelOrder(uint256 orderId) external {}
function getAllOrders() public view returns (SellOrder[] memory) {}
function getPrice(uint256 orderId) public view returns (uint256) {}
function buy(uint256 orderId) external payable {}
StableMarketplace.sol
StableMarketplace
enhances Marketplace
with stable pricing, integrating RedStone Oracles for precise price data.
Step 3 — Modifying the StableMarketplace Contract
The StableMarketplace.sol
requires extension from MainDemoConsumerBase
to interact with RedStone Oracles. Modify _getPriceFromOrder
to utilize getOracleNumericValueFromTxMsg
for price data retrieval.
contract StableMarketplace is Marketplace, MainDemoConsumerBase {
function _getPriceFromOrder(SellOrder memory order) internal view override returns (uint256) {
uint256 ethPrice = getOracleNumericValueFromTxMsg(bytes32("ETH"));
return (order.price / ethPrice) * (10 ** 8);
}
}
Step 4 — Adjusting dApp TypeScript Code
In blockchain.ts
, implement the buy
function, wrapping the marketplace contract with RedStone's framework to facilitate buy
operations with precise pricing.
import { WrapperBuilder } from "@redstone-finance/evm-connector";
async function buy(orderId: string) {
const marketplace = await getContractInstance("marketplace");
// Wrapping marketplace contract instance.
// It enables fetching data from redstone data pool
// for each contract function call
try {
const wrappedMarketplaceContract = WrapperBuilder.wrap(marketplace).usingDataService({
dataServiceId: "redstone-main-demo",
uniqueSignersCount: 1,
dataFeeds: ["ETH"],
});
// Checking expected amount
const expectedEthAmount = await wrappedMarketplaceContract.getPrice(orderId);
// Sending buy tx
const buyTx = await wrappedMarketplaceContract.buy(orderId, {
value: expectedEthAmount.mul(101).div(100), // a buffer for price movements
});
await buyTx.wait();
return buyTx;
} catch {
const errText = "Error happened while buying the NFT";
alert(errText);
}
}
Step 5 — Testing and Launching Locally
Ensure the RedStone Oracle integration is correct by running tests, compiling contracts, deploying them locally, and launching the React app.
yarn test
yarn compile
# Rename and update .env file
mv .env.example .env
# Update WALLET_PRIVATE_KEY in .env
echo "WALLET_PRIVATE_KEY=0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110" >> .env
yarn deploy:local
yarn app:start
Visit http://localhost:3000 to interact with your local NFT stable price marketplace.
Step 5.1 — Local Marketplace Interaction
Import Local Wallets to MetaMask
- In MetaMask, select the account dropdown, then
Import account
. - Input the private keys for
User 1
andUser 2
respectively:User 1
:0xac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3
User 2
:0xd293c684d884d56f8d6abd64fc76757d3664904e309a0645baf8522ab6366d9e
- Click
Import
.
Explore App
Navigate to the app at http://localhost:3000. Initially, you'll see a near-empty interface with a + Mint new NFT
link.
Mint NFTs
- Click
+ Mint new NFT
to create new NFTs. - Post-minting, view your NFT in the left column.
Post Sell Orders
- Click
SELL
on any NFT, enter the USD value, and confirm the two prompted transactions for NFT transfer approval and marketplace order creation.
Buy NFTs
- Switch MetaMask accounts to buy an NFT.
- Optionally, open browser's developer tools on the network tab to observe network requests, including the two requests for ETH price data and crypto signatures before the buy transaction.
Conclusion
You've successfully built and launched a stable price NFT marketplace on zkSync, integrating RedStone Oracles for reliable price data. This marketplace provides a platform for NFT trading with stable pricing, showcasing the potential of combining zkSync's scalability with RedStone's accurate price oracles.