diff --git a/.gitmodules b/.gitmodules
index 05a2491880..0367e3ce26 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1238,6 +1238,9 @@
[submodule "vendor/grammars/vscode-TalonScript"]
path = vendor/grammars/vscode-TalonScript
url = https://github.com/mrob95/vscode-TalonScript.git
+[submodule "vendor/grammars/vscode-aiken"]
+ path = vendor/grammars/vscode-aiken
+ url = https://github.com/aiken-lang/vscode-aiken.git
[submodule "vendor/grammars/vscode-antlers-language-server"]
path = vendor/grammars/vscode-antlers-language-server
url = https://github.com/Stillat/vscode-antlers-language-server.git
diff --git a/grammars.yml b/grammars.yml
index 23c95ad2f7..71d75cd6e9 100644
--- a/grammars.yml
+++ b/grammars.yml
@@ -1109,6 +1109,9 @@ vendor/grammars/vsc-fennel:
vendor/grammars/vscode-TalonScript:
- markdown.talon.codeblock
- source.talon
+vendor/grammars/vscode-aiken:
+- source.aiken
+- source.uplc
vendor/grammars/vscode-antlers-language-server:
- text.html.statamic
vendor/grammars/vscode-bitbake:
diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml
index aae547bd83..538369eecb 100644
--- a/lib/linguist/languages.yml
+++ b/lib/linguist/languages.yml
@@ -261,6 +261,14 @@ Agda:
tm_scope: source.agda
ace_mode: text
language_id: 12
+Aiken:
+ type: programming
+ color: "#640ff8"
+ ace_mode: text
+ extensions:
+ - ".ak"
+ tm_scope: source.aiken
+ language_id: 899409497
Alloy:
type: programming
color: "#64C800"
@@ -7446,6 +7454,14 @@ UnrealScript:
codemirror_mode: clike
codemirror_mime_type: text/x-java
language_id: 382
+Untyped Plutus Core:
+ type: programming
+ color: "#36adbd"
+ ace_mode: text
+ extensions:
+ - ".uplc"
+ tm_scope: source.uplc
+ language_id: 1061635506
UrWeb:
type: programming
color: "#ccccee"
@@ -8515,4 +8531,4 @@ xBase:
- ".prw"
tm_scope: source.harbour
ace_mode: text
- language_id: 421
+ language_id: 421
\ No newline at end of file
diff --git a/samples/Aiken/multi.ak b/samples/Aiken/multi.ak
new file mode 100644
index 0000000000..2d78aa345a
--- /dev/null
+++ b/samples/Aiken/multi.ak
@@ -0,0 +1,162 @@
+use aiken/builtin
+use aiken/bytearray
+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
+
+ (
+ mint
+ |> value.from_minted_value
+ |> value.quantity_of(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.from_minted_value
+ |> value.tokens(policy_id)
+ |> dict.to_pairs()
+
+ 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 = builtin.serialise_data(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 Pair(_, amount) = asset
+ amount == -1
+ },
+ )
+ }
+ }
+}
+
+fn insert(self: List, e: a, compare: fn(a, a) -> Ordering) -> List {
+ 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: Pairs,
+ outputs: List