Skip to content

Commit

Permalink
Merge pull request dapperlabs#16 from dapperlabs/qvvg/buy-stuff
Browse files Browse the repository at this point in the history
buy stuff
  • Loading branch information
orodio authored Jan 12, 2021
2 parents 0aa01c3 + d6937ac commit d34ca14
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 13 deletions.
63 changes: 63 additions & 0 deletions src/flow/buy-market-item.tx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as fcl from "@onflow/fcl"
import * as t from "@onflow/types"
import {tx} from "./util/tx"
import {invariant} from "@onflow/util-invariant"

const CODE = fcl.cdc`
import FungibleToken from 0xFungibleToken
import NonFungibleToken from 0xNonFungibleToken
import Kibble from 0xKibble
import KittyItems from 0xKittyItems
import KittyItemsMarket from 0xKittyItemsMarket
transaction(saleItemID: UInt64, marketCollectionAddress: Address) {
let paymentVault: @FungibleToken.Vault
let kittyItemsCollection: &KittyItems.Collection{NonFungibleToken.Receiver}
let marketCollection: &KittyItemsMarket.Collection{KittyItemsMarket.CollectionPublic}
prepare(acct: AuthAccount) {
self.marketCollection = getAccount(marketCollectionAddress)
.getCapability<&KittyItemsMarket.Collection{KittyItemsMarket.CollectionPublic}>(
KittyItemsMarket.CollectionPublicPath
)!
.borrow()
?? panic("Could not borrow market collection from market address")
let price = self.marketCollection.borrowSaleItem(saleItemID: saleItemID).salePrice
let mainKibbleVault = acct.borrow<&Kibble.Vault>(from: Kibble.VaultStoragePath)
?? panic("Cannot borrow Kibble vault from acct storage")
self.paymentVault <- mainKibbleVault.withdraw(amount: price)
self.kittyItemsCollection = acct.borrow<&KittyItems.Collection{NonFungibleToken.Receiver}>(
from: KittyItems.CollectionStoragePath
) ?? panic("Cannot borrow KittyItems collection receiver from acct")
}
execute {
self.marketCollection.purchase(
saleItemID: saleItemID,
buyerCollection: self.kittyItemsCollection,
buyerPayment: <- self.paymentVault
)
}
}
`

// prettier-ignore
export function buyMarketItem({itemId, ownerAddress}, opts = {}) {
invariant(itemId != null, "buyMarketItem({itemId, ownerAddress}) -- itemId required")
invariant(ownerAddress != null, "buyMarketItem({itemId, ownerAddress}) -- ownerAddress required")

return tx([
fcl.transaction(CODE),
fcl.args([
fcl.arg(Number(itemId), t.UInt64),
fcl.arg(String(ownerAddress), t.Address),
]),
fcl.proposer(fcl.authz),
fcl.payer(fcl.authz),
fcl.authorizations([fcl.authz]),
fcl.limit(1000),
], opts)
}
33 changes: 33 additions & 0 deletions src/flow/cancel-market-listing.tx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as fcl from "@onflow/fcl"
import * as t from "@onflow/types"
import {tx} from "./util/tx"
import {invariant} from "@onflow/util-invariant"

const CODE = fcl.cdc`
import KittyItemsMarket from 0xKittyItemsMarket
transaction(saleItemID: UInt64) {
prepare(account: AuthAccount) {
let listing <- account
.borrow<&KittyItemsMarket.Collection>(from: KittyItemsMarket.CollectionStoragePath)!
.remove(saleItemID: saleItemID)
destroy listing
}
}
`

