You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
What operating system are you using, and which version?
macOS
Describe what the problem is?
The following contract crashes to compile:
use aiken/hash.{Blake2b_224, Hash}
use aiken/interval.{Finite}
use aiken/list.{any}
use aiken/option
use aiken/transaction.{
Input, Output, OutputReference, ScriptContext, ScriptPurpose, Spend,
ValidityRange,
}
use aiken/transaction/credential.{
Address, Inline, PublicKeyCredential, Script, ScriptCredential,
VerificationKey,
}
use aiken/transaction/value.{Value}
type VerificationKeyHash =
Hash<Blake2b_224, VerificationKey>
type POSIXTime =
Int
type Lovelace =
Int
type ValidatorHash =
Hash<Blake2b_224, Script>
type Datum {
seller: VerificationKeyHash,
min_bid: Lovelace,
deadline: POSIXTime,
// the asset that is being auctioned (NFT + ADA lovelace)
for_sale: Value,
// initialized at 0, which signifies the auction doesn't yet have valid bids
highest_bid: Lovelace,
highest_bidder: VerificationKeyHash,
}
type Redeemer {
Close
Bid { bidder: VerificationKeyHash, bid: Lovelace }
}
fn spend(datum: Datum, redeemer: Redeemer, ctx: ScriptContext) -> Bool {
english_auction(
datum,
redeemer,
ctx.transaction.validity_range,
ctx.purpose,
ctx.transaction.inputs,
ctx.transaction.outputs,
)
}
fn english_auction(
datum: Datum,
redeemer: Redeemer,
validity_range: ValidityRange,
script_purpose: ScriptPurpose,
trx_inputs: List<Input>,
trx_outputs: List<Output>,
) -> Bool {
assert Spend(output_reference) = script_purpose
assert Some(validator_hash) =
our_validator_script_address_hash(trx_inputs, output_reference)
when redeemer is {
Bid(bidder, bid) ->
bid_action(
validator_hash,
validity_range,
datum,
bidder,
bid,
trx_outputs,
)
Close ->
close_action(
datum: datum,
validity_range: validity_range,
outputs: trx_outputs,
)
}
}
fn bid_action(
validator_hash: ValidatorHash,
validity_range: ValidityRange,
datum: Datum,
bidder: VerificationKeyHash,
bid: Lovelace,
outputs: List<Output>,
) -> Bool {
// lets see if min bid threshold is met or not
let min_bid_threshold_not_met = bid < datum.min_bid
// if bid is or isn't high enough
let bid_not_high_enough = bid <= datum.highest_bid
// delegate further
let bid_action2 =
bid_action2(
datum: datum,
bid: bid,
bidder: bidder,
validity_range: validity_range,
validator_hash: validator_hash,
outputs: outputs,
)
min_bid_threshold_not_met && bid_not_high_enough && bid_action2
}
fn bid_action2(
datum: Datum,
bid: Lovelace,
bidder: VerificationKeyHash,
validity_range: ValidityRange,
validator_hash: ValidatorHash,
outputs: List<Output>,
) -> Bool {
let new_expected_datum: Datum =
Datum { ..datum, highest_bid: bid, highest_bidder: bidder }
// it makes sense to approve biding action on this contract only if deadline has not been reached yet
let can_still_bid = must_start_before(validity_range, datum.deadline)
let has_new_bid_have_token_and_new_bid =
has_new_bid_have_token_and_new_bid(
validator_hash,
bid,
new_expected_datum,
outputs,
)
// we need to check if current bidder has been repaid
let is_old_bidder_repaid = is_old_bidder_repaid(datum, outputs)
can_still_bid && has_new_bid_have_token_and_new_bid && is_old_bidder_repaid
}
fn close_action(
datum: Datum,
validity_range: ValidityRange,
outputs: List<Output>,
) -> Bool {
let is_min_bid_threshold_not_met = datum.highest_bid < datum.min_bid
if is_min_bid_threshold_not_met {
close_action_winner_gets_token(
datum: datum,
validity_range: validity_range,
outputs: outputs,
)
} else {
close_action_seller_repaid(
datum: datum,
validity_range: validity_range,
outputs: outputs,
)
}
}
fn close_action_seller_repaid(
datum: Datum,
validity_range: ValidityRange,
outputs: List<Output>,
) -> Bool {
// we need to check if people placing bids have managed to place bet as high as minimum bid
let is_min_bid_threshold_not_met = datum.highest_bid < datum.min_bid
// if treshold is not met then we need to check number of things...
if is_min_bid_threshold_not_met {
// for starters if bid item has been returned to the seller by the off chain code for close action to be allowed
let is_for_sale_item_returned =
is_for_sale_returned_to_the_seller(datum, outputs)
// is bidding on item no longer possible then close action can be completed / allowed
// if bidding it is still possible, we should NOT allow owner to claim NFT while auction is running
let is_bidding_no_longer_possible =
must_start_after(validity_range, datum.deadline)
is_bidding_no_longer_possible && is_for_sale_item_returned
} else {
False
}
}
fn close_action_winner_gets_token(
datum: Datum,
validity_range: ValidityRange,
outputs: List<Output>,
) -> Bool {
// lets check if bidding is no longer possible (deadline passed)
let is_bidding_no_longer_possible =
must_start_after(validity_range, datum.deadline)
// seller received highest bid in lovelaces
let seller_receives_highest_bid =
seller_receives_highest_bid(
datum: datum,
highest_bid: datum.highest_bid,
outputs: outputs,
)
// eventually of course winner got his / her token and we need to assert for this
let winner_receives_for_sale_token =
winner_gets_auctioned_token(datum: datum, outputs: outputs)
is_bidding_no_longer_possible && seller_receives_highest_bid && winner_receives_for_sale_token
}
// we need to check that seller received the highest bid
fn seller_receives_highest_bid(
datum: Datum,
highest_bid: Lovelace,
outputs: List<Output>,
) -> Bool {
outputs
|> any_output_matches(
fn(payment_hash, output_value, _output_datum) {
payment_hash == datum.seller && output_value == value.from_lovelace(
highest_bid,
)
})
}
// this function will verify if old bidder got repaid
fn is_old_bidder_repaid(datum: Datum, outputs: List<Output>) -> Bool {
outputs
|> any_output_matches(
fn(payment_hash, output_value, _output_datum) {
// since we have old datum from the previous transaction it contains current highest bidder and when we spend this UTxO, assuming new bid is successful, new datum will include new highest_bidder
let current_highest_bid = datum.highest_bid
let current_highest_bidder_hash = datum.highest_bidder
payment_hash == current_highest_bidder_hash && output_value == value.from_lovelace(
current_highest_bid,
)
})
}
// on bid action - new bidder should have new token including bid amount (lovelaces) in one of the UTxO outputs
fn has_new_bid_have_token_and_new_bid(
validator_hash: ValidatorHash,
bid: Lovelace,
new_expected_datum: Datum,
outputs: List<Output>,
) -> Bool {
let datum_as_data: Data = new_expected_datum
outputs
|> any_output_matches(
fn(payment_hash, output_value, output_datum) {
// we need to verify if new bid also contains datum and if datum data matches
// it needs to have exactly the same values as defined by new_expected_datum requirements
let new_bid_contains_datum = output_datum == datum_as_data
new_bid_contains_datum && payment_hash == validator_hash && output_value == value.add(
new_expected_datum.for_sale,
value.from_lovelace(bid),
)
})
}
// we need to check that after auction has finished if winner received auctioned token / NFT
fn winner_gets_auctioned_token(datum: Datum, outputs: List<Output>) -> Bool {
outputs
|> any_output_matches(
fn(payment_hash, value, _datum) {
payment_hash == datum.seller && value == value.without_lovelace(
datum.for_sale,
)
})
}
// while closing auction we need to check if off-chain code returned the item to the seller
fn is_for_sale_returned_to_the_seller(
datum: Datum,
outputs: List<Output>,
) -> Bool {
outputs
|> any_output_matches(
fn(payment_hash, output_value, _output_datum) {
payment_hash == datum.seller && output_value == datum.for_sale
})
}
fn our_validator_script_address_hash(
inputs: List<Input>,
or: OutputReference,
) -> Option<ValidatorHash> {
let maybe_input: Option<Input> =
list.find(inputs, fn(input) { input.output_reference == or })
maybe_input
|> option.map(fn(v) { v.output })
|> option.map(fn(v) { v.address })
|> option.map(fn(v) { v.payment_credential })
|> option.map(
fn(v) {
when v is {
ScriptCredential(hash) -> Some(hash)
_ -> None
}
})
|> option.flatten()
}
fn must_start_before(range: ValidityRange, lower_bound: POSIXTime) -> Bool {
when range.lower_bound.bound_type is {
Finite(now) -> now < lower_bound
_ -> False
}
}
fn must_start_after(range: ValidityRange, lower_bound: POSIXTime) -> Bool {
when range.lower_bound.bound_type is {
Finite(now) -> now > lower_bound
_ -> False
}
}
fn any_output_matches(
outputs: List<Output>,
predicate: fn(ByteArray, Value, Data) -> Bool,
) -> Bool {
any(
outputs,
fn(output) {
let payment_hash = get_payment_addr_hash(output.address)
predicate(payment_hash, output.value, output.datum)
},
)
}
// pub type Output {
// address: Address,
// value: Value,
// datum: Datum,
// reference_script: Option<Hash<Blake2b_224, Script>>,
// }
// type Datum {
// seller: VerificationKeyHash,
// min_bid: Lovelace,
// deadline: POSIXTime,
// // the asset that is being auctioned (NFT + ADA lovelace)
// for_sale: Value,
// // initialized at 0, which signifies the auction doesn't yet have valid bids
// highest_bid: Lovelace,
// highest_bidder: VerificationKeyHash,
// }
fn mock_datum_data() -> Data {
let seller_hash_addr = #[1]
let policy_id1 = #[2]
let asset_name1 = #[3]
let nft = value.from_asset(policy_id1, asset_name1, 1)
let d1: Data =
Datum {
seller: seller_hash_addr,
min_bid: 1,
deadline: 1673966461,
for_sale: nft,
highest_bid: 0,
highest_bidder: seller_hash_addr,
}
}
test any_output_matches1() {
let b1 = #[1]
let b2 = #[2]
let v1 = value.from_lovelace(1)
let v2 = value.from_lovelace(2)
let d1 = mock_datum_data()
let a1 =
Address {
payment_credential: PublicKeyCredential(b1),
stake_credential: None,
}
let a2 =
Address {
payment_credential: PublicKeyCredential(b2),
stake_credential: None,
}
let b1 =
Address {
payment_credential: PublicKeyCredential(b1),
stake_credential: None,
}
let outputs: List<Output> = [
Output { address: a1, value: v1, datum: d1, reference_script: None },
Output { address: a2, value: v2, datum: d1, reference_script: None },
]
any_output_matches(outputs, fn(_payment_hash, _value, _datum) { True })
}
fn get_payment_addr_hash(address: Address) -> VerificationKeyHash {
when address.payment_credential is {
PublicKeyCredential(hash) -> hash
ScriptCredential(hash) -> hash
}
}
test get_payment_addr_hash_public_key() {
let b1 = #[1]
let b2 = #[2]
let addr =
Address {
payment_credential: PublicKeyCredential(b1),
stake_credential: Some(Inline(ScriptCredential(b2))),
}
get_payment_addr_hash(addr) == b1
}
test get_payment_addr_hash_script_key() {
let b2 = #[2]
let b3 = #[3]
let addr =
Address {
payment_credential: ScriptCredential(b3),
stake_credential: Some(Inline(ScriptCredential(b2))),
}
get_payment_addr_hash(addr) == b3
}
mati@Mateuszs-MacBook-Pro english_auction % aiken check
Compiling aiken-lang/stdlib a972f8e4d89d0998911423d7d6a1b99dd94dd1b9 (/Users/mati/Devel/OpenSource/aiken/examples/english_auction/build/packages/aiken-lang-stdlib)
Compiling aiken-lang/english_auction 0.0.0 (/Users/mati/Devel/OpenSource/aiken/examples/english_auction)
Error:
× Main thread panicked.
├─▶ at crates/aiken-lang/src/uplc.rs:3435:32
╰─▶ called `Option::unwrap()` on a `None` value
help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace.
What should be the expected behavior?
It should not crash.
The text was updated successfully, but these errors were encountered:
What Git revision are you using?
main as of time of writing
What operating system are you using, and which version?
Describe what the problem is?
The following contract crashes to compile:
What should be the expected behavior?
It should not crash.
The text was updated successfully, but these errors were encountered: