Marketplace Contract
Description
Section titled “Description”Example source from examples/marketplace/contract.algo.ts.
Prerequisites
Section titled “Prerequisites”LocalNet running (algokit localnet start)
Run This Example
Section titled “Run This Example”From the repository’s examples directory:
cd examples
npx tsx marketplace/contract.algo.ts
import type { Asset, gtxn, uint64 } from '@algorandfoundation/algorand-typescript'
import { arc4, assert, BoxMap, clone, Global, itxn, op, readonly, Txn } from '@algorandfoundation/algorand-typescript'
export class ListingKey extends arc4.Struct<{
owner: arc4.Address
asset: arc4.Uint64
nonce: arc4.Uint64
}> {}
export class ListingValue extends arc4.Struct<{
deposited: arc4.Uint64
unitaryPrice: arc4.Uint64
bidder: arc4.Address
bid: arc4.Uint64
bidUnitaryPrice: arc4.Uint64
}> {}
export default class DigitalMarketplace extends arc4.Contract {
listings = BoxMap<ListingKey, ListingValue>({ keyPrefix: 'listings' })
listingsBoxMbr(): uint64 {
return (
2_500 +
// fmt: off
// Key length
(8 +
32 +
8 +
8 +
// Value length
8 +
8 +
32 +
8 +
8) *
// fmt: on
400
)
}
quantityPrice(quantity: uint64, price: uint64, assetDecimals: uint64): uint64 {
const [amountNotScaledHigh, amountNotScaledLow] = op.mulw(price, quantity)
const [scalingFactorHigh, scalingFactorLow] = op.expw(10, assetDecimals)
const [_quotientHigh, amountToBePaid, _remainderHigh, _remainderLow] = op.divmodw(
amountNotScaledHigh,
amountNotScaledLow,
scalingFactorHigh,
scalingFactorLow,
)
assert(_quotientHigh === 0)
return amountToBePaid
}
@readonly
getListingsMbr(): uint64 {
return this.listingsBoxMbr()
}
@arc4.abimethod()
allowAsset(mbrPay: gtxn.PaymentTxn, asset: Asset) {
assert(!Global.currentApplicationAddress.isOptedIn(asset))
assert(mbrPay.receiver === Global.currentApplicationAddress)
assert(mbrPay.amount === Global.assetOptInMinBalance)
itxn
.assetTransfer({
xferAsset: asset,
assetReceiver: Global.currentApplicationAddress,
assetAmount: 0,
})
.submit()
}
@arc4.abimethod()
firstDeposit(mbrPay: gtxn.PaymentTxn, xfer: gtxn.AssetTransferTxn, unitaryPrice: arc4.Uint64, nonce: arc4.Uint64) {
assert(mbrPay.sender === Txn.sender)
assert(mbrPay.receiver === Global.currentApplicationAddress)
assert(mbrPay.amount === this.listingsBoxMbr())
const key = new ListingKey({
owner: new arc4.Address(Txn.sender),
asset: new arc4.Uint64(xfer.xferAsset.id),
nonce: nonce,
})
assert(!this.listings(key).exists)
assert(xfer.sender === Txn.sender)
assert(xfer.assetReceiver === Global.currentApplicationAddress)
assert(xfer.assetAmount > 0)
this.listings(key).value = new ListingValue({
deposited: new arc4.Uint64(xfer.assetAmount),
unitaryPrice: unitaryPrice,
bidder: new arc4.Address(),
bid: new arc4.Uint64(),
bidUnitaryPrice: new arc4.Uint64(),
})
}
@arc4.abimethod()
deposit(xfer: gtxn.AssetTransferTxn, nonce: arc4.Uint64) {
const key = new ListingKey({
owner: new arc4.Address(Txn.sender),
asset: new arc4.Uint64(xfer.xferAsset.id),
nonce: nonce,
})
assert(xfer.sender === Txn.sender)
assert(xfer.assetReceiver === Global.currentApplicationAddress)
assert(xfer.assetAmount > 0)
const existing = clone(this.listings(key).value)
this.listings(key).value = new ListingValue({
bid: existing.bid,
bidUnitaryPrice: existing.bidUnitaryPrice,
bidder: existing.bidder,
unitaryPrice: existing.unitaryPrice,
deposited: new arc4.Uint64(existing.deposited.asUint64() + xfer.assetAmount),
})
}
@arc4.abimethod()
setPrice(asset: Asset, nonce: arc4.Uint64, unitaryPrice: arc4.Uint64) {
const key = new ListingKey({
owner: new arc4.Address(Txn.sender),
asset: new arc4.Uint64(asset.id),
nonce: nonce,
})
const existing = clone(this.listings(key).value)
this.listings(key).value = new ListingValue({
bid: existing.bid,
bidUnitaryPrice: existing.bidUnitaryPrice,
bidder: existing.bidder,
deposited: existing.deposited,
unitaryPrice: unitaryPrice,
})
}
@arc4.abimethod()
buy(owner: arc4.Address, asset: Asset, nonce: arc4.Uint64, buyPay: gtxn.PaymentTxn, quantity: uint64) {
const key = new ListingKey({
owner: owner,
asset: new arc4.Uint64(asset.id),
nonce: nonce,
})
const listing = clone(this.listings(key).value)
const amountToBePaid = this.quantityPrice(quantity, listing.unitaryPrice.asUint64(), asset.decimals)
assert(buyPay.sender === Txn.sender)
assert(buyPay.receiver.bytes === owner.bytes)
assert(buyPay.amount === amountToBePaid)
this.listings(key).value = new ListingValue({
bid: listing.bid,
bidUnitaryPrice: listing.bidUnitaryPrice,
bidder: listing.bidder,
unitaryPrice: listing.unitaryPrice,
deposited: new arc4.Uint64(listing.deposited.asUint64() - quantity),
})
itxn
.assetTransfer({
xferAsset: asset,
assetReceiver: Txn.sender,
assetAmount: quantity,
})
.submit()
}
@arc4.abimethod()
withdraw(asset: Asset, nonce: arc4.Uint64) {
const key = new ListingKey({
owner: new arc4.Address(Txn.sender),
asset: new arc4.Uint64(asset.id),
nonce: nonce,
})
const listing = clone(this.listings(key).value)
if (listing.bidder !== new arc4.Address()) {
const currentBidDeposit = this.quantityPrice(listing.bid.asUint64(), listing.bidUnitaryPrice.asUint64(), asset.decimals)
itxn.payment({ receiver: listing.bidder.native, amount: currentBidDeposit }).submit()
}
this.listings(key).delete()
itxn.payment({ receiver: Txn.sender, amount: this.listingsBoxMbr() }).submit()
itxn
.assetTransfer({
xferAsset: asset,
assetReceiver: Txn.sender,
assetAmount: listing.deposited.asUint64(),
})
.submit()
}
@arc4.abimethod()
bid(owner: arc4.Address, asset: Asset, nonce: arc4.Uint64, bidPay: gtxn.PaymentTxn, quantity: arc4.Uint64, unitaryPrice: arc4.Uint64) {
const key = new ListingKey({ owner, asset: new arc4.Uint64(asset.id), nonce })
const listing = clone(this.listings(key).value)
if (listing.bidder !== new arc4.Address()) {
assert(unitaryPrice.asUint64() > listing.bidUnitaryPrice.asUint64())
const currentBidAmount = this.quantityPrice(listing.bid.asUint64(), listing.bidUnitaryPrice.asUint64(), asset.decimals)
itxn.payment({ receiver: listing.bidder.native, amount: currentBidAmount }).submit()
}
const amountToBeBid = this.quantityPrice(quantity.asUint64(), unitaryPrice.asUint64(), asset.decimals)
assert(bidPay.sender === Txn.sender)
assert(bidPay.receiver === Global.currentApplicationAddress)
assert(bidPay.amount === amountToBeBid)
this.listings(key).value = new ListingValue({
deposited: listing.deposited,
unitaryPrice: listing.unitaryPrice,
bidder: new arc4.Address(Txn.sender),
bid: quantity,
bidUnitaryPrice: unitaryPrice,
})
}
@arc4.abimethod()
acceptBid(asset: Asset, nonce: arc4.Uint64) {
const key = new ListingKey({ owner: new arc4.Address(Txn.sender), asset: new arc4.Uint64(asset.id), nonce })
const listing = clone(this.listings(key).value)
assert(listing.bidder !== new arc4.Address())
const minQuantity = listing.deposited.asUint64() < listing.bid.asUint64() ? listing.deposited.asUint64() : listing.bid.asUint64()
const bestBidAmount = this.quantityPrice(minQuantity, listing.bidUnitaryPrice.asUint64(), asset.decimals)
itxn.payment({ receiver: Txn.sender, amount: bestBidAmount }).submit()
itxn
.assetTransfer({
xferAsset: asset,
assetReceiver: listing.bidder.native,
assetAmount: minQuantity,
})
.submit()
this.listings(key).value = new ListingValue({
bidder: listing.bidder,
bidUnitaryPrice: listing.bidUnitaryPrice,
unitaryPrice: listing.unitaryPrice,
deposited: new arc4.Uint64(listing.deposited.asUint64() - minQuantity),
bid: new arc4.Uint64(listing.bid.asUint64() - minQuantity),
})
}
}
Other examples
Section titled “Other examples”- ARC4 Simple Voting Contract
- Auction
- Calculator Contract
- Hello World Contract
- Hello World ABI Contract
- Htlc Logicsig Signature
- Local Storage Contract
- Marketplace Contract
- Precompiled Precompiled Apps
- Precompiled Precompiled Factory
- Precompiled Precompiled Typed
- Proof Of Attendance Contract
- Scratch Storage Contract
- Simple Voting
- Tealscript Example
- Tealscript Teal Script Base
- Voting Contract
- ZK Whitelist