feat: create multi validator for gift card.
This version of gift card allows for minting many gift cards in a tx under the same policy by policy creator. Also allows batch redeem.
This commit is contained in:
parent
0afc3aba13
commit
73c9ac3409
|
@ -0,0 +1,168 @@
|
||||||
|
use aiken/bytearray
|
||||||
|
use aiken/cbor
|
||||||
|
use aiken/dict
|
||||||
|
use aiken/hash.{blake2b_256}
|
||||||
|
use aiken/list
|
||||||
|
use aiken/transaction.{
|
||||||
|
InlineDatum, Input, Output, ScriptContext, Spend, Transaction,
|
||||||
|
} as tx
|
||||||
|
use aiken/transaction/credential.{Address, PaymentCredential, ScriptCredential}
|
||||||
|
use aiken/transaction/value
|
||||||
|
|
||||||
|
type Action {
|
||||||
|
Mint(Int)
|
||||||
|
Burn
|
||||||
|
}
|
||||||
|
|
||||||
|
type SpendTokenName =
|
||||||
|
ByteArray
|
||||||
|
|
||||||
|
validator(creator: ByteArray) {
|
||||||
|
fn redeem(
|
||||||
|
// Each spend input checks for a token name matching the datum being burned
|
||||||
|
datum: SpendTokenName,
|
||||||
|
_r: Data,
|
||||||
|
ctx: ScriptContext,
|
||||||
|
) {
|
||||||
|
let ScriptContext { transaction, purpose } =
|
||||||
|
ctx
|
||||||
|
|
||||||
|
let Transaction { inputs, mint, .. } =
|
||||||
|
transaction
|
||||||
|
|
||||||
|
expect Spend(own_ref) =
|
||||||
|
purpose
|
||||||
|
|
||||||
|
expect Some(own_input) =
|
||||||
|
list.find(inputs, fn(input) { input.output_reference == own_ref })
|
||||||
|
|
||||||
|
let Input {
|
||||||
|
output: Output { address: Address { payment_credential, .. }, .. },
|
||||||
|
..
|
||||||
|
} =
|
||||||
|
own_input
|
||||||
|
|
||||||
|
expect ScriptCredential(own_validator_hash) =
|
||||||
|
payment_credential
|
||||||
|
|
||||||
|
value.quantity_of(mint, own_validator_hash, datum) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gift_card(rdmr: Action, ctx: ScriptContext) -> Bool {
|
||||||
|
// get values from transaction and purpose
|
||||||
|
let ScriptContext { transaction, purpose } =
|
||||||
|
ctx
|
||||||
|
|
||||||
|
expect tx.Mint(policy_id) =
|
||||||
|
purpose
|
||||||
|
|
||||||
|
let Transaction { inputs, mint, extra_signatories, outputs, .. } =
|
||||||
|
transaction
|
||||||
|
|
||||||
|
let minted_assets =
|
||||||
|
mint
|
||||||
|
|> value.tokens(policy_id)
|
||||||
|
|> dict.to_list()
|
||||||
|
|
||||||
|
when rdmr is {
|
||||||
|
Mint(total) -> {
|
||||||
|
expect [input, ..] =
|
||||||
|
inputs
|
||||||
|
// Base is created from serializing a utxo ref being spent. Thus this guarantees a unique base
|
||||||
|
let base =
|
||||||
|
cbor.serialise(input.output_reference)
|
||||||
|
// Create a list of expected token names
|
||||||
|
let expected_minted_token_names =
|
||||||
|
create_expected_minted_nfts(base, total, [])
|
||||||
|
// Check contract creator is a signer of this tx
|
||||||
|
let signature_check =
|
||||||
|
list.any(extra_signatories, fn(n) { creator == n })
|
||||||
|
// Support multiple gift card creation by allowing a
|
||||||
|
// 'number of tokens minted' == 'outputs with datum being token name'
|
||||||
|
signature_check && check_mint_and_outputs(
|
||||||
|
minted_assets,
|
||||||
|
outputs,
|
||||||
|
expected_minted_token_names,
|
||||||
|
ScriptCredential(policy_id),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Burn ->
|
||||||
|
list.all(
|
||||||
|
minted_assets,
|
||||||
|
fn(asset) {
|
||||||
|
let (_, amount) =
|
||||||
|
asset
|
||||||
|
amount == -1
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(self: List<a>, e: a, compare: fn(a, a) -> Ordering) -> List<a> {
|
||||||
|
when self is {
|
||||||
|
[] ->
|
||||||
|
[e]
|
||||||
|
[x, ..xs] ->
|
||||||
|
if compare(e, x) == Less {
|
||||||
|
[e, ..self]
|
||||||
|
} else {
|
||||||
|
[x, ..insert(xs, e, compare)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each minted token name is in the expected list, has quantity of 1,
|
||||||
|
// and has a corresponding ouput with datum containing token name.
|
||||||
|
// Otherwise fail
|
||||||
|
fn check_mint_and_outputs(
|
||||||
|
minted_assets: List<(ByteArray, Int)>,
|
||||||
|
outputs: List<Output>,
|
||||||
|
expected_assets: List<ByteArray>,
|
||||||
|
validator_cred: PaymentCredential,
|
||||||
|
) -> Bool {
|
||||||
|
when minted_assets is {
|
||||||
|
[] ->
|
||||||
|
True
|
||||||
|
[(minted_asset_name, quantity), ..rest_assets] -> {
|
||||||
|
expect True =
|
||||||
|
list.any(
|
||||||
|
expected_assets,
|
||||||
|
fn(expected_asset) { expected_asset == minted_asset_name },
|
||||||
|
)
|
||||||
|
expect True =
|
||||||
|
list.any(
|
||||||
|
outputs,
|
||||||
|
fn(output) {
|
||||||
|
let Output { address, datum, .. } =
|
||||||
|
output
|
||||||
|
datum == InlineDatum(minted_asset_name) && address.payment_credential == validator_cred
|
||||||
|
},
|
||||||
|
)
|
||||||
|
quantity == 1 && check_mint_and_outputs(
|
||||||
|
rest_assets,
|
||||||
|
outputs,
|
||||||
|
expected_assets,
|
||||||
|
validator_cred,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_expected_minted_nfts(
|
||||||
|
base: ByteArray,
|
||||||
|
counter: Int,
|
||||||
|
accum: List<ByteArray>,
|
||||||
|
) -> List<ByteArray> {
|
||||||
|
if counter == 0 {
|
||||||
|
accum
|
||||||
|
} else {
|
||||||
|
let token_name =
|
||||||
|
blake2b_256(bytearray.push(base, counter))
|
||||||
|
|
||||||
|
let accum =
|
||||||
|
[token_name, ..accum]
|
||||||
|
|
||||||
|
create_expected_minted_nfts(base, counter - 1, accum)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue