-
Notifications
You must be signed in to change notification settings - Fork 0
/
mpKhalaniTokens.hl
95 lines (84 loc) · 3.74 KB
/
mpKhalaniTokens.hl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
minting khalani_tokens
import { DatumOutbox } from "./outbox.hl"
import { Message, MessagePayloadMint, MessagePayloadBurn } from "./message.hl"
// Parameters
const ISM_KHALANI: MintingPolicyHash = MintingPolicyHash::new(#)
const KHALANI_SENDER: ByteArray = #
// The outbox script that ships these messages.
const ADDRESS_OUTBOX: Address = Address::new(
Credential::new_validator(ValidatorHash::new(#)), Option[StakingCredential]::None
)
// Burning tokens at Khalani script to mint Khalani tokens
// (Optional) Parameterize with Khalani script address to
// add a strict address check?
func main(_, ctx: ScriptContext) -> Bool {
// TODO: Should we ban the minting of other policies here?
own_mph: MintingPolicyHash = ctx.get_current_minting_policy_hash();
own_minted: Map[ByteArray]Int = ctx.tx.minted.get_policy(own_mph);
// TODO: Loosen check to allow multiple processing in the same tx.
assert(own_minted.length == 1, "Can only handle one token");
token_name: ByteArray = own_minted.head_key;
token_value: Int = own_minted.head_value;
if (token_value < 0) {
// TODO: Loosen check for composability. The check prevents destroying
// inbound messages at the Khalani Script when burning for outbound.
datum_inputs: []TxInput = ctx.tx.inputs.filter((input: TxInput) -> {
input.output.datum.switch {
Inline => true,
else => false
}
});
assert(
datum_inputs.length == 1 && datum_inputs.head.output.address == ADDRESS_OUTBOX,
"Must process an outbox as the only input with inline datum"
);
// We "trust" the parameterized outbox to validate its own logic correctly.
// At the end of the day, we can choose outbox at compile time.
outbox_output: TxOutput = ctx.tx.outputs.find((output: TxOutput) -> {
output.address == ADDRESS_OUTBOX
});
outbox_datum: DatumOutbox = outbox_output.datum.switch {
i: Inline => DatumOutbox::from_data(i.data),
else => error("Invalid outbox output: missing inline datum")
};
message: Message = outbox_datum.latest_message.unwrap();
payload_burn: MessagePayloadBurn = message.get_burn_payload();
token_name == payload_burn.token_name &&
token_value == -payload_burn.quantity &&
ctx.tx.is_signed_by(payload_burn.sender_pkh)
} else {
// For minting, must burn exactly one authentic message
// Trusting the parameterized policy to not issue duplicate messages
// TODO: Loosen the check to process multiple messages at once?
message_tokens: Map[ByteArray]Int = ctx.tx.minted.get_policy(ISM_KHALANI);
assert(
message_tokens.length == 1 && message_tokens.head_value == -1,
"Must burn a message token"
);
// TODO: Loosen check for composability, e.g., restricting to only
// a Message datum instead of any inline datum
message_inputs: []TxInput = ctx.tx.inputs.filter((input: TxInput) -> {
input.output.datum.switch {
Inline => true,
else => false
}
});
assert(message_inputs.length == 1, "Must process only a message");
message: Message = message_inputs.head.output.datum.switch {
i: Inline => Message::from_data(i.data),
else => error("Invalid message output: missing inline datum")
};
assert(message.id() == message_tokens.head_key, "Malformed message");
assert(message.sender == KHALANI_SENDER, "Invalid sender");
payload_mint: MessagePayloadMint = message.get_mint_payload();
assert(
token_name == payload_mint.token_name &&
token_value == payload_mint.quantity,
"Must mint correctly"
);
ctx.tx.outputs.any((output: TxOutput) -> {
output.address == payload_mint.recipient_address &&
output.value.get_safe(AssetClass::new(own_mph, payload_mint.token_name)) == payload_mint.quantity
})
}
}