The MarketProvider
follows the same principles as the TzombiesProvider
. This page will cover only the specifics for this provider. If you would like to view the full code, it is available on the .
Provider base
The props:
Copy interface ListingParameters {
tokenId: number
amount: number
price: number
expiry: Date
}
interface Listing {
saleId: number
seller: Address
parameters: ListingParameters
}
interface MarketProviderContextProps {
market?: Market
isApproved: boolean
listings: Listing[]
approve: () => Promise<void>
revoke: () => Promise<void>
list_for_sale: (params: ListingParameters) => Promise<CallResult | undefined>
remove_listing: (listing: Listing) => Promise<CallResult | undefined>
buy: (listing: Listing, amount: number) => Promise<CallResult | undefined>
fetchMarketplaceApproval: () => Promise<void>
fetchListings: () => Promise<void>
}
market
is the marketplace contract instance
isApproved
and sales is the provider's state
approve
, revoke
control the marketplace operator status for the connected wallet
list_for_sale
, remove_listing and buy
are the contract entry points
fetchMarketplaceApproval
and fetchListings update the state
As for the FA2 contract provider, we initialize it at load:
Copy useEffect(() => {
if (!Tezos) {
return
}
setMarket(new Market(process.env.NEXT_PUBLIC_MARKET_ADDRESS))
}, [Tezos])
Fetch approval
This one is a simple contract read.
Copy const fetchMarketplaceApproval = useCallback(async () => {
if (!account || !fa2) {
setIsApproved(false)
return
}
const arg = new operator_for_all_key(
new Address(process.env.NEXT_PUBLIC_MARKET_ADDRESS!),
new Address(account.address)
)
const approved = await fa2.get_operator_for_all_value(arg)
setIsApproved(!!approved)
}, [account, fa2])
Fetch sales
We have a counter: next_order_id.
We'll use it to iterate over the orders.
Copy const fetchListings = useCallback(async () => {
if (!market || !fa2 || !fa2.address) return
const nOrders = await market.get_next_order_id()
const listings: Listing[] = []
const inventories: Map<Address, UserInventory> = new Map()
for (let i = 1; i < nOrders.to_number(); i++) {
const order = await market.get_order_value(new Nat(i))
if (!order) continue
// filter out expired orders
if (new Date(order.expiry) < new Date()) continue
// check how many tokens the seller still has
if (!inventories.has(order.seller)) {
inventories.set(order.seller, await fetchFa2Balance(order.seller))
}
const amount = inventories
.get(order.seller)
?.get(order.token_id.to_number())
const qty = Math.min(order.amount.to_number(), amount ?? 0)
if (qty < 1) continue
listings.push({
saleId: i,
seller: order.seller,
parameters: {
tokenId: order.token_id.to_number(),
amount: qty,
price: order.price.to_big_number().toNumber(),
expiry: order.expiry,
},
})
}
setListings(listings)
}, [fa2, fetchFa2Balance, market])
Fetch on load
As usual, we fetch the state on component load:
Copy useEffect(() => {
fetchListings()
}, [fetchListings])
useEffect(() => {
fetchMarketplaceApproval()
}, [fetchMarketplaceApproval])
Entry points
Each entrypoint is called using the generated bindings.
approve / revoke
Copy const approve = useCallback(async () => {
if (!fa2 || !market) return
const arg = new add_for_all(new Address(market.address!))
await fa2.update_operators_for_all([arg], {})
}, [fa2, market])
const revoke = useCallback(async () => {
if (!fa2 || !market) return
const arg = new remove_for_all(new Address(market.address!))
await fa2.update_operators_for_all([arg], {})
}, [fa2, market])
list_for_sale / remove_listing / buy
Copy const list_for_sale = useCallback<
(params: ListingParameters) => Promise<CallResult | undefined>
>(
async ({ tokenId, amount, price, expiry }: ListingParameters) => {
if (!market || !fa2 || !fa2.address) return
return await market.list_for_sale(
new Address(fa2.address),
new Nat(tokenId),
new Nat(amount),
new Tez(price),
expiry,
{}
)
},
[market, fa2]
)
const remove_listing = useCallback(
async (listing: Listing) => {
if (!market || !fa2 || !fa2.address) return
return await market.remove_listing(new Nat(listing.saleId), {})
},
[market, fa2]
)
const buy = useCallback(
async (listing: Listing, amount: number) => {
if (!market || !fa2 || !fa2.address) return
return await market.buy(new Nat(listing.saleId), new Nat(amount), {
amount: new Tez(listing.parameters.price * amount, 'mutez'),
})
},
[market, fa2]
)
Wrap up
Memoise the value and pass it to the children:
Copy const value = useMemo(
() => ({
market,
isApproved,
listings,
approve,
revoke,
list_for_sale,
remove_listing,
buy,
fetchMarketplaceApproval,
fetchListings,
}),
[
market,
isApproved,
listings,
approve,
revoke,
list_for_sale,
remove_listing,
buy,
fetchMarketplaceApproval,
fetchListings,
]
)
return (
<MarketProviderContext.Provider value={value}>
{children}
</MarketProviderContext.Provider>
)
Export
Export the component.
Copy export { MarketProvider, useMarketProviderContext }
export type { Listing }
Include the provider in the App hierarchy, with access to the wallet and Tzombies contexts.