Skip to content

JamOnBread/contract

Repository files navigation

Jam on Bread contract

NFT marketplace, which allows you to buy and sell Cardano-based NFTs.

Design

  • Market with Cardano assets (NFTs)
  • Offer participation on the market to 3rd parties
  • Offer to use contract in own market
  • There is a fixed fee of 2.5% that is distributed between all parties, that participated in successful trade
  • Is possible to make a profit from user's treasuries by staking
  • All datums should be compatible with original the contract designed by Vacuumlabs (verified by Cbor serialization)

Fees and royalty calculation

  • 10% to JoB

  • 20% listing marketplace

  • 20% listing affiliate (can be same as a marketplace)

  • 50% to selling marketplace, which can be divided into many records (affiliates)

  • Royalty

The calculation for price 100 ADA, and a typical set of treasuries and 5% royalty:

  • 92.5 ADA to the seller (100 - 2.5 (fees) - 5 (5% royalty) )
  • 0.25 ADA to JoB treasury (100 _ 0.25 _ 0.1)
  • 0.5 ADA to listing marketplace treasury(100 _ 0.025 _ 0.2)
  • 0.5 ADA to listing affiliate treasury (100 _ 0.025 _ 0.2)
  • 0.625 ADA to selling marketplace treasury (100 _ 0.25 _ 0.25)
  • 0.625 ADA to selling affiliate treasury (100 _ 0.25 _ 0.25)
  • 5 ADA to royalty treasury (100 * 0.05)

Major changes

  • There are no more script params
  • It is not necessary to mint values
  • There is an option to produce treasury during a transaction
  • There is an option to consume treasury
  • Logic is divided between Treasury the contract and function contracts (InstantBuy/Offer)

Mint

Simple contract, that checks argument OutputReference is present on inputs. The purpose is only for minting disposable assets (for treasury validation method)

Staking

A simple contract that is validated same as the Withdrawal method, which is passed as an argument. The second argument is just salt for PolicyId It is a stake address that can be withdrawn by the wallet/token holder

Treasury

Common contract where user can collect ADA value during manipulation with a market. There are two purposes:

  • Use the treasury in transaction
  • Withdraw treasury

The contract holds information about the withdrawal method in datum

flowchart LR
   treasuryIn1[Treasury + Stake1]
   treasuryIn2[Treasury + Stake1]
   treasuryIn3[Treasury + Stake2]
   treasuryInMore[...]

   treasuryIn1 & treasuryIn2 & treasuryIn3 & treasuryInMore -->|Withdraw| tx

   tx["Transaction (Tx)"]

   tx --> |ReCreate| treasuryOut1 & treasuryOut2 & treasuryOutMore
   tx --> |Reward| user

   treasuryOut1[Treasury + Stake1]
   treasuryOut2[Treasury + Stake2]
   treasuryOutMore[...]
   user[Wallet]
Loading

Usage in transaction

Typical usage of the treasury is, that the original UTxO is spending during the transaction and a new one is created on output. The diff between output and input is provision.

  • Check, that the count of UTxOs with the same script hash and datum like spending UTxO is equal to one (there is no need to spend more than one)
  • Check, that the count of UTxOs with the same address (payment credential and spending credential) is equal to One (it is not possible to burn it or change stake address)
  • Check, that the output ADA on UTxO is bigger than the input ADA
  • Check, that the output values of ADA are bigger than the defined minimal value
  • Check, that treasury UTxOs do not contain another asset except ADA

Usage during withdrawal

There are two validation mechanisms, depending on the type of treasury:

  • by payment credential

  • by validation token

  • Check, that the validation mechanism is valid

  • Check outputs UTxOs, that only ADA is present and is bigger than a minimal value

  • The number of the same treasury (credential, datum, stake) is present on the input and output

  • The number of output treasury (credential, datum, stake) can be reduced to 5 (another can be spent without creating new ones)

Instant buy

Contract for buying NFTs

There are two purposes:

  • Accept
  • Cancel

The validator has a params:

validator(
  treasury_script_hash: Hash<Blake2b_224, credential.Script>, // Reference to treasury validator
  stake_addresses: List<credential.StakeCredential>,          // List of accepted JoB stake address
  job: types.WithdrawalMethod,                                // JoB treasury, what is present in each transaction
)

Information about price, royalty etc., are stored in datum

Accept

  • Check, that the ADA sent to a user is correct
  • Check, that all provisions are correct
  • Check, that the basis of royalty is not lower than the minimal value
  • Check, that portions defined in the redeemer are correct
  • If the royalty (payment credential) is the same as the seller's address, the royalty is not paid to a treasury, but directly to the seller's address
flowchart LR
   treasuryIn1[Treasury1]
   treasuryIn2[Treasury2]
   treasuryIn3[Treasuries...]
   instantBuy[Contract]
   buyerIn[Buyer Wallet]

   treasuryIn1 & treasuryIn2 & treasuryIn3 -->|Spend| tx
   instantBuy --> |NFT| tx
   buyerIn --> |ADA| tx

   tx["Transaction (Tx)"]

   tx --> |Provision| treasuryOut1 & treasuryOut2 & treasuryOut3
   tx --> |ADA| sellerOut
   tx --> |NFT| buyerOut

   treasuryOut1[Treasury1]
   treasuryOut2[Treasury2]
   treasuryOut3[Treasuries...]
   buyerOut[Buyer Wallet]
   sellerOut[Seller Wallet]
Loading

Cancel

The transaction is signed by stored payment credentials There is no other check for the spending contract

Improvements

  • Store selling Unit to datum and check, that Unit is present in UTxO (easy to parse, but there is a problem with CIP68)

Offer

Is very similar to instant buying instead of a final check to pay assets/ADA to the offerer's address.

Information about price, royalty etc., are stored in datum

flowchart LR
   treasuryIn1[Treasury1]
   treasuryIn2[Treasury2]
   treasuryIn3[Treasuries...]
   offer[Contract]
   sellerIn[Seller Wallet]

   treasuryIn1 & treasuryIn2 & treasuryIn3 -->|Spend| tx
   offer --> |ADA| tx
   sellerIn --> |NFT| tx

   tx["Transaction (Tx)"]

   tx --> |Provision| treasuryOut1 & treasuryOut2 & treasuryOut3
   tx --> |ADA| sellerOut
   tx --> |NFT| buyerOut

   treasuryOut1[Treasury1]
   treasuryOut2[Treasury2]
   treasuryOut3[Treasuries...]
   buyerOut[Buyer Wallet]
   sellerOut[Seller Wallet]
Loading

Possible vectors of attack

Treasury blocking - Low-impact

Attackers can block happy path usage of the contract by using treasury outside of the contract. It is a problem, because a user cannot use predefined treasuries, what is unaccessible

  • There is no problem to spawn more treasury to prevent attack (it is possible to block only one treasury in one transaction)
  • There is an option to create the treasury during the transaction. A user can withdraw locked ADA by squashing treasuries during withdrawal
  • It is problematic only for NFT with low value (NFT is more expansive than expected)

Treasury with datum by hash - Off-Chain

An attacker can create treasuries with datum, that is not Inline or with less ADA than is allowed, this treasury SHOULD NOT be listed

Building

A standard build is very simple by command:

aiken build

or if you want to keep traces:

aiken build -k

Off-Chain

There is a library for off-chain manipulation:

Testing

You can write tests in any module using the test keyword. For example:

test foo() {
  1 + 1 == 2
}

To run all tests, simply do:

aiken check

To run only tests matching the string foo, do:

aiken check -m foo

Resources

Find more on Aiken's user manual.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors