Browse All Knots

0/0
Data is loaded from Cardano and stored locally.
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
  }
}