Cost

106

Pot:
5
+
Early:
100
+
Fee:
1
21:1
Late incentive ratio to minting cost
20m
Time stretch per mint

Value Distribution after 100 mints

Burn eligibility

DEPLOY YOUR OWN KNOT

ADA amount paid during a mint to cash out lower-numbered NFTs.

min:
1
max:
100.00M

Determines burn eligible threshold and their ADA share.

min:
2
max:
20

ADA amount paid during a mint to incentivize the next buyer.

min:
1
max:
100.00M

Minutes added to the timer per new NFT mint.

min:
1
max:
40.00K
Pricing
KnotCash charges a 50 ADA one-time fee to deploy a new contract onto Cardano. No fees are taken from minting or burning within the contract. Winners are charged a 3% fee onsite or that can be avoided by using the CLI to encourage bots to fight for the pot.
ServicePrice
One Time Deployment Fee50 ADA
Minting FeeFree
Early Member Burn RewardsFree
Last Member Withdraws1% or free with CLI
oracle.ak
// 🖲️ 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
  }
}
 
oracle_nft.ak
// 🖲️ 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
  }
}
 
ds_nft.ak
// 🫀 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
  }
}
 
treasury.ak
// 💰 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
  }
}
 
pot_spend.ak
// 🪺 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
  }
}
 
pot_withdraw.ak
// 🪺 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
  }
}
 
marketplace.ak
// 🔀 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
  }
}