(mod (
    TREASURY_SINGLETON_STRUCT
    CAT_MOD_HASH
    CONDITIONS  ; XCH conditions, to be generated by the treasury
    LIST_OF_TAILHASH_CONDITIONS  ; the delegated puzzlehash must be curried in to the proposal.
    ; Puzzlehash is only run in the last coin for that asset
    ; ((TAIL_HASH CONDITIONS) (TAIL_HASH CONDITIONS)... )
    P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH
    p2_singleton_parent_amount_list  ; for xch this is just a list of (coin_parent coin_amount)
    p2_singleton_tailhash_parent_amount_list  ; list of ((asset (parent amount) (parent amount)... ) (asset (parent amount)... )... ),
    ; must match order of curryed asset list
    ; the last (parent amount) gets given the puzzlehash, the rest get given 0
    treasury_inner_puzhash
  )
  ; we need to track CAT_TYPE and DELEGATED_PUZZLE
  ; list of (asset_type (parent amount))

  ; If you're writing a proposal you'll want to use this layer
  ; if you don't, your proposal might be invalidated if the p2_singleton coins get spent

  (include condition_codes.clib)
  (include curry-and-treehash.clib)
  (include utility_macros.clib)
  (include *standard-cl-21*)

  (defun-inline calculate_singleton_puzzle_hash (PROPOSAL_SINGLETON_STRUCT inner_puzzle_hash)
    (puzzle-hash-of-curried-function (f PROPOSAL_SINGLETON_STRUCT)
      inner_puzzle_hash
      (sha256tree PROPOSAL_SINGLETON_STRUCT)
    )
  )

  (defun loop_through_list (
      TREASURY_SINGLETON_STRUCT
      SPEND_AMOUNT
      P2_SINGLETON_PUZHASH
      p2_calculated
      p2_singleton_list
      total
      output
    )
    (c
      (list CREATE_PUZZLE_ANNOUNCEMENT (sha256tree (list p2_calculated (sha256tree 0))))
      (c
        (list ASSERT_COIN_ANNOUNCEMENT (sha256 p2_calculated '$'))
        (if p2_singleton_list
            (loop_through_list
              TREASURY_SINGLETON_STRUCT
              SPEND_AMOUNT
              P2_SINGLETON_PUZHASH
              (calculate_coin_id (f (f p2_singleton_list)) P2_SINGLETON_PUZHASH (f (r (f p2_singleton_list))))
              (r p2_singleton_list)
              (+ total (f (r (f p2_singleton_list))))
              output
            )
            (if (> (- total SPEND_AMOUNT) 0)
                (c
                  (list CREATE_COIN P2_SINGLETON_PUZHASH (- total SPEND_AMOUNT) (list P2_SINGLETON_PUZHASH))
                  output
                )
                output
            )
        )
      )
    )
  )

  (defun add_announcements_to_result (p2_calculated delegated_puzhash output)
    (c
      (list CREATE_PUZZLE_ANNOUNCEMENT (sha256tree (list p2_calculated delegated_puzhash)))
      (c
        (list ASSERT_COIN_ANNOUNCEMENT (sha256 p2_calculated '$'))
        output
      )
    )
  )

  (defun sum_create_coins (conditions)
    (if conditions
        (+
          (if
            (= (f (f conditions)) CREATE_COIN)
            (if
              (> (f (r (r (f conditions)))) 0)  ; make an exception for -113 and other magic conditions
              (f (r (r (f conditions))))
              0
            )
            0
          )
          (sum_create_coins (r conditions))
        )
        0
    )
  )

  (defun-inline calculate_delegated_puzzlehash (CONDITIONS)
    (sha256tree (c ONE CONDITIONS))  ; this makes (q . CONDITIONS)
  )

  (defun wrap_in_cat_layer (CAT_MOD_HASH CAT_TAIL_HASH INNERPUZHASH)
    (puzzle-hash-of-curried-function CAT_MOD_HASH
      INNERPUZHASH
      (sha256 ONE CAT_TAIL_HASH)
      (sha256 ONE CAT_MOD_HASH)
    )
  )

  (defun dedupe ((@ lst ((@ first (parent amount)) . rest)) ids)
    (if lst
        (let ((id (sha256 parent amount)))
          (if (in id ids)
              (dedupe rest ids)
              (c first (dedupe rest (c id ids)))
          )
        )
        ()
    )
  )


  ; for a given asset type, loop through the cat coins and generate the announcements required for each
  (defun for_each_asset (
      TREASURY_SINGLETON_STRUCT
      CAT_MOD_HASH
      CONDITIONS_FOR_THIS_ASSET_TYPE
      P2_SINGLETON_PUZHASH
      p2_singleton_puzzle_hash
      parent_amount_list
      total
      create_coin_sum
      output
    )
    (if parent_amount_list
        (add_announcements_to_result
          (calculate_coin_id (f (f parent_amount_list)) p2_singleton_puzzle_hash (f (r (f parent_amount_list))))
          (if
            (r parent_amount_list)  ; this is the delegated_puzhash
            (sha256tree 0)  ; most coins destroy themselves
            (calculate_delegated_puzzlehash  ; the last coin creates the conditions
              (let ((coin_sum (- (+ total (f (r (f parent_amount_list)))) create_coin_sum)))
                (if (> coin_sum 0)
                    (c
                      (list CREATE_COIN P2_SINGLETON_PUZHASH coin_sum (list P2_SINGLETON_PUZHASH))
                      CONDITIONS_FOR_THIS_ASSET_TYPE
                    )
                    CONDITIONS_FOR_THIS_ASSET_TYPE
              ))
            )
          )
          (for_each_asset
            TREASURY_SINGLETON_STRUCT
            CAT_MOD_HASH
            CONDITIONS_FOR_THIS_ASSET_TYPE
            P2_SINGLETON_PUZHASH
            p2_singleton_puzzle_hash
            (r parent_amount_list)
            (+ total (f (r (f parent_amount_list))))
            create_coin_sum
            output
          )
        )
        output
    )
  )

  ; loops through the list of ((tailhash conditions))
  (defun for_each_asset_type (
      TREASURY_SINGLETON_STRUCT
      CAT_MOD_HASH
      P2_SINGLETON_PUZHASH
      LIST_OF_TAILHASH_CONDITIONS
      p2_singleton_tailhash_parent_amount_list  ; ((tailhash ((parent amount) (parent_amount)... ) (tailhash (parent amount))..)
      output
    )
    (if LIST_OF_TAILHASH_CONDITIONS
        (for_each_asset_type
          TREASURY_SINGLETON_STRUCT
          CAT_MOD_HASH
          P2_SINGLETON_PUZHASH
          (r LIST_OF_TAILHASH_CONDITIONS)
          (r p2_singleton_tailhash_parent_amount_list)
          (for_each_asset
            TREASURY_SINGLETON_STRUCT
            CAT_MOD_HASH
            (if
              (=
                (f (f LIST_OF_TAILHASH_CONDITIONS))
                (f (f p2_singleton_tailhash_parent_amount_list))
              )
              (f (r (f LIST_OF_TAILHASH_CONDITIONS)))
              (x)  ; bad solution format
            )
            P2_SINGLETON_PUZHASH
            (wrap_in_cat_layer CAT_MOD_HASH (f (f p2_singleton_tailhash_parent_amount_list)) P2_SINGLETON_PUZHASH)  ; p2_singleton_puzzle_hash
            (dedupe (f (r (f p2_singleton_tailhash_parent_amount_list))) 0 0)  ; list of ((parent amount) (parent amount)...)
            0  ; current total - initialise as 0
            (sum_create_coins (f (r (f LIST_OF_TAILHASH_CONDITIONS))))
            output  ; add new conditions to previous calculated output conditions
          )
        )
        output  ; at the end of the loop output our calculated conditions
    )
  )


  ; main
  (c
    (list ASSERT_MY_PUZZLEHASH (calculate_singleton_puzzle_hash TREASURY_SINGLETON_STRUCT treasury_inner_puzhash))
    (c
      (list CREATE_COIN treasury_inner_puzhash ONE (list (f (r TREASURY_SINGLETON_STRUCT))))
      (if CONDITIONS
          (loop_through_list
            TREASURY_SINGLETON_STRUCT
            (sum_create_coins CONDITIONS)
            P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH
            (if p2_singleton_parent_amount_list (calculate_coin_id (f (f p2_singleton_parent_amount_list)) P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH (f (r (f p2_singleton_parent_amount_list)))) ())
            (if p2_singleton_parent_amount_list (r p2_singleton_parent_amount_list) ())
            (if p2_singleton_parent_amount_list (f (r (f p2_singleton_parent_amount_list))) ())
            (for_each_asset_type
              TREASURY_SINGLETON_STRUCT
              CAT_MOD_HASH
              P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH
              LIST_OF_TAILHASH_CONDITIONS
              p2_singleton_tailhash_parent_amount_list  ; ((tailhash ((parent amount) (parent_amount)... ) (tailhash (parent amount))..)
              CONDITIONS
            )
          )
          (for_each_asset_type
            TREASURY_SINGLETON_STRUCT
            CAT_MOD_HASH
            P2_SINGLETON_VIA_DELEGATED_PUZZLE_PUZHASH
            LIST_OF_TAILHASH_CONDITIONS
            p2_singleton_tailhash_parent_amount_list  ; ((tailhash ((parent amount) (parent_amount)... ) (tailhash (parent amount))..)
            CONDITIONS
          )
      )
    )
  )

)
