Skip to content
This repository was archived by the owner on Sep 17, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 52 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,64 @@
# Project Newm Contracts

A user will lock a NFT into a contract and then mint the cooresponding fractions for that NFT.
A collection of contracts used my Project Newm.

# Compile Instructions
# E2E Testing

Compile LockingContract.hs
The initial testing will assume Newm holds a special wallet that will regulate all token minting. In the future, this will be expanded into more decentralized solutions.

The example in the repo is with test wallets. A recompile with proper wallets will need to occur during the initial production phase.

## Flow

An artist comes to Newm wishing to tokenize an piece of art then fractionalize the tokenization. First, Newm will create the initial tokenized version of the art by minting a token on the official Newm catalog policy id. An artist may keep the original tokenized version of their art or they may fractionalize the original token.

The fractionalization of a token is the splitting of the ownership rights of the original tokenized art into 100 million pieces. The original tokenized art is locked into a smart contract and the resulting fractions are given to the artist. From here the artist my do what they wish with the fractions.

This entire process is reversible. At any point, an artist my obtain the original tokenized by burning the 100 million fractional tokens.

## Tokenization

Each UTxO inside the tokenization contract is a unique Newm catalog, completely determined by the token prefix name like "Newm_", "paintings_", or "songs_". The contract behaves as a sequential NFT generator, minting tokens using the concatentation of the prefix with the current token counter. The sequence of NFTs will continue forever or at least until the size of the token name exceeds 32 characters.

### Process

- Create catalog UTxO inside the tokenization contract.
- Mint sequential NFT for artist.
- (Optional) If required, any catalog NFT may be burned.
- (Optional) If required, any catalog may be removed.

** Each prefix and thus each UTxO must be unique.

### Example

```bash
cardano-cli transaction policyid --script-file locking_contract.plutus > validator.hash
./createCatalogUTxO.sh
./MintNFT.sh

# if required
./burnNFT.sh
./removedCatalogUTxO.sh
```
## Fractionalization

Place the validator hash of the plutus script into the minting policy.
Each fractionalization of a token must be prepped by Newm or the artist. This ensures that only one token may be added onto an existing UTxO for fractionalization. There is no way to do entry validation for smart contracts on Cardano. Validation only occurs during a spending event thus forcing the artist or Newm to update an already existing UTxO means forcing on-chain validation. The fractionalized tokens will have the fractional Newm policy id but will share the same name as the original token. The token to be fractionalize may only be unlocked if and only if all 100 million fractions are burned together at once.

Compile MintingContract.hs
### Process

- Create fractional UTxO.
- Lock and mint token fractions.
- (Optional) If required, fractional tokens may be burned to unlock the original token.
- (Optional) If required, the left over fractional UTxO may be removed after successful burning.

** Fractionalization of tokens if the same names will result in two sets of fractions.

### Example

```bash
cardano-cli transaction policyid --script-file minting_contract.plutus > policy.id
./createFractionalUTxO.sh
./lockAndFractionalize.sh

# if required
./unlockAndSolidify.sh
./removeFractionalUTxO.sh
```
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SELLER_ADDRESS=$(cat wallets/seller-wallet/payment.addr)
BUYER_ADDRESS=$(cat wallets/buyer-wallet/payment.addr)
PROFIT_ADDRESS=$(cat wallets/profit-wallet/payment.addr)

echo
${cli} query protocol-parameters --testnet-magic 1097911063 --out-file tmp/protocol.json
${cli} query tip --testnet-magic 1097911063 | jq
#
echo
Expand All @@ -31,5 +31,4 @@ ${cli} query utxo --address ${BUYER_ADDRESS} --testnet-magic 1097911063
echo
echo -e "\033[1;34m Profit Address: \033[0m"
echo -e "\n \033[1;34m ${PROFIT_ADDRESS} \033[0m \n";
${cli} query utxo --address ${PROFIT_ADDRESS} --testnet-magic 1097911063
#
# ${cli} query utxo --address ${PROFIT_ADDRESS} --testnet-magic 1097911063
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ seller_tx_in=${TXIN::-8}

