Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.DS_Store
.codex
.DS_Store
binary_sizes_baseline.json
baseline.json
trigger.sh
Expand Down
6 changes: 4 additions & 2 deletions examples/smart_contracts/always_true.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
from opshin.prelude import *


def validator(context: ScriptContext) -> None:
pass
@dataclass()
class AlwaysTrue(Contract):
def raw(self, _context: ScriptContext) -> None:
pass
14 changes: 8 additions & 6 deletions examples/smart_contracts/assert_sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from opshin.prelude import *


def validator(context: ScriptContext) -> None:
datum: int = own_datum_unsafe(context)
redeemer: int = context.redeemer
assert (
datum + redeemer == 42
), f"Expected datum and redeemer to sum to 42, but they sum to {datum + redeemer}"
@dataclass()
class AssertSum(Contract):
def spend_with_datum(
self, datum: int, redeemer: int, _context: ScriptContext
) -> None:
assert (
datum + redeemer == 42
), f"Expected datum and redeemer to sum to 42, but they sum to {datum + redeemer}"
23 changes: 10 additions & 13 deletions examples/smart_contracts/bitwise.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
#!opshin
from opshin.prelude import *
from opshin.std.integrity import check_integrity
from opshin.std.math import *


def validator(context: ScriptContext) -> None:
"""
A contract that checks whether the bitwise AND of the datum and redeemer is zero.
"""
maybe_datum = own_datum(context)
# it is possible that no datum is attached (new in PlutusV3)
if isinstance(maybe_datum, NoOutputDatum):
# in that case, no datum was attached and we accept the transaction
@dataclass()
class Bitwise(Contract):
def spend_no_datum(self, _redeemer: int, _context: ScriptContext) -> None:
return
else:
datum: int = maybe_datum.datum
# In plutus v3, the redeemer is in the script context
redeemer: int = context.redeemer

def spend_with_datum(
self, datum: int, redeemer: int, _context: ScriptContext
) -> None:
"""
A contract that checks whether the bitwise AND of the datum and redeemer is zero.
"""
datum_bytes = bytes_big_from_unsigned_int(datum)
redeemer_bytes = bytes_big_from_unsigned_int(redeemer)
# compute the bitwise XOR of the two byte arrays
Expand Down
21 changes: 12 additions & 9 deletions examples/smart_contracts/gift.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ class WithdrawDatum(PlutusData):
pubkeyhash: PubKeyHash


def validator(context: ScriptContext) -> None:
datum: WithdrawDatum = own_datum_unsafe(context)
# check that the datum has correct structure (recommended for user inputs)
# can be omitted if the datum can not make its way into a permanent script state (i.e., not stored in an output)
check_integrity(datum)
sig_present = datum.pubkeyhash in context.transaction.signatories
assert (
sig_present
), f"Required signature missing, expected {datum.pubkeyhash.hex()} but got {[s.hex() for s in context.transaction.signatories]}"
@dataclass()
class Gift(Contract):
def spend_with_datum(
self, datum: WithdrawDatum, _redeemer: Anything, context: ScriptContext
) -> None:
# check that the datum has correct structure (recommended for user inputs)
# can be omitted if the datum can not make its way into a permanent script state (i.e., not stored in an output)
check_integrity(datum)
sig_present = datum.pubkeyhash in context.transaction.signatories
assert (
sig_present
), f"Required signature missing, expected {datum.pubkeyhash.hex()} but got {[s.hex() for s in context.transaction.signatories]}"
10 changes: 8 additions & 2 deletions examples/smart_contracts/inspect_script_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
from opshin.prelude import *


def validator(context: ScriptContext):
print(context.to_cbor())
# this validator will print the datum, redeemer and script context passed from the node in a readable format
@dataclass()
class InspectScriptContext(Contract):
def raw(self, context: ScriptContext) -> None:
print(f"script context (CBOR hex): {context.to_cbor().hex()}")
print(f"script context (native): {context}")

assert False, "Failing in order to show script logs"
70 changes: 36 additions & 34 deletions examples/smart_contracts/liquidity_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,37 +607,39 @@ def get_spending_purpose(context: ScriptContext) -> Spending:
return purpose


