Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
These scripts will represent a set of scripts that can manage treasury budget funds.

The treasury withdrawal should target the reward account for the `treasury` validator.

The `treasury` validator should only allow those funds to be withdrawn if they are paid out
to UTxOs locked by the `treasury` validator payment credential.

The script must also enforce that these funds are not used in the following ways:
 - Must not delegate funds to a stake pool
  - i.e. must not have a staking credential attached on the outputs
  - and must not delegate itself to a stake pool
 - Must not participate in governance
  - i.e. must not delegate to a DRep
  - must not register as a DRep itself
  - must not cast votes
  - must not submit proposals
 - Must not register a stake pool

This initial commit covers the withdrawal half of this equation (the simpler half).
Next, the spend code path will need to be built.
  • Loading branch information
Quantumplation committed Feb 13, 2025
0 parents commit ef89843
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Continuous Integration

on:
push:
branches: ["main"]
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aiken-lang/setup-aiken@v1
with:
version: v1.1.4
- run: aiken fmt --check
- run: aiken check -D
- run: aiken build
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Aiken compilation artifacts
artifacts/
# Aiken's project working directory
build/
# Aiken's default documentation export
docs/
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# treasury-funds

Write validators in the `validators` folder, and supporting functions in the `lib` folder using `.ak` as a file extension.

```aiken
validator my_first_validator {
spend(_datum: Option<Data>, _redeemer: Data, _output_reference: Data, _context: Data) {
True
}
}
```

## Building

```sh
aiken build
```

## Configuring

**aiken.toml**
```toml
[config.default]
network_id = 41
```

Or, alternatively, write conditional environment modules under `env`.

## Testing

You can write tests in any module using the `test` keyword. For example:

```aiken
use config
test foo() {
config.network_id + 1 == 42
}
```

To run all tests, simply do:

```sh
aiken check
```

To run only tests matching the string `foo`, do:

```sh
aiken check -m foo
```

## Documentation

If you're writing a library, you might want to generate an HTML documentation for it.

Use:

```sh
aiken docs
```

## Resources

Find more on the [Aiken's user manual](https://aiken-lang.org).
15 changes: 15 additions & 0 deletions aiken.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# This file was generated by Aiken
# You typically do not need to edit this file

[[requirements]]
name = "aiken-lang/stdlib"
version = "v2.2.0"
source = "github"

[[packages]]
name = "aiken-lang/stdlib"
version = "v2.2.0"
requirements = []
source = "github"

[etags]
18 changes: 18 additions & 0 deletions aiken.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name = "sundae/treasury-funds"
version = "0.0.0"
compiler = "v1.1.4"
plutus = "v3"
license = "Apache-2.0"
description = "Aiken contracts for project 'sundae/treasury-funds'"

[repository]
user = "sundae"
project = "treasury-funds"
platform = "github"

[[dependencies]]
name = "aiken-lang/stdlib"
version = "v2.2.0"
source = "github"

[config]
65 changes: 65 additions & 0 deletions validators/treasury.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use cardano/address.{Credential}
use cardano/assets.{lovelace_of}
use cardano/transaction.{Transaction}
use aiken/collection/pairs
use aiken/collection/list

validator treasury {
spend(_d, _r, _i, _t) {
False
}

// Funds can be withdrawn only if they are paid to the spending portion of this script
// without a staking address; this is how the funds are moved out of the reward account
// after a treasury withdrawal
withdraw(_, account: Credential, self: Transaction) {
// There must be only one withdrawal from this credential
expect [amount] = self.withdrawals |> pairs.get_all(account)

// To prevent shenanigans where a disbursement equal to the amount is used to trick
// the withdrawal, we assert that `account` never occurs in the inputs
// This is slightly less flexible than ensuring the totals carry through, but
// also simpler to reason about
expect None = self.inputs
|> list.find(fn(input) { input.output.address.payment_credential == account })

// Sum up the amounts of the withdrawal paid to `account`
// This lets us split the funds across multiple UTxOs for accounting purposes
// but still ensures that the total amount is correct
let output_sum = self.outputs
|> list.filter(fn(output) { output.address.payment_credential == account })
|> list.map(fn(output) {
// Treasury funds must not be staked
expect None = output.address.stake_credential
lovelace_of(output.value)
})
|> list.reduce(0, +)

// Ensure that the output sum is *at least* the amount
// This allows some flexibility in case of minUTxO issues, which are unlikely
// We also let the transaction fee be deducted from this withdrawal,
// Which is likely to be small, but still shouldn't be imposed on the administrator
expect output_sum >= amount - self.fee

// If all the above is true, allow the withdrawal
True
}

// (n.b. We could just use `else` for these, but it's clearer to make things explicit)

// The funds received by the treasury are not allowed to vote in governance
vote(_r, _v, _t) {
False
}

// The funds received by the treasury are not allowed to publish any certificates
// In particular, this prevents the stake script from delegating to a stake pool,
// registering a stake pool, registering as a DRep, or delegating to a DRep.
publish(_r, _c, _t) {
False
}

else(_) {
False
}
}

0 comments on commit ef89843

Please sign in to comment.