echo -e "\033[0;36m Building Tx \033[0m"
FEE=$(${cli} transaction build \
--alonzo-era \
--babbage-era \
--protocol-params-file tmp/protocol.json \
--invalid-hereafter 99999999 \
--out-file tmp/tx.draft \
--change-address ${seller_address} \
--tx-in ${seller_tx_in} \
Expand Down
20 changes: 20 additions & 0 deletions fractionalize-scripts/data/datum.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"constructor": 0,
"fields": [
{
"bytes": "d15070626abe6a5c0e3d042daccf72f3df5b401955ada1c58da7d061"
},
{
"bytes": "a896333a052024101cca7218b1ea94d87af763e2b7166bac67a34566"
},
{
"bytes": "4e65774d5f30"
},
{
"bytes": "db7bffc41a43c4d9c31342e3fd457409aeb40302aa52058df374913b"
},
{
"bytes": ""
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ seller_pkh=$(cardano-cli address key-hash --payment-verification-key-file wallet
#
policy_id=$(cat ../minting-contract/policy.id)
#
SC_ASSET="1 7470b5d94b828481469f1c1a15edbcc5d23e0326fe60892fcf8dcdeb.455247"
MINT_ASSET="100 ${policy_id}.455247"
SC_ASSET="1 a896333a052024101cca7218b1ea94d87af763e2b7166bac67a34566.4e65774d5f30"
MINT_ASSET="100000000 ${policy_id}.4e65774d5f30"
UTXO_VALUE=$(${cli} transaction calculate-min-required-utxo \
--protocol-params-file tmp/protocol.json \
--tx-out="${buyer_address} ${SC_ASSET}" | tr -dc '0-9')
Expand Down Expand Up @@ -62,25 +62,26 @@ alltxin=""
TXIN=$(jq -r --arg alltxin "" 'keys[] | . + $alltxin + " --tx-in"' tmp/script_utxo.json)
script_tx_in=${TXIN::-8}

collat_utxo=$(cardano-cli transaction txid --tx-file tmp/tx.signed)

# exit
echo -e "\033[0;36m Building Tx \033[0m"
FEE=$(${cli} transaction build \
--alonzo-era \
--babbage-era \
--protocol-params-file tmp/protocol.json \
--invalid-hereafter 99999999 \
--out-file tmp/tx.draft \
--change-address ${buyer_address} \
--tx-in-collateral="${collat_utxo}#0" \
--tx-in ${buyer_tx_in} \
--tx-in ${script_tx_in} \
--tx-in-collateral 3ffc0c720f618c2ea451dfa95ede3836f674508673678dc356c8ffc531bd3343#0 \
--tx-in-script-file ${script_path} \
--tx-in-datum-file data/datum.json \
--tx-in-redeemer-file data/lock_redeemer.json \
--tx-out="${buyer_address_out}" \
--tx-out="${script_address_out}" \
--tx-out-datum-embed-file data/datum.json \
--required-signer-hash ${buyer_pkh} \
--required-signer-hash ${seller_pkh} \
--tx-in-script-file ${script_path} \
--mint="${MINT_ASSET}" \
--mint-redeemer-file data/datum.json \
--mint-script-file ${mint_path} \
Expand Down
1 change: 1 addition & 0 deletions fractionalize-scripts/path_to_socket.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/home/logic/Documents/Work/LogicalMechanism/full-node-wallet/node/db-testnet/node.socket
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,18 @@ script_tx_in=${TXIN::-8}

echo -e "\033[0;36m Building Tx \033[0m"
FEE=$(${cli} transaction build \
--alonzo-era \
--babbage-era \
--protocol-params-file tmp/protocol.json \
--invalid-hereafter 99999999 \
--out-file tmp/tx.draft \
--change-address ${seller_address} \
--tx-in ${seller_tx_in} \
--tx-in-collateral ${collateral_tx_in} \
--tx-in ${seller_tx_in} \
--tx-in ${script_tx_in} \
--tx-in-script-file ${script_path} \
--tx-in-datum-file data/datum.json \
--tx-in-redeemer-file data/exit_redeemer.json \
--tx-out="${seller_address_out}" \
--required-signer-hash ${seller_pkh} \
--tx-in-script-file ${script_path} \
--testnet-magic 1097911063)

IFS=':' read -ra VALUE <<< "${FEE}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ seller_address=$(cat wallets/seller-wallet/payment.addr)
seller_pkh=$(cardano-cli address key-hash --payment-verification-key-file wallets/seller-wallet/payment.vkey)
policy_id=$(cat ../minting-contract/policy.id)

SC_ASSET="1 7470b5d94b828481469f1c1a15edbcc5d23e0326fe60892fcf8dcdeb.455247"
BURN_ASSET="-100 ${policy_id}.455247"
SC_ASSET="1 a896333a052024101cca7218b1ea94d87af763e2b7166bac67a34566.4e65774d5f30"
BURN_ASSET="-100000000 ${policy_id}.4e65774d5f30"
UTXO_VALUE=$(${cli} transaction calculate-min-required-utxo \
--protocol-params-file tmp/protocol.json \
--tx-out="${buyer_address} ${SC_ASSET}" | tr -dc '0-9')
Expand Down Expand Up @@ -65,9 +65,8 @@ script_tx_in=${TXIN::-8}
collat=$(cardano-cli transaction txid --tx-file tmp/tx.signed)
echo -e "\033[0;36m Building Tx \033[0m"
FEE=$(${cli} transaction build \
--alonzo-era \
--babbage-era \
--protocol-params-file tmp/protocol.json \
--invalid-hereafter 99999999 \
--out-file tmp/tx.draft \
--change-address ${buyer_address} \
--tx-in ${buyer_tx_in} \
Expand Down
2 changes: 1 addition & 1 deletion locking-contract/locking_contract.plutus

Large diffs are not rendered by default.

42 changes: 32 additions & 10 deletions locking-contract/src/CheckFuncs.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,26 @@
{-# OPTIONS_GHC -fexpose-all-unfoldings #-}
module CheckFuncs
( isValueContinuing
, isPKHGettingPaid
-- , isPKHGettingPaid
, isAddrGettingPaid
, isSingleScript
, createAddress
) where
import qualified Plutus.V1.Ledger.Address as Address
import qualified Plutus.V1.Ledger.Value as Value
import Ledger hiding (singleton)
import Plutus.V1.Ledger.Credential
-- import qualified Plutus.V1.Ledger.Address as Address
import qualified Plutus.V1.Ledger.Value as Value
import Ledger hiding ( singleton )
import PlutusTx.Prelude
{- |
Author : The Ancient Kraken
Copyright: 2022
Version : Rev 2
-}
-------------------------------------------------------------------------
-- | Create a proper Address Type.
-------------------------------------------------------------------------
createAddress :: PubKeyHash -> PubKeyHash -> Address
createAddress pkh sc = if getPubKeyHash sc == emptyByteString then Address (PubKeyCredential pkh) Nothing else Address (PubKeyCredential pkh) (Just $ StakingHash $ PubKeyCredential sc)
-------------------------------------------------------------------------------
-- | Search each TxOut for a value.
-------------------------------------------------------------------------------
Expand All @@ -51,19 +59,33 @@ isValueContinuing (x:xs) val
checkVal :: Bool
checkVal = Value.geq (txOutValue x) val
-------------------------------------------------------------------------------
-- | Search each TxOut for an address and value.
-- | Search each TxOut for an pkh and value.
-------------------------------------------------------------------------------
isPKHGettingPaid :: [TxOut] -> PubKeyHash -> Value -> Bool
isPKHGettingPaid [] _pkh _val = False
isPKHGettingPaid (x:xs) pkh val
isAddrGettingPaid :: [TxOut] -> Address -> Value -> Bool
isAddrGettingPaid [] _addr _val = False
isAddrGettingPaid (x:xs) addr val
| checkAddr && checkVal = True
| otherwise = isPKHGettingPaid xs pkh val
| otherwise = isAddrGettingPaid xs addr val
where
checkAddr :: Bool
checkAddr = txOutAddress x == Address.pubKeyHashAddress pkh
checkAddr = txOutAddress x == addr

checkVal :: Bool
checkVal = Value.geq (txOutValue x) val
-- -------------------------------------------------------------------------------
-- -- | Search each TxOut for an address and value.
-- -------------------------------------------------------------------------------
-- isPKHGettingPaid :: [TxOut] -> PubKeyHash -> Value -> Bool
-- isPKHGettingPaid [] _pkh _val = False
-- isPKHGettingPaid (x:xs) pkh val
-- | checkAddr && checkVal = True
-- | otherwise = isPKHGettingPaid xs pkh val
-- where
-- checkAddr :: Bool
-- checkAddr = txOutAddress x == Address.pubKeyHashAddress pkh

-- checkVal :: Bool
-- checkVal = Value.geq (txOutValue x) val
-------------------------------------------------------------------------------
-- | Force a single script utxo input.
-------------------------------------------------------------------------------
Expand Down
Loading