diff --git a/DEVELOPER_GUIDELINES.md b/DEVELOPER_GUIDELINES.md new file mode 100644 index 0000000..6417415 --- /dev/null +++ b/DEVELOPER_GUIDELINES.md @@ -0,0 +1,351 @@ +# Developer Guidelines + +This document provides comprehensive guidelines for developers contributing to the OADA UNHCR Yield Donation Module project. + +## ๐Ÿ“‹ Table of Contents + +- [Getting Started](#getting-started) +- [Code Style](#code-style) +- [Contract Development](#contract-development) +- [Testing Guidelines](#testing-guidelines) +- [Security Considerations](#security-considerations) +- [Contributing](#contributing) + +## ๐Ÿš€ Getting Started + +### Prerequisites + +- [Aiken](https://aiken-lang.org/) compiler (v1.9.0+) +- [Nix](https://nixos.org/) package manager +- Cardano development environment + +### Installation + +1. **Clone the repository**: + + ```bash + git clone https://github.com/OptimFinance/clean-code.git + cd yield-donation + ``` + +2. **Setup development environment**: + + ```bash + nix develop + ``` + +3. **Install dependencies**: + ```bash + cd oada + aiken check + ``` + +### Project Structure + +``` +oada-donate/ +โ”œโ”€โ”€ oada/ # Main Aiken project +โ”‚ โ”œโ”€โ”€ validators/ # Smart contract validators +โ”‚ โ”œโ”€โ”€ lib/ # Library modules +โ”‚ โ”œโ”€โ”€ aiken.toml # Project configuration +โ”‚ โ””โ”€โ”€ plutus.json # Plutus compatibility layer +โ”œโ”€โ”€ aiken-common/ # Shared Aiken utilities +โ”œโ”€โ”€ test/ # Test suite +โ”œโ”€โ”€ flake.nix # Nix development environment +โ””โ”€โ”€ README.md # This file +``` + +## ๐Ÿ‘จโ€๐Ÿ’ป Code Style + +### Naming Conventions + +- **Functions and Variables**: Use `snake_case` +- **Types and Validators**: Use `PascalCase` +- **Private Functions**: Prefix with underscore (`_`) + +### Documentation + +- Document all public functions with clear descriptions +- Include parameter types and return values +- Add examples for complex logic +- Use inline comments for business logic explanations + +### Error Handling + +- Use `expect` for required values +- Provide meaningful error messages +- Handle edge cases explicitly +- Validate inputs thoroughly + +## ๐Ÿ”ง Contract Development + +### Adding New Validators + +1. **Create the validator file** in `oada/validators/` +2. **Define the datum type** with clear field descriptions +3. **Implement required functions**: + - `mint/2` for minting operations + - `spend/3` for spending operations +4. **Add comprehensive tests** + +#### Example Validator Structure + +```aiken +validator(parameter1: Type1, parameter2: Type2) { + fn mint(redeemer: RedeemerType, ctx: ScriptContext) { + // Minting logic + } + + fn spend(datum: DatumType, redeemer: RedeemerType, ctx: ScriptContext) { + // Spending logic + } +} +``` + +### Validation Functions + +When creating validation functions in `lib/oada/validation.ak`: + +1. **Use clear parameter names** +2. **Document the business logic** +3. **Handle all edge cases** +4. **Return boolean values for validation** + +### Datum and Redeemer Design + +#### Datum Guidelines + +- Keep datum structures minimal and focused +- Use descriptive field names +- Include validation constraints in the datum when possible +- Document the purpose of each field + +#### Redeemer Guidelines + +- Design redeemers to be explicit about the action being performed +- Use sum types for different operations +- Include necessary parameters for validation +- Keep redeemers immutable and stateless + +### Common Patterns + +#### Input Validation + +```aiken +fn validate_input(input: Input, ctx: ScriptContext) -> Bool { + and{ + // Check input exists + list.has(ctx.transaction.inputs, input)?, + // Validate input value + value.quantity_of(input.output.value, token_policy, "") > 0?, + // Check authorization + list.has(ctx.transaction.extra_signatories, required_key)? + } +} +``` + +#### Output Validation + +```aiken +fn validate_output(output: Output, expected_value: Int) -> Bool { + and{ + // Check output value + value.quantity_of(output.value, token_policy, "") == expected_value?, + // Validate address + output.address.payment_credential == expected_credential?, + // Check datum if required + output.datum == expected_datum? + } +} +``` + +## ๐Ÿงช Testing Guidelines + +### Test Types + +1. **Unit Tests**: Test individual functions +2. **Integration Tests**: Test validator interactions +3. **Property Tests**: Test invariants and properties +4. **Edge Cases**: Test boundary conditions + +### Test Structure + +```aiken +test test_function_name() { + // Setup + let input = create_test_input() + + // Execute + let result = function_under_test(input) + + // Assert + result == expected_output +} +``` + +### Testing Best Practices + +- **Test Coverage**: Aim for 100% coverage of public functions +- **Edge Cases**: Test boundary conditions and error cases +- **Property Testing**: Use property-based testing for complex logic +- **Integration Testing**: Test validator interactions thoroughly +- **Mock Data**: Use realistic test data that matches production scenarios + +### Running Tests + +```bash +# Run all tests +cd oada +aiken test + +# Run tests in watch mode +aiken test --watch + +# Run specific test file +aiken test validators/donate_soada.ak + +# Run tests with coverage +aiken test --coverage +``` + +## ๐Ÿ”’ Security Considerations + +### Input Validation + +- Validate all inputs thoroughly +- Check for malicious or unexpected data +- Implement proper bounds checking +- Validate cryptographic signatures + +### Access Control + +- Implement proper authorization checks +- Use whitelists for privileged operations +- Verify signatories for sensitive actions +- Implement role-based access control + +### Resource Limits + +- Set appropriate limits for operations +- Prevent resource exhaustion attacks +- Implement rate limiting where applicable +- Monitor gas consumption + +### Audit Trail + +- Maintain clear transaction logs +- Include sufficient context in redeemers +- Log important state changes +- Enable transaction tracing + +### Common Security Patterns + +#### Authorization Check + +```aiken +fn check_authorization(required_key: KeyHash, ctx: ScriptContext) -> Bool { + list.has(ctx.transaction.extra_signatories, required_key) +} +``` + +#### Value Validation + +```aiken +fn validate_value(amount: Int, min: Int, max: Int) -> Bool { + and{ + amount >= min, + amount <= max + } +} +``` + +#### State Transition Validation + +```aiken +fn validate_state_transition(old_state: State, new_state: State) -> Bool { + // Ensure state transitions are valid + and{ + new_state.version > old_state.version, + new_state.timestamp > old_state.timestamp, + // Add other validation rules + } +} +``` + +## ๐Ÿค Contributing + +### Development Workflow + +1. **Fork the repository** +2. **Create a feature branch**: `git checkout -b feature/your-feature` +3. **Make your changes** following the development guidelines +4. **Add tests** for new functionality +5. **Run the test suite**: `aiken test` +6. **Submit a pull request** + +### Pull Request Guidelines + +1. **Clear description** of changes +2. **Reference related issues** +3. **Include tests** for new functionality +4. **Update documentation** if needed +5. **Ensure all tests pass** + +### Code Review Process + +1. **Automated checks** must pass +2. **At least one review** from maintainers +3. **Address feedback** before merging +4. **Squash commits** for clean history + +### Commit Message Guidelines + +Use conventional commit format: + +``` +type(scope): description + +[optional body] + +[optional footer] +``` + +Types: + +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation changes +- `style`: Code style changes +- `refactor`: Code refactoring +- `test`: Test changes +- `chore`: Build/tooling changes + +### Code Review Checklist + +- [ ] Code follows style guidelines +- [ ] Tests are included and passing +- [ ] Documentation is updated +- [ ] Security considerations addressed +- [ ] Performance impact considered +- [ ] Error handling implemented +- [ ] Edge cases covered + +## ๐Ÿ“š Additional Resources + +- [Aiken Documentation](https://aiken-lang.org/) +- [Cardano Developer Portal](https://developers.cardano.org/) +- [Plutus Documentation](https://plutus.readthedocs.io/) +- [Aiken Style Guide](https://aiken-lang.org/style-guide) + +## ๐Ÿ“„ License + +This project is licensed under the MIT License - see the [LICENSE.md](../LICENSE.md) file for details. + +## ๐Ÿ†˜ Getting Help + +- **Issues**: [GitHub Issues](https://github.com/OptimFinance/clean-code/issues) +- **Discussions**: [GitHub Discussions](https://github.com/OptimFinance/clean-code/discussions) + +--- + +**Note**: These guidelines are living documents. Please contribute improvements and updates as the project evolves. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a962b46 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2025 Optim Finance + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd970f6 --- /dev/null +++ b/README.md @@ -0,0 +1,230 @@ +# OADA UNHCR Yield Donation Module + +A decentralized donation protocol built on Cardano using Aiken smart contracts. OADA enables automated charitable giving through staking mechanisms and yield distribution. + +## ๐Ÿ“‹ Table of Contents + +- [Overview](#overview) +- [Architecture](#architecture) +- [Contract Structure](#contract-structure) +- [Getting Started](#getting-started) +- [Testing](#testing) +- [Contributing](#contributing) + +## ๐ŸŽฏ Overview + +OADA is a sophisticated DeFi protocol that combines staking, yield generation, and automated charitable donations. The protocol allows users to stake tokens and automatically donate a portion of their yield to charitable causes while maintaining control over their funds. + +### Key Features + +- **Automated Donations**: Configurable donation ratios for yield distribution +- **Staking Mechanics**: sOTOKEN and OTOKEN exchange mechanisms +- **Yield Management**: Automated yield calculation and distribution +- **NFT Rewards**: CIP-68 compliant NFTs for donation tracking +- **Batch Processing**: Efficient batch staking operations + +## ๐Ÿ—๏ธ Architecture + +The OADA protocol consists of several interconnected smart contracts that work together to provide a complete donation and staking ecosystem: + +``` +OADA Protocol +โ”œโ”€โ”€ Core Contracts +โ”‚ โ”œโ”€โ”€ Staking AMO (Automated Market Operations) +โ”‚ โ”œโ”€โ”€ Donation Validator +โ”‚ โ”œโ”€โ”€ Batch Stake Validator +โ”‚ โ””โ”€โ”€ Collateral AMO +โ”œโ”€โ”€ Supporting Contracts +โ”‚ โ”œโ”€โ”€ Token Policies (OTOKEN, sOTOKEN) +โ”‚ โ”œโ”€โ”€ Fee Claim Rules +โ”‚ โ””โ”€โ”€ Donation Strategies +โ””โ”€โ”€ Utilities + โ”œโ”€โ”€ Validation Functions + โ””โ”€โ”€ Common Types +``` + +## ๐Ÿ“œ Contract Structure + +### Core Validators + +#### 1. `donate_soada.ak` - Main Donation Validator + +**Purpose**: Handles the core donation logic and NFT minting + +**Key Functions**: + +- `mint(redeemer: IdMintRedeemer, ctx: ScriptContext)`: Mints donation positions +- `spend(datum: DonationDatum, redeemer: (Int, Int, Int), ctx: ScriptContext)`: Processes donations + +**Datum Structure**: + +```aiken +type DonationDatum { + owner: KeyHash, + donation_ratio: (Int, Int), // Ratio as (numerator, denominator) + initial_exchange: (Int, Int) // Initial exchange rate +} +``` + +#### 2. `staking_amo.ak` - Staking Automated Market Operations + +**Purpose**: Manages sOTOKEN supply and exchange rates + +**Key Functions**: + +- `mint(redeemer: IdMintRedeemer, ctx: ScriptContext)`: Mints staking positions +- `spend(datum: StakingAmoDatum, _redeemer: Data, ctx: ScriptContext)`: Updates staking parameters + +**Datum Structure**: + +```aiken +type StakingAmoDatum { + sotoken: PolicyId, + sotoken_amount: Int, + sotoken_backing: Int, + sotoken_limit: Int, + odao_fee: Int, + fee_claimer: Id, + fee_claim_rule: ScriptHash, + odao_sotoken: Int +} +``` + +#### 3. `batch_stake.ak` - Batch Staking Validator + +**Purpose**: Handles batch staking and unstaking operations + +**Key Functions**: + +- `spend(datum: BatchStakeDatum, redeemer: BatchStakeRedeemer, ctx: ScriptContext)`: Processes batch operations + +**Redeemer Types**: + +```aiken +type BatchStakeRedeemer { + CancelStake + DigestStake(Int, Option) +} +``` + +#### 4. `collateral_amo.ak` - Collateral Management + +**Purpose**: Manages collateral and strategy deployment + +#### 5. `deposit_amo.ak` - Deposit Management + +**Purpose**: Handles deposit operations and liquidity management + +### Supporting Contracts + +#### Token Policies + +- `otoken_policy.ak`: OTOKEN minting policy +- `sotoken_rule.ak`: sOTOKEN validation rules +- `otoken_rule.ak`: OTOKEN validation rules + +#### Fee Management + +- `fee_claim_rule.ak`: Fee claiming logic +- `donation_strategy.ak`: Donation strategy management + +### Utility Modules + +#### `validation.ak` - Core Validation Functions + +Key functions for contract validation: + +- `update_sotoken_amount/11`: Updates sOTOKEN amounts and exchange rates +- `spawn_strategy/11`: Deploys new strategies +- `despawn_strategy/9`: Destroys strategies and returns funds + +## ๐Ÿš€ Getting Started + +### Prerequisites + +- [Aiken](https://aiken-lang.org/) compiler (v1.9.0+) +- [Nix](https://nixos.org/) package manager +- Cardano development environment + +### Installation + +1. **Clone the repository**: + + ```bash + git clone https://github.com/OptimFinance/clean-code.git + cd yield-donation + ``` + +2. **Setup development environment**: + + ```bash + nix develop + ``` + +3. **Install dependencies**: + ```bash + cd oada + aiken check + ``` + +### Project Structure + +``` +oada-donate/ +โ”œโ”€โ”€ oada/ # Main Aiken project +โ”‚ โ”œโ”€โ”€ validators/ # Smart contract validators +โ”‚ โ”œโ”€โ”€ lib/ # Library modules +โ”‚ โ”œโ”€โ”€ aiken.toml # Project configuration +โ”‚ โ””โ”€โ”€ plutus.json # Plutus compatibility layer +โ”œโ”€โ”€ aiken-common/ # Shared Aiken utilities +โ”œโ”€โ”€ test/ # Test suite +โ”œโ”€โ”€ flake.nix # Nix development environment +โ”œโ”€โ”€ README.md # This file +โ”œโ”€โ”€ DEVELOPER_GUIDELINES.md # Developer guidelines +โ””โ”€โ”€ LICENSE.md # MIT License +``` + +## ๐Ÿ‘จโ€๐Ÿ’ป Development Guidelines + +For comprehensive development guidelines, code style, testing practices, and contribution workflows, please see [DEVELOPER_GUIDELINES.md](DEVELOPER_GUIDELINES.md). + +## ๐Ÿงช Testing + +### Quick Start + +```bash +cd oada +aiken test +``` + +For comprehensive testing guidelines, best practices, and advanced testing techniques, see [DEVELOPER_GUIDELINES.md](DEVELOPER_GUIDELINES.md#testing-guidelines). + +## ๐Ÿค Contributing + +We welcome contributions! Please see [DEVELOPER_GUIDELINES.md](DEVELOPER_GUIDELINES.md#contributing) for detailed information about: + +- Development workflow +- Pull request guidelines +- Code review process +- Commit message conventions +- Code review checklist + +## ๐Ÿ“š Additional Resources + +- [Aiken Documentation](https://aiken-lang.org/) +- [Cardano Developer Portal](https://developers.cardano.org/) +- [Plutus Documentation](https://plutus.readthedocs.io/) + +## ๐Ÿ“„ License + +This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. + +## ๐Ÿ†˜ Support + +- **Issues**: [GitHub Issues](https://github.com/optim/oada-donate/issues) +- **Discussions**: [GitHub Discussions](https://github.com/optim/oada-donate/discussions) +- **Documentation**: [Project Wiki](https://github.com/optim/oada-donate/wiki) + +--- + +**Note**: This is a development version. For production use, ensure thorough testing and security audits. diff --git a/oada/plutus.json b/oada/plutus.json index 78611ed..768a5a8 100644 --- a/oada/plutus.json +++ b/oada/plutus.json @@ -166,6 +166,186 @@ "compiledCode": "590e7e01000032323232323232323232323222223253333330110021532323233300c3003300e375400a2a6601a9212052756e6e696e672032206172672076616c696461746f72207769746864726177001533300c32323232323253330123370e9002180a1baa0011325333013300a30153754002264a6660286014602c6ea80044c8c8c8c8c94ccc064c040c06cdd500089919299980d9809180e9baa00113232533301d3370e9005180f9baa0011533302130203754002264a66603c66e3cdd7180918109baa01a48810013375e6e9c00cdd3998119ba7330234c01014000330234c101400033023375000297ae04bd700a9998110018a9980f80e8b0991929998120008a9981080f8b099299981298140010a99981099b8f375c602a0069110013375e6e9c004dd399813180a98121baa01d3302630133024375403a6604c6ea00112f5c0294054cc08808058dd6181300098130021bac3024003323330010013758602460426ea8039200022253330240021001133300300330270023253330213375e602a60486ea8c054c090dd5180998121baa001300b33026375201a97ae01337000046660286eacc04cc090dd5180998121baa001375c602a60486ea8074dd7180998121baa01d10023026002153301e01b16153301e01b163253333330250011001153301e01b16153301e01b16153301e01b16153301e01b16332232533301f301630213754002264a666040602e60446ea80044c94ccc085288992999811180c98121baa00113028302537540022a660469214865787065637420536f6d6528756e7772617070656429203d206c6973742e68656164286275696c74696e2e756e5f636f6e7374725f646174612872656465656d6572292e326e6429001632533302600114c103d87a8000130173302730280014bd701bac302432337606ea0c090004dd398128009baa00110013026302337540022a660429215465787065637420536f6d652872656465656d657229203d2070616972732e6765745f66697273742872656465656d6572732c205370656e642869645f696e7075742e6f75747075745f7265666572656e63652929001632323300100100422533302600114c0103d87a8000132323253330253375e00c604e006260346605400297ae01330050050023027002302a0023028001300933024301330223754604a60446ea80052f5c02a6604092013165787065637420536f6d652869645f696e70757429203d2066696e645f69645f696e7075742869642c20696e707574732900163300701900237586020603e6ea8030dd59811181198119811981198119811981198119811980f9baa00c3233001001323330010013756601e60406ea8c08cc080dd500199198008009bab301030213754602060426ea8014894ccc08c00452f5bded8c02646604a66ec0c088004dd319198008009bab302400222533302600114bd6f7b6300991981419bb03025001375066e052000375a604c002660060066054004605000266006006604e004604a002444a6660460042002264666008008604e0066644646600200200a44a66605000226605266ec0dd48021ba60034bd6f7b630099191919299981419b9000800213302d337606ea4020dd30038028a99981419b8f00800213253330293020302b375400226605c66ec0dd4804981798161baa0010041004325333029533302d00114a229405300103d87a80001301e3302e374c00297ae032333001001008002222533302f0021001132333004004303300333223233001001005225333034001133035337606ea4010dd4001a5eb7bdb1804c8c8c8c94ccc0d0cdc800400109981c99bb037520106ea001c01454ccc0d0cdc7804001099299981a9816181b9baa00113303a337606ea4024c0ecc0e0dd5000802080219299981a98160008a60103d87a80001302a3303a375000297ae03370000e00226607266ec0dd48011ba800133006006003375a606c0066eb8c0d0008c0e0008c0d8004dd718170009bad302f001303100213302d337606ea4008dd3000998030030019bab302a003375c6050004605800460540026eb8c088004dd5981180098128011129998108008a5eb804c8ccc888c8cc00400400c894ccc09c004400c4c8cc0a4dd3998149ba90063302930260013302930270014bd7019801801981580118148009bae30200013756604200266006006604a00460460022a660389214865787065637420536f6d6528636d5f6f757429203d2066696e645f69645f6f757470757428636f6c6c61746572616c5f616d6f5f69642c2074785f696e666f2e6f757470757473290016330053758604060426042603a6ea802894ccc06d4ccc06ccdd79807980f1baa300f301e3754002600a66040601e603c6ea80592f5c0294454cc07124014b6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2869642e706f6c6963795f696429203f2046616c73650014a02a66603666e21200033300e3756601a603c6ea8004dd71807980f1baa016375c601a603c6ea80585288a9980e2494876616c75652e7175616e746974795f6f66286f75747075742e76616c75652c2069642e706f6c6963795f69642c2069642e61737365745f6e616d6529203e2030203f2046616c73650014a02940c07cc070dd50008a9980d2494565787065637420536f6d6528636d5f696e29203d2066696e645f69645f696e70757428636f6c6c61746572616c5f616d6f5f69642c2074785f696e666f2e696e70757473290016330010133758601860366ea802088cc01000494ccc0694ccc068cdd79807180e9baa300e301d37546018603a6ea8004c010cc07cc038c074dd5001a5eb805288a9980da48151696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2869642e706f6c6963795f696429203f2046616c73650014a02a66603466e21200033300d37566018603a6ea8c030c074dd50009bae300e301d37540066eb8c030c074dd50018a51153301b4914e76616c75652e7175616e746974795f6f6628696e7075742e6f75747075742e76616c75652c2069642e706f6c6963795f69642c2069642e61737365745f6e616d6529203e2030203f2046616c73650014a02940dd2a400444646600200200644a66603a0022980103d87a800013232533301b300500213010330200024bd700998020020009810801180f8009bae301a301737540022a6602a0222c6032602c6ea800454cc05004058c060c054dd50008a998098078b180b980c001180b00098091baa0082301530160012223253330113007301337540022900009bad30173014375400264a666022600e60266ea8004530103d87a80001323300100137566030602a6ea8008894ccc05c004530103d87a80001323232325333017337220100042a66602e66e3c0200084c030cc070dd4000a5eb80530103d87a8000133006006003375a60320066eb8c05c008c06c008c064004c8cc004004010894ccc0580045300103d87a80001323232325333016337220100042a66602c66e3c0200084c02ccc06cdd3000a5eb80530103d87a8000133006006003375660300066eb8c058008c068008c0600048c04c004526153300d49011856616c696461746f722072657475726e65642066616c73650013656153300d49011d52756e6e696e672033206172672076616c696461746f72207370656e64001322533300e3232323253330123009301437540022a6660246644646600200200644a66603400229404c94ccc05ccdd7802180c980e8010a51133003003001301d00137566030603260326032603260326032602a6ea800cc01ccc05cc008c054dd51801180a9baa30183019301537546030602a6ea80052f5c0294454cc04d2415d70616972732e6861735f6b65792874785f696e666f2e7769746864726177616c732c20496e6c696e652873656c665f696e2e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c2929203f2046616c73650014a02a660269213e65787065637420536f6d652873656c665f696e29203d2066696e645f6f776e5f696e70757428707572706f73652c2074785f696e666f2e696e7075747329001633225333013300930153754004264646600200200644a666034002298103d87a80001323253330183375e601060366ea80080144c034cc0740092f5c0266008008002603c00460380026032602c6ea800854cc05124125657870656374205370656e64286f776e5f696e7075745f72656629203d20707572706f73650016301730180033758600260286ea80088c05c004c054004c044dd50008a4c2a6601e92011856616c696461746f722072657475726e65642066616c736500136563012300f375400a6e952000370e90011b874800054cc0280045854cc0280045854cc0280045854cc0280045924191496e636f72726563742072656465656d6572207479706520666f722076616c696461746f72207370656e642e0a2020202020202020202020202020202020202020446f75626c6520636865636b20796f7520686176652077726170706564207468652072656465656d657220747970652061732073706563696669656420696e20796f757220706c757475732e6a736f6e0049014465787065637420576974686472617746726f6d28496e6c696e652853637269707443726564656e7469616c287363726970745f68617368292929203d20707572706f7365004901aa657870656374204d657267654e65774465706f736974733a20436f6c6c61746572616c416d6f52656465656d6572203d0a2020202020206765745f69645f72656465656d6572280a2020202020202020636f6c6c61746572616c5f616d6f5f69642c0a2020202020202020547275652c0a202020202020202074785f696e666f2e696e707574732c0a202020202020202074785f696e666f2e72656465656d6572730a202020202020290049011c657870656374205b6164612c20626173655d203d20636d5f64696666005734ae7155ceaab9e5573eae815d0aba257481", "hash": "9e935267e1c88c73ff1d467acc4b9db283d16e32772413bfee2ee66c" }, + { + "title": "donate_soada.always_mint", + "redeemer": { + "title": "_r", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "parameters": [ + { + "title": "_tag", + "schema": { + "$ref": "#/definitions/Int" + } + } + ], + "compiledCode": "5901950100003232323232322322232533333300b002153330063370e900018041baa00215330074912352756e6e696e672032206172672076616c696461746f7220616c776179735f6d696e7400153330064a22930a99803a491856616c696461746f722072657475726e65642066616c73650013656153300749012452756e6e696e672033206172672076616c696461746f7220616c776179735f7370656e6400132253330084a22930a99804a4811856616c696461746f722072657475726e65642066616c73650013656300a300937540042a6600e0022c2a6600e0022c2a6600e0022c2a6600e0022c920198496e636f72726563742072656465656d6572207479706520666f722076616c696461746f7220616c776179735f7370656e642e0a2020202020202020202020202020202020202020446f75626c6520636865636b20796f7520686176652077726170706564207468652072656465656d657220747970652061732073706563696669656420696e20796f757220706c757475732e6a736f6e00375a002ae695ce2ab9d5573cae855d21", + "hash": "e768687fc9b2a1fac6f15fa5184e6a8a83e07348246f7ab81020501a" + }, + { + "title": "donate_soada.always_spend", + "datum": { + "title": "_d", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "redeemer": { + "title": "_r", + "schema": { + "$ref": "#/definitions/RedeemerWrapper$Data" + } + }, + "parameters": [ + { + "title": "_tag", + "schema": { + "$ref": "#/definitions/Int" + } + } + ], + "compiledCode": "5901950100003232323232322322232533333300b002153330063370e900018041baa00215330074912352756e6e696e672032206172672076616c696461746f7220616c776179735f6d696e7400153330064a22930a99803a491856616c696461746f722072657475726e65642066616c73650013656153300749012452756e6e696e672033206172672076616c696461746f7220616c776179735f7370656e6400132253330084a22930a99804a4811856616c696461746f722072657475726e65642066616c73650013656300a300937540042a6600e0022c2a6600e0022c2a6600e0022c2a6600e0022c920198496e636f72726563742072656465656d6572207479706520666f722076616c696461746f7220616c776179735f7370656e642e0a2020202020202020202020202020202020202020446f75626c6520636865636b20796f7520686176652077726170706564207468652072656465656d657220747970652061732073706563696669656420696e20796f757220706c757475732e6a736f6e00375a002ae695ce2ab9d5573cae855d21", + "hash": "e768687fc9b2a1fac6f15fa5184e6a8a83e07348246f7ab81020501a" + }, + { + "title": "donate_soada.mint", + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/optim~1types~1IdMintRedeemer" + } + }, + "parameters": [ + { + "title": "donation_target", + "schema": { + "$ref": "#/definitions/ByteArray" + } + }, + { + "title": "staking_amo", + "schema": { + "$ref": "#/definitions/optim~1types~1AssetClass" + } + }, + { + "title": "batch_stake", + "schema": { + "$ref": "#/definitions/ByteArray" + } + } + ], + "compiledCode": "592dd90100003232323232323232323232323232323232323223222322232533333301b002153232323232323330193001301b37540102a6603492011c52756e6e696e672032206172672076616c696461746f72206d696e740013232533301b32323232323232325333023300b302537540022646464646466664646464646464646464444a66606a603a606e6ea80604c8c8c94ccc0e0c080c0e8dd500f89919299981d299981d19198008008049129998200008a50132533303d3375e603c60806ea8c10c00801452889980180180098218008a51153303b491496c6973742e616e7928696e707574732c20666e28696e70757429207b20696e7075742e6f75747075745f7265666572656e6365203d3d206f75745f726566207d29203f2046616c73650014a02a666074646600200200a44a66608000229444c94ccc0f4c8c94ccc10c00454cc1000ec584c94ccc110c11c0084c8c94ccc108c8c8c94ccc114c0b4c11cdd500089929998232999823199119198008008019129998270008a50132533304b3371e6eb8c14400801052889980180180098288009bac304c304d304d304d304d304d304d304d304d304937540546eb8c09cc124dd50020a5115330474901386c6973742e6861732874785f696e666f2e65787472615f7369676e61746f726965732c20646174756d2e6f776e657229203f2046616c73650014a026464a666090a66609066ebccc004c00920000034c0103d879800014a22a6609292146726174696f6e616c2e636f6d7061726528726174696f6e616c2e66726f6d5f696e742830292c20646f6e6174696f6e5f726174696f29203d3d204c657373203f2046616c73650014a02a666090a66609066ebccc004c00920020034c0103d87b800014a22a6609292149726174696f6e616c2e636f6d7061726528726174696f6e616c2e66726f6d5f696e742831292c20646f6e6174696f6e5f726174696f29203d3d2047726561746572203f2046616c73650014a02a666090a66609066ebcc090c12cdd50031ba73304d3024304b37540446609a605460966ea80892f5c0294454cc125240167646174756d2e696e697469616c5f65786368616e6765203d3d20287374616b696e675f616d6f5f646174756d2e736f746f6b656e5f6261636b696e672c207374616b696e675f616d6f5f646174756d2e736f746f6b656e5f616d6f756e7429203f2046616c73650014a02a666090606666604e6eacc0a8c12cdd50059bae3029304b37540449110014a22a660929214a76616c75652e7175616e746974795f6f66286f75747075742e76616c75652c207374616b696e675f616d6f5f646174756d2e736f746f6b656e2c20222229203e2030203f2046616c73650014a029405280a502232323232533304d337100040022980103d87980001533304d337100020042980103d87b800014c103d87a8000337046eb4c148008dd69829182980199b82375a60a20046eb4c144c148004c134dd500118261baa002230303304c37500026609898010101004bd700a50304b304837540022a6608c07c2c6644a66608c605c002298103d87a80001533304633710002900009817998259817998259ba830330023304b3750606600297ae04bd7009817998259817998259ba80023304b375000297ae04bd701bad30253758604c608e6ea8008dd698131bac302630473754004a6660866056608a6ea80044c94ccc12000454cc1140f0584c8c94ccc12800454cc11c0f8584c8c94ccc13000454cc124100584c94ccc134c1400084c8c926533304e002153304b04216132325333050001153304d0441613253330513054002149854cc13811458c94cccccc15400454cc1381145854cc1381145854cc138114584dd68008a998270228b182900098290019929999998298008a998260218b0a998260218b0a998260218b09bad001153304c043163050002533304d003153304a0411613232533304f001153304c0431613253330503053002149854cc13411058c94cccccc15000454cc1341105854cc1341105854cc134110584dd68008a998268220b182880098288021929999998290008a998258210b0a998258210b0a998258210b09bad001153304b04216304f003153304a04116325333333051001153304a04116153304a04116137580022a660940822c2a660940822c609c002609c00464a66666609e0022a6609007e2c2a6609007e2c26eb000454cc1200fc5854cc1200fc58c130004c130008c94cccccc13400454cc1180f45854cc1180f45854cc1180f45854cc1180f4584dd7000982500098231baa001153304403b1632533333304b0011001153304403b16153304403b16153304403b16153304403b163301d0053756603e608a6ea809854ccc1094ccc108cdc79980a80124004012294454cc10d24013b6279746561727261792e64726f702869645f746f6b656e5f6e616d652c203129203d3d20746f6b656e5f6e616d655f7461696c203f2046616c73650014a02a6660846058002294454cc10d24011e69645f746f6b656e5f7175616e74697479203d3d2031203f2046616c73650014a029405281bad3044002375c60840022a660820782c608a002660246eacc080c104dd500080498218010998018018008a5030430011533303a533303a3370e60160086022006294454cc0ed241386c6973742e6c656e677468286f776e5f6f75747075747329203d3d20646963742e73697a65286f776e5f6d696e747329203f2046616c73650014a02a6660746601460120064604a002294454cc0ed2413a6c6973742e616c6c28646963742e76616c756573286f776e5f6d696e7473292c20666e286e29207b206e203d3d2031207d29203f2046616c73650014a029405280a503300c37286ecc0052002303e303b375403e2a66607066010600e002466e1c005200114a22a6607292013b6c6973742e616c6c28646963742e76616c756573286f776e5f6d696e7473292c20666e286e29207b206e203d3d202d31207d29203f2046616c73650014a066016646600200200844a66607a002297adef6c60132323232533303d337229101000021533303d3371e91010000210031005133042337606ea4008dd3000998030030019bab303f003375c607a0046082004607e002004646600200200844a666078002297ae013232533303a32533303b3025303d3754002266e3c018dd71820981f1baa00114a06036607a6ea8c06cc0f4dd500109981f801198020020008998020020009820001181f0009bae303b303837540302a6606c92124657870656374204d696e74286f776e5f706f6c6963795f696429203d20707572706f736500162300700122323300100100322533303800114a2264a66606a60086eb4c0ec0084cc00c00c004528181d800980080091299981a0008a4000266e0120023300200230370012233371800266e04dc680100080111192999817980c98189baa00114bd6f7b63009bab3035303237540026601c004002600200244a666060002297ae0133031302f3032001330020023033001300100122533302e0011480004cdc0240046600400460620026eb0c024c0acdd50061bac3004302b37540186eacc0b8c0bcc0bcc0bcc0bcc0acdd50062999813980798149baa001132533302c001153302901f1613232533302e001153302b02116132325333030001153302d02316132325333032001153302f02516132325333034001153303102716132325333036001153303302916132325333038001153303502b1613232533303a001153303702d16132533303b303e0021324994ccc0dcc07cc0e4dd5001899299981e0008a9981c8178b09919299981f0008a9981d8188b099299981f98210010a4c2a660780642c64a6666660860022a660780642c2a660780642c2a660780642c2a660780642c26eb8004c100004c100008c94cccccc10400454cc0e80c05854cc0e80c05854cc0e80c05854cc0e80c0584dd7000981f000981d1baa003153303802e16153303802e1632533333303f001153303802e16153303802e16153303802e16153303802e161375c0026078002607800464a66666607a00220022a6606c0582c2a6606c0582c2a6606c0582c2a6606c0582c6074002607400464a6666660760022a660680542c2a660680542c2a660680542c26eb400454cc0d00a858c0e0004c0e0008c94cccccc0e400454cc0c80a05854cc0c80a05854cc0c80a0584dd68008a998190140b181b000981b00119299999981b8008a998180130b0a998180130b0a998180130b09bad00115330300261630340013034002325333333035001153302e02416153302e02416153302e024161375a0022a6605c0482c6064002606400464a6666660660022a660580442c2a660580442c2a660580442c26eb400454cc0b008858c0c0004c0c0008c94cccccc0c400454cc0a80805854cc0a80805854cc0a80805854cc0a8080584dd7000981700098151baa001153302801e1632533333302f0011001153302801e16153302801e16153302801e16153302801e1633001300830293754605860526ea8010dd5980198149baa00a22323253330293370e90020008981798161baa002153330293013001132533302a3012302c375400226060605a6ea800454cc0ac09458c8c8cc004004014894ccc0c00045300103d87a800013232323253330303372200e0042a66606066e3c01c0084c064cc0d40052f5c0298103d87a80001330060060033032003375c6060004606800460640026eb8c0bcc0b0dd50010a998152491b536372697074206f7574707574206d697373696e6720646174756d0016302a3754002600660546ea80088c0acc0b0c0b00048c0a8c0acc0acc0acc0acc0acc0acc0acc0acc0acc0ac00454cc09006458c8cc004004dd6180298131baa00722533302800114c103d87a8000132325333026533302653330263375e600e60526ea8c01cc0a4dd5180418149baa00230123302b30073029375403297ae014a22a6604e92151696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2869642e706f6c6963795f696429203f2046616c73650014a02a66604c602266600a6eacc020c0a4dd5180418149baa002375c600e60526ea8064dd7180418149baa01914a22a6604e92014e76616c75652e7175616e746974795f6f6628696e7075742e6f75747075742e76616c75652c2069642e706f6c6963795f69642c2069642e61737365745f6e616d6529203e2030203f2046616c73650014a029404c03ccc0ac0092f5c02660080080026058004605400244464a66604a601e604e6ea8004520001375a605660506ea8004c94ccc094c03cc09cdd50008a60103d87a8000132330010013756605860526ea8008894ccc0ac004530103d87a8000132323232533302b337220100042a66605666e3c0200084c050cc0c0dd4000a5eb80530103d87a8000133006006003375a605a0066eb8c0ac008c0bc008c0b4004cc01000c00888c8cc00400400c894ccc0a0004530103d87a800013232323253330283372200e0042a66605066e3c01c0084c044cc0b4dd3000a5eb80530103d87a8000133006006003375660540066eb8c0a0008c0b0008c0a80048c0980048c094c098004c08cc090008c088004c078dd50050a4c2a660389211856616c696461746f722072657475726e65642066616c7365001365632533301b30030011325333020001153301d01616132533302130240021324994ccc074c014c07cdd500089929998110008a9980f80c0b0991929998120008a9981080d0b0992999812981400109924ca666042601260466ea800c4c94ccc09800454cc08c070584c94ccc09cc0a8008526153302401d1632533333302b001153302401d16153302401d16153302401d16153302401d161375c002605000260486ea800c54cc08806c5854cc08806c58c94cccccc0a400454cc08806c5854cc08806c5854cc08806c584dd68008a9981100d8b1813000981300119299999981380088008a9981000c8b0a9981000c8b0a9981000c8b0a9981000c8b181200098101baa001153301e01716153301e017163253333330250011001153301e01716153301e01716153301e01716153301e017163022001301e37540042a666036600a0022a66603e603c6ea8008526153301c01516153301c01516301c3754002a66666604201220122a660340262c2a660340262c2a660340262c2a660340262c2a660349211d52756e6e696e672033206172672076616c696461746f72207370656e6400133232232322533301f3232325333022300c30243754002264646464a66604c601c60506ea80044c8c8c8c8c94ccc0acc04cc0b4dd50008991919191919192999819180d181a1baa001132325333034301c303637540022646464a66606e603e60726ea80044c8c8c8c94ccc0ecc08cc0f4dd5000899191919299981f981398209baa0011323232325333043302b3045375400226464a66608a605a608e6ea80044c8c94ccc11d4ccc11ccc88c8cc00400400c894ccc13c004528099299982619b8f375c60a400400829444cc00c00c004c148004dd618269827182718271827182718271827182718251baa027375c604860946ea80b85288a998242481386c6973742e6861732874785f696e666f2e65787472615f7369676e61746f726965732c20646174756d2e6f776e657229203f2046616c73650014a02a66608e6664644464a6660986068609c6ea80044c8cdc399981299198008008021129998298008a5eb7bdb1804c8c8c8c94ccc14ccdc8a44100002153330533371e91010000210031005133058337606ea4008dd3000998030030019bab3055003375c60a600460ae00460aa0026eb8c14c004dd71829982a000a4002609e6ea8c148c13cdd50008a99826a495665787065637420536f6d65284173736574436c617373286f776e5f7363726970745f686173682c206f776e5f746f6b656e5f6e616d652929203d206765745f6f776e5f696428707572706f73652c20696e7075747329001632533304c3036304e3754002298103d87a800013232533304e30383050375400226464a6660a0607460a46ea8004530103d87a800013039330553039330553752004660aa60ac60a66ea80052f5c097ae032533305400114c103d87a8000130393305530560014bd701803992999828181d18291baa00114bd6f7b63009bab305630533754002660506eacc098c148dd5181318291baa003001375c60a860a26ea800454cc13d2415e6578706563742053637269707443726564656e7469616c286f776e5f7363726970745f6861736829203d0a20202020202020206f776e5f696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c0016302a30503754605460a06ea8c090c140dd5000982918279baa001533304b3035304d375405426466050006466ebcc0a8c140dd5000801182898271baa02a153304c49125657870656374205370656e64286f776e5f696e7075745f72656629203d20707572706f73650016300100122533304d00114bd70099827182598278009980100118280009bac3024304a375404e6eacc134c138c138c138c138c128dd50138a99982399b8900a480005288a999823a99982399b8700a375a6036056294454cc12124126746f5f646f6e6174655f62617365203d3d2072656465656d65722e337264203f2046616c73650014a02a66608e66600207601000826660026eb8c090c128dd501719b81300b00f00800214a029405280a502223232533304b533304b3375e6050609c6ea8c0a0c138dd5001981b998281ba903c4bd700a51153304c4914a6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2862617463685f7374616b6529203f2046616c73650014a02a666096a66609666e1cccc08cdd5981118271baa003375c6050609c6ea80752210000414a22a6609892015076616c75652e7175616e746974795f6f66286f75747075742e76616c75652c207374616b696e675f616d6f5f646174756d2e736f746f6b656e2c20222229203d3d20616d6f756e74203f2046616c73650014a02a666096a66609666e3cdd7181418271baa00200514a22a66098920128756e7374616b655f646174756d2e6f776e6572203d3d20726563697069656e74203f2046616c73650014a02a66609666ebcc0a0c138dd5181118271baa002303433050375200a97ae014a22a660989215f756e7374616b655f646174756d2e72657475726e5f616464726573732e7061796d656e745f63726564656e7469616c203d3d20566572696669636174696f6e4b657943726564656e7469616c28726563697069656e7429203f2046616c73650014a029405280a5032533304b3033304d3754004264a6660a00022a6609a0922c26464a6660a40022a6609e0962c264a6660a660ac004264932999827981b98289baa0011325333054001153305104d16132325333056001153305304f161325333057305a00213232498c94ccc154c0f40044c94ccc16800454cc15c14c584c94ccc16cc1780084c9263253330583040001132533305d001153305a05616132533305e3061002132498cc04000415c54cc16c15c58c94cccccc188004400454cc16c15c5854cc16c15c5854cc16c15c5854cc16c15c58c17c004c16cdd50010a99982c1821000899299982e8008a9982d02b0b09919299982f8008a9982e02c0b0991929998308008a9982f02d0b099299983118328010a4c2a660be0b62c64a6666660cc0022a660be0b62c2a660be0b62c2a660be0b62c26eb400454cc17c16c58c18c004c18c008c94cccccc19000454cc1741645854cc1741645854cc174164584dd68008a9982e82c8b183080098308011929999998310008a9982d82b8b0a9982d82b8b0a9982d82b8b09bad001153305b05716305f001305b37540042a660b20aa2c60b26ea800454cc16015058c94cccccc17c004400454cc1601505854cc1601505854cc1601505854cc16015058c170004c160dd50018a99982a981f8008a99982c982c1baa003149854cc1581485854cc15814858c158dd5001198048018280a9982a0280b19299999982d80088008a9982a0280b0a9982a0280b0a9982a0280b0a9982a0280b182c000982c00119299999982c80088008a998290270b0a998290270b0a998290270b0a998290270b182b00098291baa001153305004c16153305004c163253333330570011001153305004c16153305004c16153305004c16153305004c1630540013054002325333333055001153304e04a16153304e04a16153304e04a16153304e04a161375c00260a4002609c6ea800854cc1301205888c94ccc134c0d40044c94ccc14800454cc13c00c584c94ccc14cc1580085261533050004163253333330570011533050004161533050004161533050004161533050004161375c00260a800260a06ea800c54ccc134c0dc0044c94ccc14800454cc13c00c584c94ccc14cc1580085261533050004163253333330570011533050004161533050004161533050004161533050004161375c00260a800260a06ea800c54cc13800858c138dd500119299999982900088008a998258238b0a998258238b0a998258238b0a998258238b1980e0009bab301e304c3754052609660906ea800454cc11924014b65787065637420536f6d65286368616e67655f756e7374616b655f6f757470757429203d206c6973742e61742874785f696e666f2e6f7574707574732c2072656465656d65722e326e642900163300337586030608e6ea8090dd6980d814182498231baa001153304449014d65787065637420536f6d6528646f6e6174696f6e5f756e7374616b655f6f757470757429203d206c6973742e61742874785f696e666f2e6f7574707574732c2072656465656d65722e317374290016330013758602c608a6ea8088dd6980f813180080091129998238010a6103d87a8000132325333045302d0031302e3304a0024bd70099980280280099b8000348004c12c00cc124008dd6982298211baa00115330404901a965787065637420536f6d6528746f5f646f6e61746529203d0a2020202020206f746f6b656e5f7969656c640a20202020202020207c3e20726174696f6e616c2e6d756c28646f6e6174696f6e5f726174696f290a20202020202020207c3e20726174696f6e616c2e646976285f2c2063757272656e745f65786368616e6765290a20202020202020207c3e206f7074696f6e2e6d617028726174696f6e616c2e7472756e6361746529001632325333040302a30423754002298103d87a8000130293304537506008608c60866ea80052f5c064a666080605460846ea8004530103d87a800013029330453300900230463043375400297ae0323232533304233710004900009815998239815998239ba8302f001330473750605e00497ae04bd700a999821181680109815998239815998239ba800133047375000497ae04bd700a6103d87a8000375a608e60900046eb4c118004c108dd50069980380280198009980300200111919b84375a60880026eb4c110c114004c100dd50009820981f1baa001153303c034163300a375a602e6eb0c044c0f4dd50109bad301137586022607a6ea8084cc008c8c8c8c8c098cc108dd419b81337046eb4c10c010004cdc11bad304300200333042375066e0800c0052f5c06eb4c108c10c004c0f8dd50039bad30403041001303c375400e0026600260426607a6ea000ccc0f53010101004bd70181f181d9baa002223232302433040375066e08dd698208011bad304100133040375066e08dd6982098210011bad304130420014bd70181e9baa002303c37540042a6607092012f65787065637420536f6d65286275726e5f66656529203d20726174696f6e616c2e6e6577283939392c203130303029001633006483383d20d00f33300d3756601860706ea8c030c0e0dd50079bae30123038375400e91100303a303737540022a6606a92016465787065637420536f6d6528696e697469616c5f65786368616e676529203d20726174696f6e616c2e6e657728646174756d2e696e697469616c5f65786368616e67652e3173742c20646174756d2e696e697469616c5f65786368616e67652e326e6429001633003375a60206eb0c01cc0d8dd500d1bad300a3758600e606c6ea8068c0e0c0d4dd50008a99819a497165787065637420536f6d652863757272656e745f65786368616e676529203d20726174696f6e616c2e6e6577287374616b696e675f616d6f5f646174756d2e736f746f6b656e5f6261636b696e672c207374616b696e675f616d6f5f646174756d2e736f746f6b656e5f616d6f756e7429001633001375a600a60686ea800cdd69804181a1baa003225333032301a00114c103d87a8000153330323371000290000980d9981b980d9981b9ba8301f002330373750603e00297ae04bd700980d9981b980d9981b9ba800233037375000297ae04bd702999817980b98189baa0011325333034001153303102716132325333036001153303302916132325333038001153303502b1613232533303a001153303702d1613232533303c001153303902f1613232533303e001153303b03116132325333040001153303d03316132325333042001153303f03516132533304330460021324994ccc0fcc09cc104dd500189929998220008a9982081b8b0991929998230008a9982181c8b099299982398250010a4c2a660880742c64a6666660960022a660880742c2a660880742c2a660880742c2a660880742c26eb8004c120004c120008c94cccccc12400454cc1080e05854cc1080e05854cc1080e05854cc1080e0584dd7000982300098211baa0031533040036161533040036163253333330470011533040036161533040036161533040036161533040036161375c0026088002608800464a66666608a00220022a6607c0682c2a6607c0682c2a6607c0682c2a6607c0682c6084002608400464a6666660860022a660780642c2a660780642c2a660780642c26eb400454cc0f00c858c100004c100008c94cccccc10400454cc0e80c05854cc0e80c05854cc0e80c0584dd68008a9981d0180b181f000981f00119299999981f8008a9981c0170b0a9981c0170b0a9981c0170b09bad001153303802e16303c001303c00232533333303d001153303602c16153303602c16153303602c161375a0022a6606c0582c6074002607400464a6666660760022a660680542c2a660680542c2a660680542c26eb400454cc0d00a858c0e0004c0e0008c94cccccc0e400454cc0c80a05854cc0c80a05854cc0c80a05854cc0c80a0584dd7000981b00098191baa001153303002616325333333037001100115330300261615330300261615330300261615330300261633001300530313754606860626ea8010dd5980198189baa00e22323253330313370e90020008981b981a1baa00215333031301b0011325333032301a3034375400226070606a6ea800454cc0cc0b458c8c8cc004004014894ccc0e00045300103d87a800013232323253330383372200e0042a66607066e3c01c0084c084cc0f40052f5c0298103d87a8000133006006003303a003375c6070004607800460740026eb8c0dcc0d0dd50010a998192491b536372697074206f7574707574206d697373696e6720646174756d001630323754002600660646ea80088c0ccc0d0c0d00048c0c8c0ccc0ccc0ccc0ccc0ccc0ccc0ccc0ccc0ccc0cc00454cc0b008458cc018dd6180098169baa00a2533302b533302b3375e6010605c6ea8c020c0b8dd5180118171baa0013017330303008302e375403c97ae014a22a6605892151696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2869642e706f6c6963795f696429203f2046616c73650014a02a666056602c6660066eacc008c0b8dd5180118171baa001375c6010605c6ea8078dd7180118171baa01e14a22a6605892014e76616c75652e7175616e746974795f6f6628696e7075742e6f75747075742e76616c75652c2069642e706f6c6963795f69642c2069642e61737365745f6e616d6529203e2030203f2046616c73650014a029408c0c0c0c4004888c94ccc0b0c058c0b8dd50008a400026eb4c0c8c0bcdd5000992999816180b18171baa00114c0103d87a8000132330010013756606660606ea8008894ccc0c8004530103d87a80001323232325333032337220100042a66606466e3c0200084c06ccc0dcdd4000a5eb80530103d87a8000133006006003375a60680066eb8c0c8008c0d8008c0d0004cc01000c00888c8cc00400400c894ccc0bc004530103d87a8000132323232533302f3372200e0042a66605e66e3c01c0084c060cc0d0dd3000a5eb80530103d87a8000133006006003375660620066eb8c0bc008c0cc008c0c4004c0b0c0a4dd50008a99813a496665787065637420536f6d652873656c665f696e29203d206c6973742e66696e642874785f696e666f2e696e707574732c20666e28696e70757429207b20696e7075742e6f75747075745f7265666572656e6365203d3d2073656c665f6f75745f726566207d290016330013758600460506ea80148cdd7980198149baa00100422323300100100322533302c00114c103d87a800013232533302a3005002130133302f0024bd700998020020009818001181700091815000981418129baa001153302349124657870656374205370656e642873656c665f6f75745f72656629203d20707572706f73650016302730280023026001302237540022930a998102491856616c696461746f722072657475726e65642066616c736500136565333021001153301e01b16132325333023001153302001d16132325333025001153302201f1613253330263029002149854cc08c08058c94cccccc0a800454cc08c0805854cc08c0805854cc08c080584dd68008a998118100b181380098138011929999998140008a9981080f0b0a9981080f0b0a9981080f0b09bad001153302101e1630250013025002325333333026001153301f01c16153301f01c16153301f01c161375a0022a6603e0382c6046002a6666660480022a6603a0342c2a6603a0342c26eb000454cc0740685854cc074068594ccc068c008c070dd5000899299980f8008a9980e00d0b0991929998108008a9980f00e0b0991929998118008a9981000f0b099299981218138010991924ca66604a0042a660440402c26464a66604e0022a660480442c264a66605060560042930a998128118b1929999998160008a998128118b0a998128118b0a998128118b09bad0011533025023163029001302900332533333302a0011533023021161533023021161533023021161375a0022a660460422c604e004a6660480062a6604203e2c26464a66604c0022a660460422c264a66604e60540042930a998120110b1929999998158008a998120110b0a998120110b0a998120110b09bad001153302402216302800130280043253333330290011533022020161533022020161533022020161375a0022a660440402c604c0062a6604203e2c64a6666660500022a6604203e2c2a6604203e2c26eb000454cc08407c5854cc08407c58c094004c094008c94cccccc09800454cc07c0745854cc07c074584dd60008a9980f80e8b0a9980f80e8b181180098118011929999998120008a9980e80d8b0a9980e80d8b0a9980e80d8b0a9980e80d8b09bae0013021001301d37540022a660360322ca66666604201220122a660340302c2a660340302c2a660340302c2a660340302c603e60386ea8020dc3a40006e952000370e90011b8848000dd2a40046e052000153301400116153301400116153301400116153301400116490191496e636f72726563742072656465656d6572207479706520666f722076616c696461746f72207370656e642e0a2020202020202020202020202020202020202020446f75626c6520636865636b20796f7520686176652077726170706564207468652072656465656d657220747970652061732073706563696669656420696e20796f757220706c757475732e6a736f6e00375c0026eb800524015965787065637420536f6d65287374616b696e675f616d6f5f7265666572656e636529203d2066696e645f69645f696e707574287374616b696e675f616d6f2c2074785f696e666f2e7265666572656e63655f696e70757473290049016a657870656374207374616b696e675f616d6f5f646174756d3a205374616b696e67416d6f446174756d203d206765745f6f75747075745f646174756d287374616b696e675f616d6f5f7265666572656e63652e6f75747075742c2074785f696e666f2e646174756d73290049014665787065637420646174756d3a20446f6e6174696f6e446174756d203d206765745f6f75747075745f646174756d286f75747075742c2074785f696e666f2e646174756d73290049015e65787065637420536f6d6528646f6e6174696f6e5f726174696f29203d20726174696f6e616c2e6e657728646174756d2e646f6e6174696f6e5f726174696f2e3173742c20646174756d2e646f6e6174696f6e5f726174696f2e326e64290049011872656465656d65723a2049644d696e7452656465656d65720049013065787065637420536f6d65286461746129203d20646963742e67657428646174756d732c20646174756d5f686173682900490170657870656374205b506169722869645f746f6b656e5f6e616d652c2069645f746f6b656e5f7175616e74697479295d203d0a202020202020646963742e746f5f70616972732876616c75652e746f6b656e73286f75747075742e76616c75652c206f776e5f706f6c6963795f696429290049015065787065637420756e7374616b655f646174756d3a2042617463685374616b65446174756d203d206765745f6f75747075745f646174756d286f75747075742c2074785f696e666f2e646174756d73290049011972656465656d65723a2028496e742c20496e742c20496e742900490114646174756d3a20446f6e6174696f6e446174756d005734ae7155ceaab9e5573eae815d0aba257481", + "hash": "52aa9af3e11b2640a59540212dac83bc82f0310f99c15d8754aac8cf" + }, + { + "title": "donate_soada.spend", + "datum": { + "title": "datum", + "schema": { + "$ref": "#/definitions/donate_soada~1DonationDatum" + } + }, + "redeemer": { + "title": "redeemer", + "schema": { + "$ref": "#/definitions/RedeemerWrapper$Tuple$Int_Int_Int" + } + }, + "parameters": [ + { + "title": "donation_target", + "schema": { + "$ref": "#/definitions/ByteArray" + } + }, + { + "title": "staking_amo", + "schema": { + "$ref": "#/definitions/optim~1types~1AssetClass" + } + }, + { + "title": "batch_stake", + "schema": { + "$ref": "#/definitions/ByteArray" + } + } + ], + "compiledCode": "592dd90100003232323232323232323232323232323232323223222322232533333301b002153232323232323330193001301b37540102a6603492011c52756e6e696e672032206172672076616c696461746f72206d696e740013232533301b32323232323232325333023300b302537540022646464646466664646464646464646464444a66606a603a606e6ea80604c8c8c94ccc0e0c080c0e8dd500f89919299981d299981d19198008008049129998200008a50132533303d3375e603c60806ea8c10c00801452889980180180098218008a51153303b491496c6973742e616e7928696e707574732c20666e28696e70757429207b20696e7075742e6f75747075745f7265666572656e6365203d3d206f75745f726566207d29203f2046616c73650014a02a666074646600200200a44a66608000229444c94ccc0f4c8c94ccc10c00454cc1000ec584c94ccc110c11c0084c8c94ccc108c8c8c94ccc114c0b4c11cdd500089929998232999823199119198008008019129998270008a50132533304b3371e6eb8c14400801052889980180180098288009bac304c304d304d304d304d304d304d304d304d304937540546eb8c09cc124dd50020a5115330474901386c6973742e6861732874785f696e666f2e65787472615f7369676e61746f726965732c20646174756d2e6f776e657229203f2046616c73650014a026464a666090a66609066ebccc004c00920000034c0103d879800014a22a6609292146726174696f6e616c2e636f6d7061726528726174696f6e616c2e66726f6d5f696e742830292c20646f6e6174696f6e5f726174696f29203d3d204c657373203f2046616c73650014a02a666090a66609066ebccc004c00920020034c0103d87b800014a22a6609292149726174696f6e616c2e636f6d7061726528726174696f6e616c2e66726f6d5f696e742831292c20646f6e6174696f6e5f726174696f29203d3d2047726561746572203f2046616c73650014a02a666090a66609066ebcc090c12cdd50031ba73304d3024304b37540446609a605460966ea80892f5c0294454cc125240167646174756d2e696e697469616c5f65786368616e6765203d3d20287374616b696e675f616d6f5f646174756d2e736f746f6b656e5f6261636b696e672c207374616b696e675f616d6f5f646174756d2e736f746f6b656e5f616d6f756e7429203f2046616c73650014a02a666090606666604e6eacc0a8c12cdd50059bae3029304b37540449110014a22a660929214a76616c75652e7175616e746974795f6f66286f75747075742e76616c75652c207374616b696e675f616d6f5f646174756d2e736f746f6b656e2c20222229203e2030203f2046616c73650014a029405280a502232323232533304d337100040022980103d87980001533304d337100020042980103d87b800014c103d87a8000337046eb4c148008dd69829182980199b82375a60a20046eb4c144c148004c134dd500118261baa002230303304c37500026609898010101004bd700a50304b304837540022a6608c07c2c6644a66608c605c002298103d87a80001533304633710002900009817998259817998259ba830330023304b3750606600297ae04bd7009817998259817998259ba80023304b375000297ae04bd701bad30253758604c608e6ea8008dd698131bac302630473754004a6660866056608a6ea80044c94ccc12000454cc1140f0584c8c94ccc12800454cc11c0f8584c8c94ccc13000454cc124100584c94ccc134c1400084c8c926533304e002153304b04216132325333050001153304d0441613253330513054002149854cc13811458c94cccccc15400454cc1381145854cc1381145854cc138114584dd68008a998270228b182900098290019929999998298008a998260218b0a998260218b0a998260218b09bad001153304c043163050002533304d003153304a0411613232533304f001153304c0431613253330503053002149854cc13411058c94cccccc15000454cc1341105854cc1341105854cc134110584dd68008a998268220b182880098288021929999998290008a998258210b0a998258210b0a998258210b09bad001153304b04216304f003153304a04116325333333051001153304a04116153304a04116137580022a660940822c2a660940822c609c002609c00464a66666609e0022a6609007e2c2a6609007e2c26eb000454cc1200fc5854cc1200fc58c130004c130008c94cccccc13400454cc1180f45854cc1180f45854cc1180f45854cc1180f4584dd7000982500098231baa001153304403b1632533333304b0011001153304403b16153304403b16153304403b16153304403b163301d0053756603e608a6ea809854ccc1094ccc108cdc79980a80124004012294454cc10d24013b6279746561727261792e64726f702869645f746f6b656e5f6e616d652c203129203d3d20746f6b656e5f6e616d655f7461696c203f2046616c73650014a02a6660846058002294454cc10d24011e69645f746f6b656e5f7175616e74697479203d3d2031203f2046616c73650014a029405281bad3044002375c60840022a660820782c608a002660246eacc080c104dd500080498218010998018018008a5030430011533303a533303a3370e60160086022006294454cc0ed241386c6973742e6c656e677468286f776e5f6f75747075747329203d3d20646963742e73697a65286f776e5f6d696e747329203f2046616c73650014a02a6660746601460120064604a002294454cc0ed2413a6c6973742e616c6c28646963742e76616c756573286f776e5f6d696e7473292c20666e286e29207b206e203d3d2031207d29203f2046616c73650014a029405280a503300c37286ecc0052002303e303b375403e2a66607066010600e002466e1c005200114a22a6607292013b6c6973742e616c6c28646963742e76616c756573286f776e5f6d696e7473292c20666e286e29207b206e203d3d202d31207d29203f2046616c73650014a066016646600200200844a66607a002297adef6c60132323232533303d337229101000021533303d3371e91010000210031005133042337606ea4008dd3000998030030019bab303f003375c607a0046082004607e002004646600200200844a666078002297ae013232533303a32533303b3025303d3754002266e3c018dd71820981f1baa00114a06036607a6ea8c06cc0f4dd500109981f801198020020008998020020009820001181f0009bae303b303837540302a6606c92124657870656374204d696e74286f776e5f706f6c6963795f696429203d20707572706f736500162300700122323300100100322533303800114a2264a66606a60086eb4c0ec0084cc00c00c004528181d800980080091299981a0008a4000266e0120023300200230370012233371800266e04dc680100080111192999817980c98189baa00114bd6f7b63009bab3035303237540026601c004002600200244a666060002297ae0133031302f3032001330020023033001300100122533302e0011480004cdc0240046600400460620026eb0c024c0acdd50061bac3004302b37540186eacc0b8c0bcc0bcc0bcc0bcc0acdd50062999813980798149baa001132533302c001153302901f1613232533302e001153302b02116132325333030001153302d02316132325333032001153302f02516132325333034001153303102716132325333036001153303302916132325333038001153303502b1613232533303a001153303702d16132533303b303e0021324994ccc0dcc07cc0e4dd5001899299981e0008a9981c8178b09919299981f0008a9981d8188b099299981f98210010a4c2a660780642c64a6666660860022a660780642c2a660780642c2a660780642c2a660780642c26eb8004c100004c100008c94cccccc10400454cc0e80c05854cc0e80c05854cc0e80c05854cc0e80c0584dd7000981f000981d1baa003153303802e16153303802e1632533333303f001153303802e16153303802e16153303802e16153303802e161375c0026078002607800464a66666607a00220022a6606c0582c2a6606c0582c2a6606c0582c2a6606c0582c6074002607400464a6666660760022a660680542c2a660680542c2a660680542c26eb400454cc0d00a858c0e0004c0e0008c94cccccc0e400454cc0c80a05854cc0c80a05854cc0c80a0584dd68008a998190140b181b000981b00119299999981b8008a998180130b0a998180130b0a998180130b09bad00115330300261630340013034002325333333035001153302e02416153302e02416153302e024161375a0022a6605c0482c6064002606400464a6666660660022a660580442c2a660580442c2a660580442c26eb400454cc0b008858c0c0004c0c0008c94cccccc0c400454cc0a80805854cc0a80805854cc0a80805854cc0a8080584dd7000981700098151baa001153302801e1632533333302f0011001153302801e16153302801e16153302801e16153302801e1633001300830293754605860526ea8010dd5980198149baa00a22323253330293370e90020008981798161baa002153330293013001132533302a3012302c375400226060605a6ea800454cc0ac09458c8c8cc004004014894ccc0c00045300103d87a800013232323253330303372200e0042a66606066e3c01c0084c064cc0d40052f5c0298103d87a80001330060060033032003375c6060004606800460640026eb8c0bcc0b0dd50010a998152491b536372697074206f7574707574206d697373696e6720646174756d0016302a3754002600660546ea80088c0acc0b0c0b00048c0a8c0acc0acc0acc0acc0acc0acc0acc0acc0acc0ac00454cc09006458c8cc004004dd6180298131baa00722533302800114c103d87a8000132325333026533302653330263375e600e60526ea8c01cc0a4dd5180418149baa00230123302b30073029375403297ae014a22a6604e92151696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2869642e706f6c6963795f696429203f2046616c73650014a02a66604c602266600a6eacc020c0a4dd5180418149baa002375c600e60526ea8064dd7180418149baa01914a22a6604e92014e76616c75652e7175616e746974795f6f6628696e7075742e6f75747075742e76616c75652c2069642e706f6c6963795f69642c2069642e61737365745f6e616d6529203e2030203f2046616c73650014a029404c03ccc0ac0092f5c02660080080026058004605400244464a66604a601e604e6ea8004520001375a605660506ea8004c94ccc094c03cc09cdd50008a60103d87a8000132330010013756605860526ea8008894ccc0ac004530103d87a8000132323232533302b337220100042a66605666e3c0200084c050cc0c0dd4000a5eb80530103d87a8000133006006003375a605a0066eb8c0ac008c0bc008c0b4004cc01000c00888c8cc00400400c894ccc0a0004530103d87a800013232323253330283372200e0042a66605066e3c01c0084c044cc0b4dd3000a5eb80530103d87a8000133006006003375660540066eb8c0a0008c0b0008c0a80048c0980048c094c098004c08cc090008c088004c078dd50050a4c2a660389211856616c696461746f722072657475726e65642066616c7365001365632533301b30030011325333020001153301d01616132533302130240021324994ccc074c014c07cdd500089929998110008a9980f80c0b0991929998120008a9981080d0b0992999812981400109924ca666042601260466ea800c4c94ccc09800454cc08c070584c94ccc09cc0a8008526153302401d1632533333302b001153302401d16153302401d16153302401d16153302401d161375c002605000260486ea800c54cc08806c5854cc08806c58c94cccccc0a400454cc08806c5854cc08806c5854cc08806c584dd68008a9981100d8b1813000981300119299999981380088008a9981000c8b0a9981000c8b0a9981000c8b0a9981000c8b181200098101baa001153301e01716153301e017163253333330250011001153301e01716153301e01716153301e01716153301e017163022001301e37540042a666036600a0022a66603e603c6ea8008526153301c01516153301c01516301c3754002a66666604201220122a660340262c2a660340262c2a660340262c2a660340262c2a660349211d52756e6e696e672033206172672076616c696461746f72207370656e6400133232232322533301f3232325333022300c30243754002264646464a66604c601c60506ea80044c8c8c8c8c94ccc0acc04cc0b4dd50008991919191919192999819180d181a1baa001132325333034301c303637540022646464a66606e603e60726ea80044c8c8c8c94ccc0ecc08cc0f4dd5000899191919299981f981398209baa0011323232325333043302b3045375400226464a66608a605a608e6ea80044c8c94ccc11d4ccc11ccc88c8cc00400400c894ccc13c004528099299982619b8f375c60a400400829444cc00c00c004c148004dd618269827182718271827182718271827182718251baa027375c604860946ea80b85288a998242481386c6973742e6861732874785f696e666f2e65787472615f7369676e61746f726965732c20646174756d2e6f776e657229203f2046616c73650014a02a66608e6664644464a6660986068609c6ea80044c8cdc399981299198008008021129998298008a5eb7bdb1804c8c8c8c94ccc14ccdc8a44100002153330533371e91010000210031005133058337606ea4008dd3000998030030019bab3055003375c60a600460ae00460aa0026eb8c14c004dd71829982a000a4002609e6ea8c148c13cdd50008a99826a495665787065637420536f6d65284173736574436c617373286f776e5f7363726970745f686173682c206f776e5f746f6b656e5f6e616d652929203d206765745f6f776e5f696428707572706f73652c20696e7075747329001632533304c3036304e3754002298103d87a800013232533304e30383050375400226464a6660a0607460a46ea8004530103d87a800013039330553039330553752004660aa60ac60a66ea80052f5c097ae032533305400114c103d87a8000130393305530560014bd701803992999828181d18291baa00114bd6f7b63009bab305630533754002660506eacc098c148dd5181318291baa003001375c60a860a26ea800454cc13d2415e6578706563742053637269707443726564656e7469616c286f776e5f7363726970745f6861736829203d0a20202020202020206f776e5f696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c0016302a30503754605460a06ea8c090c140dd5000982918279baa001533304b3035304d375405426466050006466ebcc0a8c140dd5000801182898271baa02a153304c49125657870656374205370656e64286f776e5f696e7075745f72656629203d20707572706f73650016300100122533304d00114bd70099827182598278009980100118280009bac3024304a375404e6eacc134c138c138c138c138c128dd50138a99982399b8900a480005288a999823a99982399b8700a375a6036056294454cc12124126746f5f646f6e6174655f62617365203d3d2072656465656d65722e337264203f2046616c73650014a02a66608e66600207601000826660026eb8c090c128dd501719b81300b00f00800214a029405280a502223232533304b533304b3375e6050609c6ea8c0a0c138dd5001981b998281ba903c4bd700a51153304c4914a6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2862617463685f7374616b6529203f2046616c73650014a02a666096a66609666e1cccc08cdd5981118271baa003375c6050609c6ea80752210000414a22a6609892015076616c75652e7175616e746974795f6f66286f75747075742e76616c75652c207374616b696e675f616d6f5f646174756d2e736f746f6b656e2c20222229203d3d20616d6f756e74203f2046616c73650014a02a666096a66609666e3cdd7181418271baa00200514a22a66098920128756e7374616b655f646174756d2e6f776e6572203d3d20726563697069656e74203f2046616c73650014a02a66609666ebcc0a0c138dd5181118271baa002303433050375200a97ae014a22a660989215f756e7374616b655f646174756d2e72657475726e5f616464726573732e7061796d656e745f63726564656e7469616c203d3d20566572696669636174696f6e4b657943726564656e7469616c28726563697069656e7429203f2046616c73650014a029405280a5032533304b3033304d3754004264a6660a00022a6609a0922c26464a6660a40022a6609e0962c264a6660a660ac004264932999827981b98289baa0011325333054001153305104d16132325333056001153305304f161325333057305a00213232498c94ccc154c0f40044c94ccc16800454cc15c14c584c94ccc16cc1780084c9263253330583040001132533305d001153305a05616132533305e3061002132498cc04000415c54cc16c15c58c94cccccc188004400454cc16c15c5854cc16c15c5854cc16c15c5854cc16c15c58c17c004c16cdd50010a99982c1821000899299982e8008a9982d02b0b09919299982f8008a9982e02c0b0991929998308008a9982f02d0b099299983118328010a4c2a660be0b62c64a6666660cc0022a660be0b62c2a660be0b62c2a660be0b62c26eb400454cc17c16c58c18c004c18c008c94cccccc19000454cc1741645854cc1741645854cc174164584dd68008a9982e82c8b183080098308011929999998310008a9982d82b8b0a9982d82b8b0a9982d82b8b09bad001153305b05716305f001305b37540042a660b20aa2c60b26ea800454cc16015058c94cccccc17c004400454cc1601505854cc1601505854cc1601505854cc16015058c170004c160dd50018a99982a981f8008a99982c982c1baa003149854cc1581485854cc15814858c158dd5001198048018280a9982a0280b19299999982d80088008a9982a0280b0a9982a0280b0a9982a0280b0a9982a0280b182c000982c00119299999982c80088008a998290270b0a998290270b0a998290270b0a998290270b182b00098291baa001153305004c16153305004c163253333330570011001153305004c16153305004c16153305004c16153305004c1630540013054002325333333055001153304e04a16153304e04a16153304e04a16153304e04a161375c00260a4002609c6ea800854cc1301205888c94ccc134c0d40044c94ccc14800454cc13c00c584c94ccc14cc1580085261533050004163253333330570011533050004161533050004161533050004161533050004161375c00260a800260a06ea800c54ccc134c0dc0044c94ccc14800454cc13c00c584c94ccc14cc1580085261533050004163253333330570011533050004161533050004161533050004161533050004161375c00260a800260a06ea800c54cc13800858c138dd500119299999982900088008a998258238b0a998258238b0a998258238b0a998258238b1980e0009bab301e304c3754052609660906ea800454cc11924014b65787065637420536f6d65286368616e67655f756e7374616b655f6f757470757429203d206c6973742e61742874785f696e666f2e6f7574707574732c2072656465656d65722e326e642900163300337586030608e6ea8090dd6980d814182498231baa001153304449014d65787065637420536f6d6528646f6e6174696f6e5f756e7374616b655f6f757470757429203d206c6973742e61742874785f696e666f2e6f7574707574732c2072656465656d65722e317374290016330013758602c608a6ea8088dd6980f813180080091129998238010a6103d87a8000132325333045302d0031302e3304a0024bd70099980280280099b8000348004c12c00cc124008dd6982298211baa00115330404901a965787065637420536f6d6528746f5f646f6e61746529203d0a2020202020206f746f6b656e5f7969656c640a20202020202020207c3e20726174696f6e616c2e6d756c28646f6e6174696f6e5f726174696f290a20202020202020207c3e20726174696f6e616c2e646976285f2c2063757272656e745f65786368616e6765290a20202020202020207c3e206f7074696f6e2e6d617028726174696f6e616c2e7472756e6361746529001632325333040302a30423754002298103d87a8000130293304537506008608c60866ea80052f5c064a666080605460846ea8004530103d87a800013029330453300900230463043375400297ae0323232533304233710004900009815998239815998239ba8302f001330473750605e00497ae04bd700a999821181680109815998239815998239ba800133047375000497ae04bd700a6103d87a8000375a608e60900046eb4c118004c108dd50069980380280198009980300200111919b84375a60880026eb4c110c114004c100dd50009820981f1baa001153303c034163300a375a602e6eb0c044c0f4dd50109bad301137586022607a6ea8084cc008c8c8c8c8c098cc108dd419b81337046eb4c10c010004cdc11bad304300200333042375066e0800c0052f5c06eb4c108c10c004c0f8dd50039bad30403041001303c375400e0026600260426607a6ea000ccc0f53010101004bd70181f181d9baa002223232302433040375066e08dd698208011bad304100133040375066e08dd6982098210011bad304130420014bd70181e9baa002303c37540042a6607092012f65787065637420536f6d65286275726e5f66656529203d20726174696f6e616c2e6e6577283939392c203130303029001633006483383d20d00f33300d3756601860706ea8c030c0e0dd50079bae30123038375400e91100303a303737540022a6606a92016465787065637420536f6d6528696e697469616c5f65786368616e676529203d20726174696f6e616c2e6e657728646174756d2e696e697469616c5f65786368616e67652e3173742c20646174756d2e696e697469616c5f65786368616e67652e326e6429001633003375a60206eb0c01cc0d8dd500d1bad300a3758600e606c6ea8068c0e0c0d4dd50008a99819a497165787065637420536f6d652863757272656e745f65786368616e676529203d20726174696f6e616c2e6e6577287374616b696e675f616d6f5f646174756d2e736f746f6b656e5f6261636b696e672c207374616b696e675f616d6f5f646174756d2e736f746f6b656e5f616d6f756e7429001633001375a600a60686ea800cdd69804181a1baa003225333032301a00114c103d87a8000153330323371000290000980d9981b980d9981b9ba8301f002330373750603e00297ae04bd700980d9981b980d9981b9ba800233037375000297ae04bd702999817980b98189baa0011325333034001153303102716132325333036001153303302916132325333038001153303502b1613232533303a001153303702d1613232533303c001153303902f1613232533303e001153303b03116132325333040001153303d03316132325333042001153303f03516132533304330460021324994ccc0fcc09cc104dd500189929998220008a9982081b8b0991929998230008a9982181c8b099299982398250010a4c2a660880742c64a6666660960022a660880742c2a660880742c2a660880742c2a660880742c26eb8004c120004c120008c94cccccc12400454cc1080e05854cc1080e05854cc1080e05854cc1080e0584dd7000982300098211baa0031533040036161533040036163253333330470011533040036161533040036161533040036161533040036161375c0026088002608800464a66666608a00220022a6607c0682c2a6607c0682c2a6607c0682c2a6607c0682c6084002608400464a6666660860022a660780642c2a660780642c2a660780642c26eb400454cc0f00c858c100004c100008c94cccccc10400454cc0e80c05854cc0e80c05854cc0e80c0584dd68008a9981d0180b181f000981f00119299999981f8008a9981c0170b0a9981c0170b0a9981c0170b09bad001153303802e16303c001303c00232533333303d001153303602c16153303602c16153303602c161375a0022a6606c0582c6074002607400464a6666660760022a660680542c2a660680542c2a660680542c26eb400454cc0d00a858c0e0004c0e0008c94cccccc0e400454cc0c80a05854cc0c80a05854cc0c80a05854cc0c80a0584dd7000981b00098191baa001153303002616325333333037001100115330300261615330300261615330300261615330300261633001300530313754606860626ea8010dd5980198189baa00e22323253330313370e90020008981b981a1baa00215333031301b0011325333032301a3034375400226070606a6ea800454cc0cc0b458c8c8cc004004014894ccc0e00045300103d87a800013232323253330383372200e0042a66607066e3c01c0084c084cc0f40052f5c0298103d87a8000133006006003303a003375c6070004607800460740026eb8c0dcc0d0dd50010a998192491b536372697074206f7574707574206d697373696e6720646174756d001630323754002600660646ea80088c0ccc0d0c0d00048c0c8c0ccc0ccc0ccc0ccc0ccc0ccc0ccc0ccc0ccc0cc00454cc0b008458cc018dd6180098169baa00a2533302b533302b3375e6010605c6ea8c020c0b8dd5180118171baa0013017330303008302e375403c97ae014a22a6605892151696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c2869642e706f6c6963795f696429203f2046616c73650014a02a666056602c6660066eacc008c0b8dd5180118171baa001375c6010605c6ea8078dd7180118171baa01e14a22a6605892014e76616c75652e7175616e746974795f6f6628696e7075742e6f75747075742e76616c75652c2069642e706f6c6963795f69642c2069642e61737365745f6e616d6529203e2030203f2046616c73650014a029408c0c0c0c4004888c94ccc0b0c058c0b8dd50008a400026eb4c0c8c0bcdd5000992999816180b18171baa00114c0103d87a8000132330010013756606660606ea8008894ccc0c8004530103d87a80001323232325333032337220100042a66606466e3c0200084c06ccc0dcdd4000a5eb80530103d87a8000133006006003375a60680066eb8c0c8008c0d8008c0d0004cc01000c00888c8cc00400400c894ccc0bc004530103d87a8000132323232533302f3372200e0042a66605e66e3c01c0084c060cc0d0dd3000a5eb80530103d87a8000133006006003375660620066eb8c0bc008c0cc008c0c4004c0b0c0a4dd50008a99813a496665787065637420536f6d652873656c665f696e29203d206c6973742e66696e642874785f696e666f2e696e707574732c20666e28696e70757429207b20696e7075742e6f75747075745f7265666572656e6365203d3d2073656c665f6f75745f726566207d290016330013758600460506ea80148cdd7980198149baa00100422323300100100322533302c00114c103d87a800013232533302a3005002130133302f0024bd700998020020009818001181700091815000981418129baa001153302349124657870656374205370656e642873656c665f6f75745f72656629203d20707572706f73650016302730280023026001302237540022930a998102491856616c696461746f722072657475726e65642066616c736500136565333021001153301e01b16132325333023001153302001d16132325333025001153302201f1613253330263029002149854cc08c08058c94cccccc0a800454cc08c0805854cc08c0805854cc08c080584dd68008a998118100b181380098138011929999998140008a9981080f0b0a9981080f0b0a9981080f0b09bad001153302101e1630250013025002325333333026001153301f01c16153301f01c16153301f01c161375a0022a6603e0382c6046002a6666660480022a6603a0342c2a6603a0342c26eb000454cc0740685854cc074068594ccc068c008c070dd5000899299980f8008a9980e00d0b0991929998108008a9980f00e0b0991929998118008a9981000f0b099299981218138010991924ca66604a0042a660440402c26464a66604e0022a660480442c264a66605060560042930a998128118b1929999998160008a998128118b0a998128118b0a998128118b09bad0011533025023163029001302900332533333302a0011533023021161533023021161533023021161375a0022a660460422c604e004a6660480062a6604203e2c26464a66604c0022a660460422c264a66604e60540042930a998120110b1929999998158008a998120110b0a998120110b0a998120110b09bad001153302402216302800130280043253333330290011533022020161533022020161533022020161375a0022a660440402c604c0062a6604203e2c64a6666660500022a6604203e2c2a6604203e2c26eb000454cc08407c5854cc08407c58c094004c094008c94cccccc09800454cc07c0745854cc07c074584dd60008a9980f80e8b0a9980f80e8b181180098118011929999998120008a9980e80d8b0a9980e80d8b0a9980e80d8b0a9980e80d8b09bae0013021001301d37540022a660360322ca66666604201220122a660340302c2a660340302c2a660340302c2a660340302c603e60386ea8020dc3a40006e952000370e90011b8848000dd2a40046e052000153301400116153301400116153301400116153301400116490191496e636f72726563742072656465656d6572207479706520666f722076616c696461746f72207370656e642e0a2020202020202020202020202020202020202020446f75626c6520636865636b20796f7520686176652077726170706564207468652072656465656d657220747970652061732073706563696669656420696e20796f757220706c757475732e6a736f6e00375c0026eb800524015965787065637420536f6d65287374616b696e675f616d6f5f7265666572656e636529203d2066696e645f69645f696e707574287374616b696e675f616d6f2c2074785f696e666f2e7265666572656e63655f696e70757473290049016a657870656374207374616b696e675f616d6f5f646174756d3a205374616b696e67416d6f446174756d203d206765745f6f75747075745f646174756d287374616b696e675f616d6f5f7265666572656e63652e6f75747075742c2074785f696e666f2e646174756d73290049014665787065637420646174756d3a20446f6e6174696f6e446174756d203d206765745f6f75747075745f646174756d286f75747075742c2074785f696e666f2e646174756d73290049015e65787065637420536f6d6528646f6e6174696f6e5f726174696f29203d20726174696f6e616c2e6e657728646174756d2e646f6e6174696f6e5f726174696f2e3173742c20646174756d2e646f6e6174696f6e5f726174696f2e326e64290049011872656465656d65723a2049644d696e7452656465656d65720049013065787065637420536f6d65286461746129203d20646963742e67657428646174756d732c20646174756d5f686173682900490170657870656374205b506169722869645f746f6b656e5f6e616d652c2069645f746f6b656e5f7175616e74697479295d203d0a202020202020646963742e746f5f70616972732876616c75652e746f6b656e73286f75747075742e76616c75652c206f776e5f706f6c6963795f696429290049015065787065637420756e7374616b655f646174756d3a2042617463685374616b65446174756d203d206765745f6f75747075745f646174756d286f75747075742c2074785f696e666f2e646174756d73290049011972656465656d65723a2028496e742c20496e742c20496e742900490114646174756d3a20446f6e6174696f6e446174756d005734ae7155ceaab9e5573eae815d0aba257481", + "hash": "52aa9af3e11b2640a59540212dac83bc82f0310f99c15d8754aac8cf" + }, + { + "title": "donate_soada.mint_nft", + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "parameters": [ + { + "title": "donation_script", + "schema": { + "$ref": "#/definitions/ByteArray" + } + }, + { + "title": "reference_holder", + "schema": { + "$ref": "#/definitions/aiken~1transaction~1credential~1Credential" + } + }, + { + "title": "nft_name", + "schema": { + "$ref": "#/definitions/Data" + } + }, + { + "title": "nft_description", + "schema": { + "$ref": "#/definitions/Data" + } + }, + { + "title": "nft_image_url", + "schema": { + "$ref": "#/definitions/Data" + } + } + ], + "compiledCode": "590cac010000323232323232323232323232232222222533300c32323253323301030013012375400426464646464646464a6660380022a6603202a2c264a66603a60400042646464646464a66603e602060426ea80044c8c8c8c8c94ccc0914ccc090cdc424000012294454cc09524113646f6e61746564203e2030203f2046616c73650014a02a666048a66604866ebcdd30081ba633029337606ea402130101010033029337606ea401d3010101004bd6f7b6300a5115330254914f6f776e5f746f6b656e73203d3d205b50616972287265666572656e63655f746f6b656e5f6e616d652c2031292c205061697228757365725f746f6b656e5f6e616d652c2031295d203f2046616c73650014a02a666048a66604866ebc0100085288a99812a481397265666572656e63655f746f6b656e5f646174756d203d3d2065787065637465645f7265666572656e63655f646174756d203f2046616c73650014a02a666048002294454cc09524011e7265666572656e63655f746f6b656e5f6c6f636b6564203f2046616c73650014a029405280a503375e601a604c6ea8c034c098dd500200e9808998139ba6330273376098010c4b6465736372697074696f6e0001a33027337609810847646f6e6174656400375000e6604e66ec13010645696d61676500019330273376098105446e616d650001b3302733760981054474696d650037506464a6660486022604c6ea80044c8c94ccc098c04cc0a0dd5000899299981399b8733706006904078592919b8300148203c2c948400454cc0a12411b6578706563742073746172745f646179203d3d20656e645f6461790016375a605860526ea800454cc09d24013f6578706563742046696e69746528656e645f736c6f7429203d2076616c69646974795f72616e67652e75707065725f626f756e642e626f756e645f747970650016300f30283754602060506ea800cdd6981518139baa00115330254901416578706563742046696e6974652873746172745f736c6f7429203d2076616c69646974795f72616e67652e6c6f7765725f626f756e642e626f756e645f747970650016300d30263754601a604c6ea8004c0a0c0a4c0a4c0a4c0a4c0a4c0a4c0a4c094dd500aa5eb7bdb180cc09d301010100330274c103d87980004bd702999810980918119baa001132533302600115330230211613232533302800115330250231613232533302a001153302702516132533302b302e002132498c8cc004004018894ccc0b40045261323300300330310023232302e002325333333033001153302c02a16153302c02a16153302c02a16153302c02a161375c0026058002605e0022a6605004c2c64a66666605e0022a66604e603060526ea800454ccc0acc0a8dd50008a4c2a6605004c2c2a6605004c2c2a6605004c2c2a6605004c2c2a6605004c2c2a6605004c2c6058002605800464a66666605a0022a6604c0482c2a6604c0482c2a6604c0482c26eb400454cc09809058c0a8004c0a8008c94cccccc0ac00454cc090088584dd58008a998120110b0a998120110b0a998120110b181400098121baa001153302202016325333333029001100115330220201615330220201615330220201615330220201632323253330233370e90020008981498131baa002153330233010001132533302430153026375400226054604e6ea800454cc0952413065787065637420536f6d65286461746129203d20646963742e67657428646174756d732c20646174756d5f6861736829001632323300100100522533302a00114c0103d87a8000132323232533302a3372200e0042a66605466e3c01c0084c064cc0bc0052f5c0298103d87a8000133006006003302c003375c6054004605c00460580026eb8c0a4c098dd50010a998122491b536372697074206f7574707574206d697373696e6720646174756d001630243754002600e60486ea8008dd59813181398139813981398139813981398139813981398119baa0133025302237540022a660409201a865787065637420536f6d65287265666572656e63655f746f6b656e5f6f757470757429203d206c6973742e66696e64280a20202020202074785f696e666f2e6f7574707574732c0a202020202020666e286f757470757429207b2076616c75652e7175616e746974795f6f66286f75747075742e76616c75652c20706f6c6963795f69642c207265666572656e63655f746f6b656e5f6e616d6529203d3d2031207d0a2020202029001632330010013758600a60446ea8048894ccc0900045300103d87a8000132325333022300f3253330233010302537540022900009bad30293026375400264a6660466020604a6ea80045300103d87a80001323300100137566054604e6ea8008894ccc0a4004530103d87a800013232323253330293372201a0042a66605266e3c0340084c060cc0b8dd4000a5eb80530103d87a8000133006006003375a60560066eb8c0a4008c0b4008c0ac004cc040dd5980698129baa00201213011330270024bd700998020020009814001181300099b8a48904000de1400000433714910104000643b00000332533301c300d301e375400226464a66603c601660406ea80044c94ccc08c00454cc080074584c94ccc090c09c0084c8dd6980380129998120008a9981080f0b0991929998130008a998118100b0991929998140008a998128110b099299981498160010a4c2a6604c0462c64a66666605a0022a6604c0462c2a6604c0462c2a6604c0462c26eb400454cc09808c58c0a8004c0a8008c94cccccc0ac00454cc0900845854cc0900845854cc090084584dd68008a998120108b181400098140011929999998148008a9981100f8b0a9981100f8b0a9981100f8b09bad001153302201f163026001153302101e16325333333028001153302101e16153302101e16137580022a6604203c2c2a6604203c2c604a00260426ea800454cc07c070594cccccc094004400454cc07806c5854cc07806c5854cc07806c5854cc07806c58c088c07cdd50008a9980ea4815e65787065637420536f6d65286461746129203d2070616972732e6765745f66697273742874785f696e666f2e72656465656d6572732c205370656e6428646f6e6174696f6e5f696e7075742e6f75747075745f7265666572656e6365292900163322323300100100322533302300114c0103d87a8000132323253330223375e00c6048006260226604e00297ae013300500500230240023027002302500137566042604460446044604460446044604460446044603c6ea8038cdd2a400466040600a603c6ea800d2f5c0460426044604400264666e312008337006e34005200700137286eccc00cc070dd50008a9980d00b0b180f00099198008009bac3002301b375401644a66603a002297ae013232533301b3375e600a603c6ea8c014c078dd51803180f1baa0023374a9001198101ba90164bd70099810001198020020008998020020009810801180f8009180e8009180e180e80099299980a9801180b9baa00114bd6f7b63009bab301b301837540026600464660020026eacc06cc070c070c070c070c060dd500411299980d0008a5eb7bdb1804c8c8c8c94ccc068cdc8a45000021533301a3371e9101000021003100513301f337606ea4008dd3000998030030019bab301c003375c6034004603c00460380020086e1d200222323300100100322533301a00114c103d87a8000132323232533301a3372200e0042a66603466e3c01c0084c024cc07cdd3000a5eb80530103d87a8000133006006003375660380066eb8c068008c078008c070004dd2a40006eb8c058c04cdd50011b874800054cc041240120657870656374204d696e7428706f6c6963795f696429203d20707572706f73650016301430150023013001300f37540022930a99806a491856616c696461746f722072657475726e65642066616c73650013656375c0029201a3657870656374205b646f6e6174696f6e5f696e7075745d203d206c6973742e66696c746572280a20202020202074785f696e666f2e696e707574732c0a202020202020666e28696e70757429207b20696e7075742e6f75747075742e616464726573732e7061796d656e745f63726564656e7469616c203d3d2053637269707443726564656e7469616c28646f6e6174696f6e5f73637269707429207d0a202020202900490149657870656374205772617070656452656465656d65722872656465656d6572293a205772617070656452656465656d65723c28496e742c20496e742c20496e74293e203d206461746100490169657870656374207265666572656e63655f746f6b656e5f646174756d3a204369703638446174756d3c566f69643e203d206765745f6f75747075745f646174756d287265666572656e63655f746f6b656e5f6f75747075742c2074785f696e666f2e646174756d7329005734ae7155ceaab9e5573eae815d0aba257481", + "hash": "812f93828d698ea456f46c2f33c9e5f186c3ef668ea71f059b3c4829" + }, + { + "title": "donate_soada.token_unlock", + "datum": { + "title": "_datum", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "parameters": [ + { + "title": "token", + "schema": { + "$ref": "#/definitions/optim~1types~1AssetClass" + } + } + ], + "compiledCode": "5901920100003232323232323232222253330053232330010013758600460146ea8c008c028dd50019129998060008a5013253330093371090001919991119299980719b8748008c040dd50008a400026eb4c050c044dd500099299980719b8748008c040dd50008a60103d87a8000132330010013756602a60246ea8008894ccc050004530103d87a80001323232325333014337220100042a66602866e3c0200084cdd2a4000660326ea00052f5c02980103d87a8000133006006003375a602c0066eb8c050008c060008c058004c8cc004004010894ccc04c0045300103d87a80001323232325333013337220100042a66602666e3c0200084cdd2a4000660306e980052f5c02980103d87a80001330060060033756602a0066eb8c04c008c05c008c054004dd5980098069baa3001300d375460200066eb8c014c034dd50049bae3001300d375401246020602200229444cc00c00c004c03c0048c030004526153300649011856616c696461746f722072657475726e65642066616c736500136565734ae7155ceaab9e5573eae815d0aba201", + "hash": "93b7296a27af483b12ea21933fecb0acce9967c765b5323ead4df2d6" + }, { "title": "donation_strategy.mint", "redeemer": { @@ -487,6 +667,21 @@ } ] }, + "RedeemerWrapper$Tuple$Int_Int_Int": { + "title": "Wrapped Redeemer", + "description": "A redeemer wrapped in an extra constructor to make multi-validator detection possible on-chain.", + "anyOf": [ + { + "dataType": "constructor", + "index": 1, + "fields": [ + { + "$ref": "#/definitions/Tuple$Int_Int_Int" + } + ] + } + ] + }, "RedeemerWrapper$donation_strategy/DonationStrategyRedeemer": { "title": "Wrapped Redeemer", "description": "A redeemer wrapped in an extra constructor to make multi-validator detection possible on-chain.", @@ -517,6 +712,33 @@ } ] }, + "Tuple$Int_Int": { + "title": "Tuple", + "dataType": "list", + "items": [ + { + "$ref": "#/definitions/Int" + }, + { + "$ref": "#/definitions/Int" + } + ] + }, + "Tuple$Int_Int_Int": { + "title": "Tuple", + "dataType": "list", + "items": [ + { + "$ref": "#/definitions/Int" + }, + { + "$ref": "#/definitions/Int" + }, + { + "$ref": "#/definitions/Int" + } + ] + }, "aiken/transaction/OutputReference": { "title": "OutputReference", "description": "An `OutputReference` is a unique reference to an output on-chain. The `output_index`\n corresponds to the position in the output list of the transaction (identified by its id)\n that produced that output", @@ -681,6 +903,30 @@ } ] }, + "donate_soada/DonationDatum": { + "title": "DonationDatum", + "anyOf": [ + { + "title": "DonationDatum", + "dataType": "constructor", + "index": 0, + "fields": [ + { + "title": "owner", + "$ref": "#/definitions/ByteArray" + }, + { + "title": "donation_ratio", + "$ref": "#/definitions/Tuple$Int_Int" + }, + { + "title": "initial_exchange", + "$ref": "#/definitions/Tuple$Int_Int" + } + ] + } + ] + }, "donation_strategy/DonationStrategyRedeemer": { "title": "DonationStrategyRedeemer", "anyOf": [ diff --git a/oada/validators/donate_soada.ak b/oada/validators/donate_soada.ak new file mode 100644 index 0000000..81017f5 --- /dev/null +++ b/oada/validators/donate_soada.ak @@ -0,0 +1,779 @@ +use aiken/bytearray +use aiken/cbor +use aiken/dict +use aiken/hash.{blake2b_256} +use aiken/interval.{Interval, IntervalBound, Finite, PositiveInfinity} +use aiken/list +use aiken/math/rational +use aiken/option +use aiken/pairs +use aiken/transaction.{ + Mint, + Output, + ScriptContext, + Spend, + Input, + InlineDatum, + NoDatum, + OutputReference, + TransactionId, + Transaction, + ValidityRange +} +use aiken/transaction/credential.{Address, Credential, ScriptCredential, VerificationKeyCredential} +use aiken/transaction/value + +use optim/types.{IdMintRedeemer, KeyHash, Id, MintId, AssetClass, ScriptHash} +use optim/utils.{burn_own_id, find_id_input, get_output_datum, mint_own_id} +use optim/types/oada.{StakingAmoDatum} + +pub type WrappedRedeemer { + InvalidWrapped + WrappedRedeemer{ + data: data + } +} + +type BatchStakeDatum { + owner: KeyHash, + return_address: Address +} + +type DonationDatum { + owner: KeyHash, + donation_ratio: (Int, Int), + initial_exchange: (Int, Int) +} + +type Cip68Metadata = Pairs + +type Cip68Datum { + metadata: Cip68Metadata, + version: Int, + extra: extra +} + +validator(_tag: Int) { + fn always_mint(_r: Data, _ctx: Data) { + True + } + + fn always_spend(_d: Data, _r: Data, _ctx: Data) { + True + } +} + +const one_day = 86_400_000 // 60 * 60 * 24 +// actually just gives an arbitrary time within the current day, taken from the interval +pub fn validity_range_to_day(validity_range: ValidityRange) -> Int { + expect Finite(start_slot) = validity_range.lower_bound.bound_type + expect Finite(end_slot) = validity_range.upper_bound.bound_type + let start_day = start_slot / one_day + let end_day = end_slot / one_day + expect start_day == end_day + end_slot +} + +validator(donation_target: KeyHash, staking_amo: Id, batch_stake: ScriptHash) { + fn mint(redeemer: IdMintRedeemer, ctx: ScriptContext) { + let ScriptContext(tx_info, purpose) = ctx + + expect Some(staking_amo_reference) = find_id_input(staking_amo, tx_info.reference_inputs) + expect staking_amo_datum: StakingAmoDatum = get_output_datum(staking_amo_reference.output, tx_info.datums) + + mint_own_id( + redeemer, + purpose, + tx_info.inputs, + tx_info.outputs, + tx_info.mint, + fn (output: Output) { + expect datum: DonationDatum = get_output_datum(output, tx_info.datums) + expect Some(donation_ratio) = rational.new(datum.donation_ratio.1st, datum.donation_ratio.2nd) + + and{ + list.has(tx_info.extra_signatories, datum.owner)?, + (rational.compare(rational.from_int(0), donation_ratio) == Less)?, + (rational.compare(rational.from_int(1), donation_ratio) == Greater)?, + (datum.initial_exchange == (staking_amo_datum.sotoken_backing, staking_amo_datum.sotoken_amount))?, + (value.quantity_of(output.value, staking_amo_datum.sotoken, "") > 0)? + } + } + ) + } + + fn spend(datum: DonationDatum, redeemer: (Int, Int, Int), ctx: ScriptContext) { + let ScriptContext(tx_info, purpose) = ctx + expect Spend(self_out_ref) = purpose + expect Some(self_in) = list.find(tx_info.inputs, fn(input) { input.output_reference == self_out_ref }) + + expect Some(staking_amo_reference) = find_id_input(staking_amo, tx_info.reference_inputs) + expect staking_amo_datum: StakingAmoDatum = get_output_datum(staking_amo_reference.output, tx_info.datums) + + expect Some(current_exchange) = rational.new(staking_amo_datum.sotoken_backing, staking_amo_datum.sotoken_amount) + expect Some(initial_exchange) = rational.new(datum.initial_exchange.1st, datum.initial_exchange.2nd) + + let self_sotoken = value.quantity_of(self_in.output.value, staking_amo_datum.sotoken, "") + expect Some(burn_fee) = rational.new(999, 1000) + let sotoken = rational.mul(rational.from_int(self_sotoken), burn_fee) + let otoken_yield = + rational.sub(current_exchange, initial_exchange) |> rational.mul(sotoken) + expect Some(donation_ratio) = rational.new(datum.donation_ratio.1st, datum.donation_ratio.2nd) + let to_donate_base = + otoken_yield + |> rational.mul(donation_ratio) + |> rational.truncate + expect Some(to_donate) = + otoken_yield + |> rational.mul(donation_ratio) + |> rational.div(_, current_exchange) + |> option.map(rational.truncate) + + expect Some(donation_unstake_output) = list.at(tx_info.outputs, redeemer.1st) + expect Some(change_unstake_output) = list.at(tx_info.outputs, redeemer.2nd) + + let validate_unstake_output = fn(recipient: KeyHash, amount: Int, output: Output) { + expect unstake_datum: BatchStakeDatum = get_output_datum(output, tx_info.datums) + and{ + (output.address.payment_credential == ScriptCredential(batch_stake))?, + (value.quantity_of(output.value, staking_amo_datum.sotoken, "") == amount)?, + (unstake_datum.owner == recipient)?, + (unstake_datum.return_address.payment_credential == VerificationKeyCredential(recipient))? + } + } + + and{ + list.has(tx_info.extra_signatories, datum.owner)?, + burn_own_id(purpose, tx_info.inputs, tx_info.mint), + or{ + to_donate_base <= 0, + and{ + (to_donate_base == redeemer.3rd)?, + validate_unstake_output(donation_target, to_donate, donation_unstake_output), + validate_unstake_output(datum.owner, rational.truncate(sotoken) - to_donate, change_unstake_output) + } + } + } + } +} + +validator( + donation_script: ScriptHash, + reference_holder: Credential, + nft_name: Data, + nft_description: Data, + nft_image_url: Data +) { + fn mint_nft(_redeemer: Data, ctx: ScriptContext) { + let ScriptContext(tx_info, purpose) = ctx + expect Mint(policy_id) = purpose + + let own_tokens = dict.to_pairs(value.tokens(value.from_minted_value(tx_info.mint), policy_id)) + + expect [donation_input] = list.filter( + tx_info.inputs, + fn(input) { input.output.address.payment_credential == ScriptCredential(donation_script) } + ) + let token_name = + cbor.serialise(donation_input.output_reference) |> blake2b_256 |> bytearray.drop(4) + let donated: Int = { + expect Some(data) = pairs.get_first(tx_info.redeemers, Spend(donation_input.output_reference)) + expect WrappedRedeemer(redeemer): WrappedRedeemer<(Int, Int, Int)> = data + redeemer.3rd + } + + let reference_token_name = bytearray.concat(#"000643b0", token_name) + let user_token_name = bytearray.concat(#"000de140", token_name) + + expect Some(reference_token_output) = list.find( + tx_info.outputs, + fn(output) { value.quantity_of(output.value, policy_id, reference_token_name) == 1 } + ) + + expect reference_token_datum: Cip68Datum = get_output_datum(reference_token_output, tx_info.datums) + + let timestamp: Data = validity_range_to_day(tx_info.validity_range) + + let donated_data: Data = donated + let expected_reference_datum = Cip68Datum{ + metadata: [ + Pair("description", nft_description), + Pair("donated", donated_data), + Pair("image", nft_image_url), + Pair("name", nft_name), + Pair("time", timestamp) + ], + version: 1, + extra: Void + } + + let reference_token_locked = reference_token_output.address.payment_credential == reference_holder + + and{ + (donated > 0)?, + (own_tokens == [Pair(reference_token_name, 1), Pair(user_token_name, 1)])?, + (reference_token_datum == expected_reference_datum)?, + reference_token_locked? + } + } +} + +fn test_donation( + transform_mint_transaction: fn(Transaction) -> Transaction, + transform_donate_transaction: fn(Transaction) -> Transaction, + include_nft: Bool +) -> Bool { + let staking_amo_id: Id = AssetClass { + policy_id: "staking_amo_policy_id", + asset_name: "staking_amo_token_name" + } + let sotoken_amount = 8_000_000_000_001 + let sotoken_backing_0 = 9_000_000_000_001 + let sotoken_backing_1 = 9_500_000_000_001 + + let initial_staking_amo_datum = StakingAmoDatum{ + sotoken: "sotoken_policy_id", + sotoken_amount: sotoken_amount, + sotoken_backing: sotoken_backing_0, + sotoken_limit: 10_000_000_000_000, + odao_fee: 0, + odao_sotoken: 0, + fee_claimer: AssetClass("fee_claimer_policy_id", ""), + fee_claim_rule: "fee_claimer_script" + } + + let staking_amo_input_0 = Input { + output_reference: OutputReference(TransactionId("staking_amo_tx_id"), 0), + output: Output { + address: Address{ payment_credential: ScriptCredential(staking_amo_id.policy_id), stake_credential: None }, + value: value.from_asset(staking_amo_id.policy_id, staking_amo_id.asset_name, 1), + datum: InlineDatum(initial_staking_amo_datum), + reference_script: None + } + } + + let staking_amo_input_1 = Input { + ..staking_amo_input_0, + output: Output { + ..staking_amo_input_0.output , + datum: InlineDatum(StakingAmoDatum{ + ..initial_staking_amo_datum, + sotoken_amount: sotoken_amount, + sotoken_backing: sotoken_backing_1 + }) + } + } + + let token_seed_input = Input { + output_reference: OutputReference(TransactionId("seed_tx_id"), 0), + output: Output { + address: Address{ payment_credential: VerificationKeyCredential("user_key_hash"), stake_credential: None }, + value: value.zero(), + datum: NoDatum, + reference_script: None + } + } + + let donation_target = "donation_target_key_hash" + let batch_stake = "batch_stake_script_hash" + + let minted_id = value.from_asset( + "donate_soda_script_hash", + token_seed_input.output_reference |> cbor.serialise |> blake2b_256, + 1 + ) + + let donation_datum = DonationDatum{ + owner: "donation_owner", + donation_ratio: (3,4), + initial_exchange: (sotoken_backing_0, sotoken_amount), + } + + let locked_sotoken = 1_000_000 + let unburned_sotoken = locked_sotoken * 999 / 1000 + // doing this calculation inline just to avoid duplicating the validator code + // as a sanity check + let donated_base = + unburned_sotoken + * (sotoken_backing_1 - sotoken_backing_0) + * donation_datum.donation_ratio.1st + / donation_datum.donation_ratio.2nd + / sotoken_amount + let donated_base_data: Data = donated_base + let donated_sotoken = + unburned_sotoken + * (sotoken_backing_1 - sotoken_backing_0) + * sotoken_amount + * donation_datum.donation_ratio.1st + / sotoken_backing_1 + / donation_datum.donation_ratio.2nd + / sotoken_amount + + let donation_output = Output { + address: Address{ payment_credential: ScriptCredential("donate_soda_script_hash"), stake_credential: None }, + value: minted_id |> value.merge(value.from_asset("sotoken_policy_id", "", locked_sotoken)), + datum: InlineDatum(donation_datum), + reference_script: None + } + + let donation_output_ref = OutputReference(TransactionId("donation_tx_id"), 0) + + let token_name = donation_output_ref |> cbor.serialise |> blake2b_256 |> bytearray.drop(4) + let reference_token_name = bytearray.concat(#"000643b0", token_name) + let user_token_name = bytearray.concat(#"000de140", token_name) + let minted_reference_nft = value.from_asset("donation_nft_script_hash", reference_token_name, 1) + let minted_user_nft = value.from_asset("donation_nft_script_hash", user_token_name, 1) + + let reference_holder = ScriptCredential("always_fail_script_hash") + let nft_name: Data = "sOADA Yield Donation NFT" + let nft_description: Data = "Proof of your donation" + let nft_image_url: Data = "https://example.com/image.jpg" + + let donate_redeemer_wrapped: Data = WrappedRedeemer((0, 1, donated_base)) + + // arbitrary day in 2025 + let start = 365 * one_day * 55 + one_day * 200 + one_day / 3 + let end = start + 60 * 60 + let end_data: Data = end + + let donate_transaction = Transaction { + ..transaction.placeholder(), + reference_inputs: [staking_amo_input_1], + inputs: [ + Input{ + output_reference: donation_output_ref, + output: donation_output + } + ], + outputs: [ + Output{ + address: Address{ payment_credential: ScriptCredential(batch_stake), stake_credential: None }, + value: value.from_asset("sotoken_policy_id", "", donated_sotoken), + datum: InlineDatum(BatchStakeDatum { + owner: donation_target, + return_address: Address{ payment_credential: VerificationKeyCredential(donation_target), stake_credential: None } + }), + reference_script: None + }, + Output{ + address: Address{ payment_credential: ScriptCredential(batch_stake), stake_credential: None }, + value: value.from_asset("sotoken_policy_id", "", unburned_sotoken - donated_sotoken), + datum: InlineDatum(BatchStakeDatum { + owner: donation_datum.owner, + return_address: Address{ payment_credential: VerificationKeyCredential(donation_datum.owner), stake_credential: None } + }), + reference_script: None + }, + Output{ + address: Address{ payment_credential: reference_holder, stake_credential: None }, + value: minted_reference_nft, + datum: InlineDatum(Cip68Datum{ + metadata: [ + Pair("description", nft_description), + Pair("donated", donated_base_data), + Pair("image", nft_image_url), + Pair("name", nft_name), + Pair("time", end_data) + ], + version: 1, + extra: Void, + }), + reference_script: None, + } + ] |> fn(outputs) { if !include_nft { list.take(outputs, 2) } else { outputs } }, + mint: if !include_nft { value.zero() } else { minted_reference_nft |> value.merge(minted_user_nft) } + |> value.merge(value.negate(minted_id)) + |> value.to_minted_value, + extra_signatories: [donation_datum.owner], + redeemers: [Pair(Spend(donation_output_ref), donate_redeemer_wrapped)], + validity_range: Interval { + lower_bound: IntervalBound { + bound_type: Finite(start), + is_inclusive: True, + }, + upper_bound: IntervalBound { + bound_type: Finite(end), + is_inclusive: True, + } + } + } |> transform_donate_transaction + + expect actual_donation_datum: DonationDatum = + list.at(donate_transaction.inputs, 0) + |> option.map(fn(input: Input) { + expect InlineDatum(datum) = input.output.datum + datum + }) |> option.or_else(donation_datum) + let donate_redeemer: (Int, Int, Int) = { + expect Some(data) = pairs.get_first(donate_transaction.redeemers, Spend(donation_output_ref)) + expect WrappedRedeemer(redeemer): WrappedRedeemer<(Int, Int, Int)> = data + redeemer + } + + and{ + mint( + donation_target, + staking_amo_id, + batch_stake, + MintId(token_seed_input.output_reference), + ScriptContext( + Transaction { + ..transaction.placeholder(), + reference_inputs: [staking_amo_input_0], + inputs: [token_seed_input], + mint: minted_id |> value.to_minted_value, + outputs: [donation_output], + extra_signatories: [donation_datum.owner] + } |> transform_mint_transaction, + Mint("donate_soda_script_hash") + ) + ), + spend( + donation_target, + staking_amo_id, + batch_stake, + actual_donation_datum, + donate_redeemer, + ScriptContext(donate_transaction, Spend(donation_output_ref)) + ), + if !include_nft { True } else { + mint_nft( + "donate_soda_script_hash", + reference_holder, + nft_name, + nft_description, + nft_image_url, + Void, + ScriptContext(donate_transaction, Mint("donation_nft_script_hash")) + ) + } + } +} + +fn transform_reference_nft_datum(transform: fn(Cip68Datum) -> Cip68Datum) -> fn(Transaction) -> Transaction { + fn(t: Transaction) { + expect Some(reference_nft_output) = list.at(t.outputs, 2) + expect reference_nft_datum: Cip68Datum = { + expect InlineDatum(datum) = reference_nft_output.datum + datum + } + Transaction{ + ..t, + outputs: + list.take(t.outputs, 2) |> list.concat(_, [ + Output{ + ..reference_nft_output, + datum: InlineDatum(transform(reference_nft_datum)) + } + ]) + } + } +} + +fn transform_batch_stake_datum(output_index: Int, transform: fn(BatchStakeDatum) -> BatchStakeDatum) -> fn(Transaction) -> Transaction { + fn(t: Transaction) { + expect Some(batch_stake_output) = list.at(t.outputs, output_index) + expect batch_stake_datum: BatchStakeDatum = { + expect InlineDatum(datum) = batch_stake_output.datum + datum + } + Transaction{ + ..t, + outputs: + list.take(t.outputs, output_index) |> list.concat(_, [ + Output{ + ..batch_stake_output, + datum: InlineDatum(transform(batch_stake_datum)) + } + ]) |> list.concat(list.slice(t.outputs, output_index + 1, -1)) + } + } +} + +fn test_cancel( + transform_mint_transaction: fn(Transaction) -> Transaction, + transform_cancel_transaction: fn(Transaction) -> Transaction, +) { + test_donation( + transform_mint_transaction, + fn(t) { + expect Some(donation_input) = list.at(t.inputs, 0) + expect donation_datum: DonationDatum = { + expect InlineDatum(data) = donation_input.output.datum + data + } + + expect Some(staking_amo_input) = list.at(t.reference_inputs, 0) + expect staking_amo_datum: StakingAmoDatum = { + expect InlineDatum(data) = staking_amo_input.output.datum + data + } + + let donation_redeemer: Data = WrappedRedeemer((0, 0, 0)) + + Transaction{ + ..t, + reference_inputs: [ + Input { + ..staking_amo_input, + output: Output { + ..staking_amo_input.output, + datum: InlineDatum(StakingAmoDatum{ + ..staking_amo_datum, + sotoken_backing: donation_datum.initial_exchange.1st, + sotoken_amount: donation_datum.initial_exchange.2nd + }) + } + } + ], + outputs: [ + Output { + address: Address{ payment_credential: VerificationKeyCredential(donation_datum.owner), stake_credential: None }, + value: donation_input.output.value, + datum: NoDatum, + reference_script: None + } + ], + redeemers: [Pair(Spend(donation_input.output_reference), donation_redeemer)], + } |> transform_cancel_transaction + }, + False + ) +} + +fn transform_validity_range(transform: fn(ValidityRange) -> ValidityRange) -> fn(Transaction) -> Transaction { + fn(t: Transaction) { Transaction{ ..t, validity_range: transform(t.validity_range) } } +} + +test test_donate_succeeds() { + test_donation(fn(t) {t}, fn(t) {t}, True) +} + +test test_cancel_succeeds() { + test_cancel(fn(t) {t}, fn(t) {t}) +} + +// negative tests below heavily depend on knowledge of the structure and +// assumptions of `test_donation` +!test test_donate_missing_donation_output_fails() { + test_donation(fn(t) {t}, fn(t) { Transaction{..t, outputs: list.drop(t.outputs, 1) } }, True) +} + +!test test_donate_incorrect_donation_report_fails() { + let donate_redeemer = (0, 1, 9999999999) + let donate_amount_data: Data = donate_redeemer.3rd + let donate_redeemer_wrapped: Data = WrappedRedeemer(donate_redeemer) + test_donation(fn(t) {t}, fn(t) { + expect [donation_input] = t.inputs + Transaction{ + ..t, + redeemers: [Pair(Spend(donation_input.output_reference), donate_redeemer_wrapped)] + } |> transform_reference_nft_datum(fn(datum) { + Cip68Datum{ + ..datum, + metadata: pairs.map(datum.metadata, fn (key, value: Data) { + if key == "donated" { donate_amount_data } else { value } + }) + } + }) + }, True) +} + +!test test_donate_incorrect_donation_ratio_fails() { + test_donation( + fn(t) {t}, + fn(t) { + expect [donation_input] = t.inputs + expect donation_datum: DonationDatum = { + expect InlineDatum(datum) = donation_input.output.datum + datum + } + Transaction{ + ..t, + inputs: [ + Input{ + ..donation_input, + output: Output{ + ..donation_input.output, + datum: InlineDatum(DonationDatum{ + ..donation_datum, + donation_ratio: (1, 10) + }) + } + } + ] + } + }, + True + ) +} + +!test test_donate_bad_validity_range_fails() { + or{ + test_donation( + fn(t) {t}, + transform_validity_range(fn(validity_range: ValidityRange) { + expect Finite(range_end) = validity_range.upper_bound.bound_type + Interval { + ..validity_range, + upper_bound: IntervalBound { + ..validity_range.upper_bound, + bound_type: Finite(range_end + one_day) + } + } + }), + True + ), + test_donation( + fn(t) {t}, + transform_validity_range(fn(validity_range: ValidityRange) { + expect Finite(range_start) = validity_range.lower_bound.bound_type + Interval { + ..validity_range, + lower_bound: IntervalBound { + ..validity_range.lower_bound, + bound_type: Finite(range_start - one_day) + } + } + }), + True + ), + test_donation( + fn(t) {t}, + transform_validity_range(fn(validity_range: ValidityRange) { + Interval { + ..validity_range, + upper_bound: IntervalBound { + ..validity_range.upper_bound, + bound_type: PositiveInfinity + } + } + }), + True + ), + } +} + +!test test_donate_missing_change_output_fails() { + test_donation( + fn(t) {t}, + fn(t) { Transaction{..t, outputs: list.take(t.outputs, 1) |> list.concat(list.drop(t.outputs, 2)) } }, + True + ) +} + +!test test_donate_wrong_output_order_fails() { + test_donation( + fn(t) {t}, + fn(t) { + Transaction{ + ..t, + outputs: list.take(t.outputs, 2) |> list.reverse + } + }, + True + ) +} + +!test test_donate_missing_reference_nft_output_fails() { + test_donation( + fn(t) {t}, + fn(t) { Transaction{..t, outputs: list.take(t.outputs, 2) } }, + True + ) +} + +!test test_donate_bad_batch_stake_owner() { + or{ + test_donation( + fn(t) {t}, + transform_batch_stake_datum(0, fn(datum) { + BatchStakeDatum{ ..datum, owner: "bad_owner_key_hash" } + }), + True + ), + test_donation( + fn(t) {t}, + transform_batch_stake_datum(1, fn(datum) { + BatchStakeDatum{ ..datum, owner: "bad_owner_key_hash" } + }), + True + ), + } +} + +!test test_donate_bad_reference_nft_datum_fails() { + or{ + test_donation( + fn(t) { t }, + transform_reference_nft_datum(fn(datum) { + Cip68Datum{ + ..datum, + metadata: pairs.map(datum.metadata, fn (key, value: Data) { + let donated: Data = 100000000 + if key == "donated" { donated } else { value } + }) + } + }), + True + ), + test_donation( + fn(t) {t}, + transform_reference_nft_datum(fn(datum) { + Cip68Datum{ + ..datum, + metadata: pairs.foldr( + datum.metadata, + [], + fn(key, value, acc) { + if key == "name" { + let new_name: Data = "Incorrect metadata name" + list.push(acc, Pair("name", new_name)) + } else { + list.push(acc, Pair(key, value)) + } + } + ) + } + }), + True + ), + test_donation( + fn(t) {t}, + transform_reference_nft_datum(fn(datum) { + Cip68Datum{ + ..datum, + metadata: pairs.foldr( + datum.metadata, + [], + fn(key, value, acc) { + if key == "name" { + acc + } else { + list.push(acc, Pair(key, value)) + } + } + ) + } + }), + True + ), + } +} + +!test test_donation_nft_double_satisfaction_fails() { + test_donation( + fn(t) {t}, + fn(t) { Transaction{..t, inputs: list.concat(t.inputs, t.inputs) } }, + True // double satisfaction will succeed if we don't mint the NFT, but the donator has nothing to gain from doing this + ) +} + +validator(token: AssetClass) { + fn token_unlock(_datum: Data, _redeemer: Data, ctx: ScriptContext) { + list.any( + ctx.transaction.inputs, + fn (input) { value.quantity_of(input.output.value, token.policy_id, token.asset_name) > 0 } + ) + } +} diff --git a/test/src/datums.ts b/test/src/datums.ts index 13745f3..cce7535 100644 --- a/test/src/datums.ts +++ b/test/src/datums.ts @@ -3,6 +3,8 @@ import { addTypeSchema, bigintEncoder, listEncoder, + mapEncoder, + stringEncoder, rawDataEncoder, toPlutusData, } from './schema.ts' @@ -296,6 +298,30 @@ export type DigestStake = SchemaToType export type BatchStakeRedeemer = CancelStake | DigestStake +export const yieldDonationDatumSchema = { + name: 'YieldDonationDatum' as const, + constructor: 0n, + fields: [ + [ "owner", pubKeyHashEncoder ], + [ "donationRatio", listEncoder(bigintEncoder) ], + [ "initialExchange", listEncoder(bigintEncoder) ] + ] as const +} +addTypeSchema(yieldDonationDatumSchema) +export type YieldDonationDatum = SchemaToType + +export const yieldDonationNftDatumSchema = { + name: 'YieldDonationNftDatum' as const, + constructor: 0n, + fields: [ + [ "metadata", mapEncoder(stringEncoder, stringEncoder) ], + [ "version", bigintEncoder ], + [ "extraData", rawDataEncoder ] + ] as const +} +addTypeSchema(yieldDonationNftDatumSchema) +export type YieldDonationNftDatum = SchemaToType + export const toWrappedData = (data: any) => new Constr(1, [toPlutusData(data)]) export const _x = (() => 1)() diff --git a/test/src/main.ts b/test/src/main.ts index a4add11..a54064b 100644 --- a/test/src/main.ts +++ b/test/src/main.ts @@ -127,6 +127,9 @@ const { transformDepositAmoOutputAssets, transformStakingAmoDatum, + createYieldDonation, + commitYieldDonation, + withoutAdminToken, withoutFeeClaimerToken, withoutControllerSignature, @@ -191,6 +194,7 @@ await sequenceTransactions([ ({...x, sotokenAmount: x.sotokenAmount + 1n}) )) }, + () => createYieldDonation(500_000n, [7n, 10n]), { label: 'Minting sOADA beyond limit fails', expect: 'Fail', @@ -228,6 +232,7 @@ await sequenceTransactions([ () => donate(10_000_000_000_000n), () => syncDonations().then(addSignature(controllerPrivateKey)), () => mergeStakingRate().then(addSignature(controllerPrivateKey)), + () => commitYieldDonation(), () => despawnStrategy('DonationStrategy').then(addSignature(controllerPrivateKey)), ...( baseAssetUnit === 'lovelace' diff --git a/test/src/oada.ts b/test/src/oada.ts index 03d654c..c52dff2 100644 --- a/test/src/oada.ts +++ b/test/src/oada.ts @@ -4,6 +4,7 @@ import { Data, Lucid, ScriptHash, + fromText, } from 'lucid' import * as L from 'lucid' import * as hex from 'https://deno.land/std@0.216.0/encoding/hex.ts' @@ -27,10 +28,13 @@ import { stakingAmoDatumSchema, strategyDatumSchema, toWrappedData, -BatchStakeRedeemer + BatchStakeRedeemer, + YieldDonationDatum, + yieldDonationDatumSchema, + YieldDonationNftDatum } from "./datums.ts"; import { fromPlutusData, toData, toPlutusData } from "./schema.ts"; -import { AssetClass } from "./plutus-v1-encoders.ts"; +import { AssetClass, Credential } from "./plutus-v1-encoders.ts"; // hack to force evaluate the datums module until I can figure out the right way const _xx = _x @@ -122,7 +126,8 @@ export const initOtoken = async ({ const mintId = async ( validator: Validator, datum: Data, - seedUtxo?: L.UTxO + seedUtxo?: L.UTxO, + extraAssets?: L.Assets ): Promise => { if (!seedUtxo) { const result = await newTokenName() @@ -140,7 +145,7 @@ export const initOtoken = async ({ .payToAddressWithData( validator.mkAddress(), { inline: Data.to(datum) }, - { [validator.hash + tokenName]: 1n } + { ...extraAssets, [validator.hash + tokenName]: 1n } ) .signWithPrivateKey(seedMaster.privateKey) return tx @@ -790,6 +795,143 @@ export const initOtoken = async ({ ) } + const createYieldDonation = async (sotokenAmount: bigint, donationRatio: [bigint, bigint]) => { + const stakingAmoInput = await getStakingAmoUtxo() + const stakingAmoDatum: StakingAmoDatum = fromPlutusData(stakingAmoDatumSchema, await forceUtxoDatum(stakingAmoInput)) + const { paymentCredential } = utils.getAddressDetails(await lucid.wallet.address()) + const datum: YieldDonationDatum = { + kind: 'YieldDonationDatum', + owner: paymentCredential!.hash, + initialExchange: [stakingAmoDatum.sotokenBacking, stakingAmoDatum.sotokenAmount], + donationRatio + } + return newTx() + .readFrom([stakingAmoInput]) + .addSignerKey(paymentCredential!.hash) + .compose(await mintId( + yieldDonation, + toPlutusData(datum), + undefined, + { [sotokenPolicy.hash]: sotokenAmount } + )) + } + + const commitYieldDonation = async () => { + const [donationInput] = await lucid.utxosAt(yieldDonation.mkAddress()) + const { paymentCredential } = utils.getAddressDetails(await lucid.wallet.address()) + const stakingAmoInput = await getStakingAmoUtxo() + const stakingAmoDatum: StakingAmoDatum = fromPlutusData(stakingAmoDatumSchema, await forceUtxoDatum(stakingAmoInput)) + const donationUnstakeDatum: BatchStakeDatum = { + kind: 'BatchStakeDatum', + owner: feeClaimer.pubKeyHash, + returnAddress: { + kind: 'Address', + paymentCredential: { + kind: 'PubKeyCredential', + hash: feeClaimer.pubKeyHash + }, + stakingCredential: { + kind: 'Nothing' + } + } + } + const changeUnstakeDatum: BatchStakeDatum = { + kind: 'BatchStakeDatum', + owner: paymentCredential!.hash, + returnAddress: { + kind: 'Address', + paymentCredential: { + kind: 'PubKeyCredential', + hash: paymentCredential!.hash + }, + stakingCredential: { + kind: 'Nothing' + } + } + } + const unburnedSotoken = donationInput.assets[sotokenPolicy.hash] * 999n / 1000n + const donationDatum = fromPlutusData(yieldDonationDatumSchema, await forceUtxoDatum(donationInput)) + const initialExchange = donationDatum.initialExchange + const currentExchange = [stakingAmoDatum.sotokenBacking, stakingAmoDatum.sotokenAmount] + const yieldChange = [currentExchange[0] * initialExchange[1] - initialExchange[0] * currentExchange[1], initialExchange[1] * currentExchange[1]] + const otokenYield = [yieldChange[0] * unburnedSotoken, yieldChange[1]] + const baseDonation = + otokenYield[0] + * donationDatum.donationRatio[0] + / otokenYield[1] + / donationDatum.donationRatio[1] + const toDonate = + otokenYield[0] + * donationDatum.donationRatio[0] + * currentExchange[1] + / otokenYield[1] + / donationDatum.donationRatio[1] + / currentExchange[0] + + const tokenName = utxoToTokenName(donationInput).slice(8, ) + + const metadata: Map = new Map(donationNftMetadata) + + const now = lucid.provider.time || Date.now() + const start = now - 60_000 * 10 + const oneDay = 24 * 60 * 60 * 1000 + const leftInDay = oneDay - start % oneDay + const ttl = 60_000 * 30 + const end = start + (leftInDay < ttl ? leftInDay - 1 : ttl) + + metadata.set(fromText('donated'), baseDonation) + metadata.set(fromText('time'), BigInt(end)) + + const referenceNftDatum: YieldDonationNftDatum = { + kind: 'YieldDonationNftDatum', + metadata, + version: 1n, + extraData: { constructor: 0n, fields: [] } + } + + const mintTx = newTx() + + if (baseDonation > 0) { + mintTx + .mintAssets({ + [yieldDonationNft.hash + '000643b0' + tokenName]: 1n, + [yieldDonationNft.hash + '000de140' + tokenName]: 1n, + }, Data.void()) + .attachMintingPolicy(yieldDonationNft.validator) + .payToAddressWithData( + seedMaster.address, + { inline: Data.to(toPlutusData(referenceNftDatum)) }, + { [yieldDonationNft.hash + '000643b0' + tokenName]: 1n }, + ) + .payToContract( + batchStake.mkAddress(), + { inline: Data.to(toPlutusData(donationUnstakeDatum)) }, + toDonate > 0n ? { [sotokenPolicy.hash]: toDonate } : {} + ) + .payToContract( + batchStake.mkAddress(), + { inline: Data.to(toPlutusData(changeUnstakeDatum)) }, + { [sotokenPolicy.hash]: unburnedSotoken - (toDonate > 0n ? toDonate : 0n) } + ) + } + + + const idUnit = Object.keys(donationInput.assets).find(unit => unit.startsWith(yieldDonation.hash)) + + if (!idUnit) + throw new Error("Invalid donation input") + + return newTx() + .readFrom([stakingAmoInput]) + .collectFrom([donationInput], Data.to(toWrappedData([1n, 2n, baseDonation]))) + .addSignerKey(paymentCredential!.hash) + .mintAssets({ [idUnit]: -1n, }, Data.to(toPlutusData({ kind: 'BurnNft' }))) + .attachSpendingValidator(yieldDonation.validator) + .compose(mintTx) + .validFrom(start) + .validTo(end) + } + const mintIdAsAdmin = (validator: Validator, datum: Data, seedUtxo?: L.UTxO) => mintId(validator, datum, seedUtxo) .then(async tx => tx.compose(await includeAdminToken())) @@ -864,6 +1006,28 @@ export const initOtoken = async ({ [otokenPolicy.hash, toPlutusData(stakingAmoId)] ) + const yieldDonation = loadValidator(OadaScripts, 'donate_soada', [ + feeClaimer.pubKeyHash, + toPlutusData(stakingAmoId), + batchStake.hash + ]) + const donationNftMetadata = new Map([ + [fromText('description'), fromText('Proof of your donation')], + [fromText('donated'), null], + [fromText('image'), fromText('https://example.com/image.jpg')], + [fromText('name'), fromText('sOADA Yield Donation NFT')], + ]) + const yieldDonationNft = loadValidator(OadaScripts, 'donate_soada', [ + yieldDonation.hash, + toPlutusData({ + kind: 'PubKeyCredential', + hash: seedMaster.pubKeyHash, + } as Credential), + donationNftMetadata.get(fromText('name'))!, + donationNftMetadata.get(fromText('description'))!, + donationNftMetadata.get(fromText('image'))! + ], 'mint_nft') + // shorter aliases for log output const registerRules = async () => newTx() @@ -988,6 +1152,9 @@ export const initOtoken = async ({ transformDepositAmoOutputAssets, transformStakingAmoDatum, + createYieldDonation, + commitYieldDonation, + redirectId, withoutAdminToken, withoutFeeClaimerToken, diff --git a/test/src/utils.ts b/test/src/utils.ts index 2e1be62..8a28480 100644 --- a/test/src/utils.ts +++ b/test/src/utils.ts @@ -56,9 +56,10 @@ export const mkScriptUtils = (lucid: Lucid) => { const loadValidator = ( blueprint: Blueprint, name: string, - parameters: Data[] = [] + parameters: Data[] = [], + scriptType?: string ): Validator => { - for (const s of scriptTypes) { + for (const s of (scriptType ? [scriptType] : scriptTypes)) { const script = blueprint.validators.find(v => v.title === `${name}.${s}`) if (!script) continue;