// prettier-ignore
export function cancelMarketListing({ itemId }, opts = {}) {
invariant(itemId != null, "cancelMarketListing({itemId}) -- itemId required")

return tx([
fcl.transaction(CODE),
fcl.args([
fcl.arg(Number(itemId), t.UInt64),
]),
fcl.proposer(fcl.authz),
fcl.payer(fcl.authz),
fcl.authorizations([fcl.authz]),
fcl.limit(1000),
], opts)
}
6 changes: 4 additions & 2 deletions src/flow/fetch-account-item.script.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import KittyItems from 0xKittyItems
pub struct Item {
pub let id: UInt64
pub let type: UInt64
pub let owner: Address
init(id: UInt64, type: UInt64) {
init(id: UInt64, type: UInt64, owner: Address) {
self.id = id
self.type = type
self.owner = owner
}
}
Expand All @@ -22,7 +24,7 @@ pub fun fetch(address: Address, id: UInt64): Item? {
if let collection = cap.borrow() {
if let item = collection.borrowKittyItem(id: id) {
return Item(id: id, type: item.typeID)
return Item(id: id, type: item.typeID, owner: address)
} else {
return nil
}
Expand Down
14 changes: 7 additions & 7 deletions src/flow/fetch-account-items.script.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {send, decode, script, args, arg, cdc} from "@onflow/fcl"
import * as fcl from "@onflow/fcl"
import {Address} from "@onflow/types"

const CODE = cdc`
const CODE = fcl.cdc`
import NonFungibleToken from 0xNonFungibleToken
import KittyItems from 0xKittyItems
Expand All @@ -21,10 +21,10 @@ export function fetchAccountItems(address) {
if (address == null) return Promise.resolve([])

// prettier-ignore
return send([
script(CODE),
args([
arg(address, Address)
return fcl.send([
fcl.script(CODE),
fcl.args([
fcl.arg(address, Address)
]),
]).then(decode)
]).then(fcl.decode).then(d => d.sort((a, b) => a - b))
}
119 changes: 119 additions & 0 deletions src/flow/fetch-market-item.script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import * as fcl from "@onflow/fcl"
import * as t from "@onflow/types"
// import {batch} from "./util/batch"

// const CODE = fcl.cdc`
// import KittyItemsMarket from 0xfcceff21d9532b58

// pub struct Item {
// pub let id: UInt64
// pub let isCompleted: Bool
// pub let price: UFix64
// pub let owner: Address

// init(id: UInt64, isCompleted: Bool, price: UFix64, owner: Address) {
// self.id = id
// self.isCompleted = isCompleted
// self.price = price
// self.owner: owner
// }
// }

// pub fun fetch(address: Address, id: UInt64): Item? {
// let cap = getAccount(address)
// .getCapability<&KittyItemsMarket.Collection{KittyItemsMarket.CollectionPublic}>(KittyItemsMarket.CollectionPublicPath)!

// if let collection = cap.borrow() {
// // this currently throws as the collection.borrowSaleItem returns a non-optional resource
// if let item = collection.borrowSaleItem(saleItemID: id) {
// return Item(id: id, isCompleted: item.saleCompleted, price: item.salePrice, owner: address)
// } else {
// return nil
// }
// } else {
// return nil
// }
// }

// pub fun main(keys: [String], addresses: [Address], ids: [UInt64]): {String: Item?} {
// let r: {String: Item?} = {}
// var i = 0
// while i < keys.length {
// let key = keys[i]
// let address = addresses[i]
// let id = ids[i]
// r[key] = fetch(address: address, id: id)
// i = i + i
// }
// return r
// }
// `

// const collate = px => {
// return Object.keys(px).reduce(
// (acc, key) => {
// acc.keys.push(key)
// acc.addresses.push(px[key][0])
// acc.ids.push(px[key][1])
// return acc
// },
// {keys: [], addresses: [], ids: []}
// )
// }

// const {enqueue} = batch("FETCH_MARKET_ITEM", async px => {
// const {keys, addresses, ids} = collate(px)

// // prettier-ignore
// return fcl.send([
// fcl.script(CODE),
// fcl.args([
// fcl.arg(keys, t.Array(t.String)),
// fcl.arg(addresses, t.Array(t.Address)),
// fcl.arg(ids.map(Number), t.Array(t.UInt64)),
// ]),
// ]).then(fcl.decode)
// })

// export async function fetchMarketItem(address, id) {
// if (address == null) return Promise.resolve(null)
// if (id == null) return Promise.resolve(null)
// return enqueue(address, id)
// }

export async function fetchMarketItem(address, id) {
return fcl
.send([
fcl.script`
import KittyItemsMarket from 0xfcceff21d9532b58
pub struct Item {
pub let id: UInt64
pub let isCompleted: Bool
pub let price: UFix64
pub let owner: Address
init(id: UInt64, isCompleted: Bool, price: UFix64, owner: Address) {
self.id = id
self.isCompleted = isCompleted
self.price = price
self.owner = owner
}
}
pub fun main(address: Address, id: UInt64): Item? {
let cap = getAccount(address)
.getCapability<&KittyItemsMarket.Collection{KittyItemsMarket.CollectionPublic}>(KittyItemsMarket.CollectionPublicPath)!
if let collection = cap.borrow() {
let item = collection.borrowSaleItem(saleItemID: id)
return Item(id: id, isCompleted: item.saleCompleted, price: item.salePrice, owner: address)
} else {
return nil
}
}
`,
fcl.args([fcl.arg(address, t.Address), fcl.arg(Number(id), t.UInt64)]),
])
.then(fcl.decode)
}
2 changes: 1 addition & 1 deletion src/flow/fetch-market-items.script.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ export function fetchMarketItems(address) {
fcl.args([
fcl.arg(address, t.Address)
])
]).then(fcl.decode)
]).then(fcl.decode).then(d => d.sort((a, b) => a - b))
}
2 changes: 1 addition & 1 deletion src/global/config.comp.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export function Config() {
.put("0xFungibleToken", "0x9a0766d93b6608b7")
.put("0xNonFungibleToken", "0x631e88ae7f1d7c20")
.put("0xKibble", CONTRACTS)
.put("0xKittyItems", CONTRACTS)
.put("0xKittyItemsMarket", CONTRACTS)
.put("0xKittyItems", CONTRACTS)
}, [])
return null
}
96 changes: 96 additions & 0 deletions src/hooks/use-market-item.hook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import {atomFamily, selectorFamily, useRecoilState} from "recoil"
import {sansPrefix} from "@onflow/fcl"
import {IDLE, PROCESSING} from "../global/constants"
import {useCurrentUser} from "../hooks/use-current-user.hook"
import {useAccountItems} from "../hooks/use-account-items.hook"
import {useMarketItems} from "../hooks/use-market-items.hook"
import {useKibblesBalance} from "../hooks/use-kibbles-balance.hook"
import {fetchMarketItem} from "../flow/fetch-market-item.script"
import {buyMarketItem} from "../flow/buy-market-item.tx"
import {cancelMarketListing} from "../flow/cancel-market-listing.tx"

