| Service | Price |
|---|---|
| One Time Deployment Fee | 50 ADA |
| Minting Fee | Free |
| Early Member Burn Rewards | Free |
| Last Member Withdraws | 1% or free with CLI |
// 🖲️ store and spend the state thread game logic
use cardano/transaction.{Input,
Output, OutputReference, Transaction, find_input}
use cocktail.{inputs_at_with_policy, outputs_at_with_policy}
use utils/datum_oracle.{OracleDatum}
use utils/find_policy_id_of_first_token.{find_policy_id_of_first_token}
use utils/redeemer_oracle.{MintDsNFT, OracleRedeemer}
use utils/utils_oracle.{is_datum_updated, is_output_value_clean, is_value_paid}
pub fn oracle_spend(
datum_opt: Option<OracleDatum>,
redeemer: OracleRedeemer,
input: OutputReference,
tx: Transaction,
) {
// find the input being spent
expect Some(own_input) = find_input(tx.inputs, input)
// find the NFT from its own input
let oracle_nft_policy = find_policy_id_of_first_token(own_input)
// find the address of the input being spent
let own_address = own_input.output.address
// destructure input datum
expect Some(input_datum) = datum_opt
when
(
redeemer,
inputs_at_with_policy(tx.inputs, own_address, oracle_nft_policy),
outputs_at_with_policy(tx.outputs, own_address, oracle_nft_policy),
)
is {
(
// when the redeemer includes a new_winner
MintDsNFT { winner: new_winner },
// when 1 input with oracle nft is present
[_],
// when 1 output with oracle nft is present
[only_output],
) ->
and {
is_output_value_clean(only_output),
is_datum_updated(input_datum, only_output, new_winner),
is_value_paid(tx, input_datum),
}
_ -> False
}
}
validator oracle {
spend(
datum_opt: Option<OracleDatum>,
redeemer: OracleRedeemer,
input: OutputReference,
tx: Transaction,
) {
oracle_spend(datum_opt, redeemer, input, tx)
}
else(_) {
fail
}
}
// 🖲️ one shot nft policy for state thread
use cardano/address.{Address}
use cardano/assets.{PolicyId}
use cardano/transaction.{OutputReference, Transaction}
use utils/utils_oracle_nft.{
listing_is_sent, nft_names, one_time, oracle_is_sent, tokens_are_minted,
}
pub fn oracle_nft_mint(
utxo_ref: OutputReference,
oracle_address: Address,
knot_address: Address,
knot_fee: Int,
policy_id: PolicyId,
tx: Transaction,
) {
// [utxo_ref.transaction_id, "Knot A Receipt"]
expect [oracle_name, receipt_name] = nft_names(utxo_ref)
and {
// Makes sure utxo_ref is a part of the inputs
one_time(tx.inputs, utxo_ref)?,
// Checks if only two tokens are minted and their names are orcale_name and receipt_name
tokens_are_minted(tx.mint, policy_id, oracle_name, receipt_name, 1)?,
// Checks if oracle_nft is (name and policy) is sent to oracle_address
oracle_is_sent(tx.outputs, policy_id, oracle_address, oracle_name)?,
// Checks if correct fee is sent to knot_address.
listing_is_sent(tx.outputs, knot_address, knot_fee)?,
}
}
validator oracle_nft(
utxo_ref: OutputReference,
oracle_address: Address,
knot_address: Address,
knot_fee: Int,
) {
mint(_redeemer: Data, policy_id: PolicyId, tx: Transaction) {
oracle_nft_mint(
utxo_ref,
oracle_address,
knot_address,
knot_fee,
policy_id,
tx,
)
}
else(_) {
fail
}
}
// 🫀 mint and burn ds_nft (ds is a nod to the original contract I called Double Spent)
use aiken/primitive/bytearray.{from_string}
use cardano/assets.{PolicyId}
use cardano/transaction.{InlineDatum, Input, Transaction}
use cocktail.{
check_policy_only_burn, convert_int_to_bytes, inputs_with, inputs_with_policy,
only_minted_token,
}
use utils/datum_oracle.{OracleDatum}
use utils/utils_ds_nft.{MintPolarity, RBurn, RMint, check_game_mode}
use utils/config.{config_receipt_name_string}
pub fn ds_nft_mint(
oracle_nft: PolicyId,
redeemer: MintPolarity,
policy_id: PolicyId,
tx: Transaction,
) {
when redeemer is {
RMint ->
when inputs_with_policy(tx.inputs, oracle_nft) is {
// when 1 oracle input is present
[oracle_input] ->
// There are 2 nfts from this policy so we make sure the receipt is not present, and infer the real oracle nft is being spent
when
inputs_with(
[oracle_input],
oracle_nft,
from_string(config_receipt_name_string),
)
is {
[_] -> False
_ -> {
// Oracle NFT is being spent
// with inline datum
expect InlineDatum(input_datum) = oracle_input.output.datum
expect OracleDatum { count, mode, slot_start, slot_increase, .. } =
input_datum
and {
only_minted_token(
tx.mint,
policy_id,
convert_int_to_bytes(count),
1,
),
check_game_mode(tx, slot_start, slot_increase, count, mode),
}
}
}
_ -> False
}
// all minting values are negative
RBurn -> check_policy_only_burn(tx.mint, policy_id)
}
}
validator ds_nft(oracle_nft: PolicyId) {
mint(redeemer: MintPolarity, policy_id: PolicyId, tx: Transaction) {
ds_nft_mint(oracle_nft, redeemer, policy_id, tx)
}
else(_) {
fail
}
}
// 💰 burn a ds_nft to unlock utxos length of divisor
use aiken/builtin.{divide_integer, less_than_integer}
use aiken/collection/list
use aiken/primitive/int.{from_utf8}
use cardano/assets.{PolicyId}
use cardano/transaction.{
InlineDatum, Output, OutputReference, Transaction, find_input,
}
use cocktail.{inputs_at, inputs_with_policy, only_minted_token}
use utils/datum_oracle.{OracleDatum}
use utils/redeemer_treasury.{TreasuryRedeemer, Withdraw}
pub fn treasury_spend(
oracle_nft: PolicyId,
nft_policy: PolicyId,
redeemer: TreasuryRedeemer,
input: OutputReference,
tx: Transaction,
) {
when
(
redeemer,
inputs_with_policy(tx.reference_inputs, oracle_nft),
find_input(tx.inputs, input),
)
is {
(
// when redeemer has asset name
Withdraw { asset_name },
// when 1 input reference with oracle nft is present
[oracle_input_ref],
// when we are spending our own input
Some(own_input),
) ->
when (oracle_input_ref.output.datum, from_utf8(asset_name)) is {
(
// when oracle utxo has some inline datum
InlineDatum(oracle_datum),
// when the redeemer's asset name converts to an integer from utf8
Some(asset_name_int),
) -> {
// oracle datum is valid
expect OracleDatum { count, divisor, .. } = oracle_datum
// use asset_name to check for burn
let is_burned = only_minted_token(tx.mint, nft_policy, asset_name, -1)
// use asset_name_int to check if less than count divided by divisor
let under_threshold =
less_than_integer(asset_name_int, divide_integer(count, divisor))
// use divisor to check amount of inputs being spent
let is_correct_input_count =
list.length(inputs_at(tx.inputs, own_input.output.address)) == divisor
is_burned? && under_threshold? && is_correct_input_count?
}
_ -> {
trace @"Invalid oracle datum or nft name integer"
False
}
}
_ -> {
trace @"Invalid redeemer, oracle input reference, or own input"
False
}
}
}
validator treasury(oracle_nft: PolicyId, nft_policy: PolicyId) {
spend(
_datum_opt: Option<Data>,
redeemer: TreasuryRedeemer,
input: OutputReference,
tx: Transaction,
) {
treasury_spend(oracle_nft, nft_policy, redeemer, input, tx)
}
else(_) {
fail
}
}
// 🪺 store utxos for winner - spend logic offloaded to pot_withdraw validator
use aiken/crypto.{ScriptHash}
use aiken_design_patterns/stake_validator
use cardano/transaction.{OutputReference, Transaction}
pub fn pot_spend_spend(withdraw_script_hash: ScriptHash, tx: Transaction) {
stake_validator.spend_minimal(
withdraw_script_hash: withdraw_script_hash,
tx: tx,
)
}
validator pot_spend(withdraw_script_hash: ScriptHash) {
spend(
_datum_opt: Option<Data>,
_redeemer: Void,
_input: OutputReference,
tx: Transaction,
) {
pot_spend_spend(withdraw_script_hash, tx)
}
else(_) {
fail
}
}
// 🪺 be the winner and wait for timer to expire to withdraw the pot utxos
use aiken_design_patterns/stake_validator
use cardano/address.{Credential}
use cardano/assets.{PolicyId}
use cardano/transaction.{InlineDatum, Transaction}
use cocktail.{inputs_with_policy, key_signed, valid_after}
use utils/datum_oracle.{OracleDatum}
pub fn pot_withdraw_withdraw(
oracle_nft: PolicyId,
own_credential: Credential,
tx: Transaction,
) {
stake_validator.withdraw(
// This logic runs once per transaction rather than once per utxo
withdrawal_logic: fn(_own_validator, tx) {
when inputs_with_policy(tx.reference_inputs, oracle_nft) is {
[oracle_input_ref] ->
when oracle_input_ref.output.datum is {
// some datum is present
InlineDatum(oracle_datum) -> {
// oracle datum is valid
expect OracleDatum {
count,
slot_start,
slot_increase,
winner,
..
} = oracle_datum
and {
// is_winner?,
key_signed(tx.extra_signatories, winner),
// is_timer_expired?,
valid_after(
tx.validity_range,
slot_start + count * slot_increase,
),
}
}
_ -> {
trace @"Invalid oracle datum"
False
}
}
_ -> {
trace @"Invalid redeemer or oracle reference"
False
}
}
},
stake_cred: own_credential,
tx: tx,
)
}
validator pot_withdraw(oracle_nft: PolicyId) {
withdraw(_redeemer: Void, own_credential: Credential, tx: Transaction) {
pot_withdraw_withdraw(oracle_nft, own_credential, tx)
}
else(_) {
fail
}
}
// 🔀 Trade NFTs peer to peer
use cardano/address.{Address}
use cardano/assets.{from_lovelace, lovelace_of}
use cardano/transaction.{Input, OutputReference, Transaction, find_input}
use cocktail.{
address_pub_key, get_all_value_to, inputs_at, key_signed, value_geq,
}
use utils/datum_marketplace.{MarketplaceDatum}
use utils/redeemer_marketplace.{Buy, Close, MarketplaceRedeemer}
pub fn marketplace_spend(
owner: Address,
fee_percentage_basis_point: Int,
datum_opt: Option<MarketplaceDatum>,
redeemer: MarketplaceRedeemer,
input: OutputReference,
tx: Transaction,
) {
expect Some(datum) = datum_opt
when redeemer is {
Buy -> {
expect Some(own_input) = find_input(tx.inputs, input)
let own_address = own_input.output.address
let is_only_one_input_from_own_address =
when inputs_at(tx.inputs, own_address) is {
[_] -> True
_ -> False
}
let is_proceed_paid =
get_all_value_to(tx.outputs, datum.seller)
|> value_geq(
from_lovelace(datum.price + lovelace_of(own_input.output.value)),
)
let is_fee_paid =
get_all_value_to(tx.outputs, owner)
|> value_geq(
from_lovelace(datum.price * fee_percentage_basis_point / 10000),
)
is_only_one_input_from_own_address && is_fee_paid && is_proceed_paid
}
Close -> {
expect Some(pub_key) = address_pub_key(datum.seller)
key_signed(tx.extra_signatories, pub_key)
}
}
}
validator marketplace(owner: Address, fee_percentage_basis_point: Int) {
spend(
datum_opt: Option<MarketplaceDatum>,
redeemer: MarketplaceRedeemer,
input: OutputReference,
tx: Transaction,
) {
marketplace_spend(
owner,
fee_percentage_basis_point,
datum_opt,
redeemer,
input,
tx,
)
}
else(_) {
fail
}
}