NFT marketplace, which allows you to buy and sell Cardano-based NFTs.
- 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)
-
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)
- 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)
Simple contract, that checks argument OutputReference is present on inputs. The purpose is only for minting disposable assets (for treasury validation method)
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
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]
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
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 presentand 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)
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
- 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]
The transaction is signed by stored payment credentials There is no other check for the spending contract
- Store selling Unit to datum and check, that Unit is present in UTxO (easy to parse, but there is a problem with CIP68)
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]
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)
An attacker can create treasuries with datum, that is not Inline or with less ADA than is allowed, this treasury SHOULD NOT be listed
A standard build is very simple by command:
aiken buildor if you want to keep traces:
aiken build -kThere is a library for off-chain manipulation:
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 checkTo run only tests matching the string foo, do:
aiken check -m fooFind more on Aiken's user manual.