def validator(context: ScriptContext) -> None:
"""
Validates that the pool is spent correctly
DISCLAIMER: This is a simple example to demonstrate onchain based contract upgradeability and should not be used in production.
"""
purpose = get_spending_purpose(context)
redeemer: PoolAction = context.redeemer
check_integrity(redeemer)
datum: PoolState = own_datum_unsafe(context)
check_integrity(datum)
own_input_info = context.transaction.inputs[redeemer.pool_input_index]
assert (
own_input_info.out_ref == purpose.tx_out_ref
), "Index of own input does not match purpose"

own_output = context.transaction.outputs[redeemer.pool_output_index]
if isinstance(redeemer, AddLiquidity) or isinstance(redeemer, RemoveLiquidity):
check_valid_license_present(
redeemer.license_input_index,
context.transaction,
datum.up_pool_params.license_policy_id,
)
check_change_liquidity(datum, redeemer, context, own_input_info, own_output)
elif isinstance(redeemer, SwapAsset):
check_valid_license_present(
redeemer.license_input_index,
context.transaction,
datum.up_pool_params.license_policy_id,
)
check_swap(datum, redeemer, context, own_input_info, own_output)
elif isinstance(redeemer, PoolUpgrade):
check_upgrade(datum, redeemer, context, own_input_info, own_output)
else:
assert False, "Unknown redeemer"
@dataclass()
class LiquidityPool(Contract):
def spend_with_datum(
self, datum: PoolState, redeemer: PoolAction, context: ScriptContext
) -> None:
"""
Validates that the pool is spent correctly
DISCLAIMER: This is a simple example to demonstrate onchain based contract upgradeability and should not be used in production.
"""
purpose = get_spending_purpose(context)
check_integrity(redeemer)
check_integrity(datum)
own_input_info = context.transaction.inputs[redeemer.pool_input_index]
assert (
own_input_info.out_ref == purpose.tx_out_ref
), "Index of own input does not match purpose"

own_output = context.transaction.outputs[redeemer.pool_output_index]
if isinstance(redeemer, AddLiquidity) or isinstance(redeemer, RemoveLiquidity):
check_valid_license_present(
redeemer.license_input_index,
context.transaction,
datum.up_pool_params.license_policy_id,
)
check_change_liquidity(datum, redeemer, context, own_input_info, own_output)
elif isinstance(redeemer, SwapAsset):
check_valid_license_present(
redeemer.license_input_index,
context.transaction,
datum.up_pool_params.license_policy_id,
)
check_swap(datum, redeemer, context, own_input_info, own_output)
elif isinstance(redeemer, PoolUpgrade):
check_upgrade(datum, redeemer, context, own_input_info, own_output)
else:
assert False, "Unknown redeemer"
42 changes: 22 additions & 20 deletions examples/smart_contracts/marketplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,25 @@ def check_owner_signed(signatories: List[PubKeyHash], owner: PubKeyHash) -> None
), f"Owner did not sign transaction, requires {owner.hex()} but got {[s.hex() for s in signatories]}"


def validator(context: ScriptContext) -> None:
purpose = context.purpose
datum: Listing = own_datum_unsafe(context)
check_integrity(datum)
redeemer: ListingAction = context.redeemer
check_integrity(redeemer)

tx_info = context.transaction
assert isinstance(purpose, Spending), f"Wrong script purpose: {purpose}"
own_utxo = resolve_spent_utxo(tx_info.inputs, purpose)
own_addr = own_utxo.address

check_single_utxo_spent(tx_info.inputs, own_addr)
# It is recommended to explicitly check all options with isinstance for user input
if isinstance(redeemer, Buy):
check_paid(tx_info.outputs, datum.vendor, datum.price)
elif isinstance(redeemer, Unlist):
check_owner_signed(tx_info.signatories, datum.owner)
else:
assert False, "Wrong redeemer"
@dataclass()
class Marketplace(Contract):
def spend_with_datum(
self, datum: Listing, redeemer: ListingAction, context: ScriptContext
) -> None:
purpose = context.purpose
check_integrity(datum)
check_integrity(redeemer)

tx_info = context.transaction
assert isinstance(purpose, Spending), f"Wrong script purpose: {purpose}"
own_utxo = resolve_spent_utxo(tx_info.inputs, purpose)
own_addr = own_utxo.address

check_single_utxo_spent(tx_info.inputs, own_addr)
# It is recommended to explicitly check all options with isinstance for user input
if isinstance(redeemer, Buy):
check_paid(tx_info.outputs, datum.vendor, datum.price)
elif isinstance(redeemer, Unlist):
check_owner_signed(tx_info.signatories, datum.owner)
else:
assert False, "Wrong redeemer"
Loading
Loading