function expand(key) {
return key.split("|")
}

function comp(address, id) {
return [address, id].join("|")
}

export const $state = atomFamily({
key: "market-item::state",
default: selectorFamily({
key: "market-item::default",
get: key => async () => fetchMarketItem(...expand(key)),
}),
})

export const $status = atomFamily({
key: "market-item::status",
default: IDLE,
})

export function useMarketItem(address, id) {
const [cu] = useCurrentUser()
const ownerItems = useAccountItems(address)
const cuItems = useAccountItems(cu.addr)
const ownerMarket = useMarketItems(address)
const cuMarket = useMarketItems(cu.addr)
const kibble = useKibblesBalance(cu.addr)
const key = comp(address, id)
const [item, setItem] = useRecoilState($state(key))
const [status, setStatus] = useRecoilState($status(key))

const owned = sansPrefix(cu.addr) === sansPrefix(address)

return {
...item,
status,
owned,
async buy() {
await buyMarketItem(
{itemId: id, ownerAddress: address},
{
onStart() {
setStatus(PROCESSING)
},
async onSuccess() {
if (address !== cu.addr) {
ownerItems.refresh()
ownerMarket.refresh()
}
cuItems.refresh()
cuMarket.refresh()
kibble.refresh()
},
async onComplete() {
setStatus(IDLE)
},
}
)
},
async cancelListing() {
await cancelMarketListing(
{itemId: id},
{
onStart() {
setStatus(PROCESSING)
},
async onSuccess() {
cuItems.refresh()
cuMarket.refresh()
kibble.refresh()
},
async onComplete() {
setStatus(IDLE)
},
}
)
},
async refresh() {
setStatus(PROCESSING)
await fetchMarketItem(...expand(key)).then(setItem)
setStatus(IDLE)
},
}
}
2 changes: 2 additions & 0 deletions src/pages/wip/qvvg.page.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export function Page() {
<InitCluster address={user.addr} />
<AccountItemsCluster address={user.addr} />
<MarketItemsCluster address={user.addr} />
<h3>Store</h3>
<MarketItemsCluster address="0xfcceff21d9532b58" />
</Stack>
</Base>
)
Expand Down
Loading

0 comments on commit d34ca14

Please sign in to comment.