Search code examples
pact-langkadena

How do I create a KDA wallet guard that will allow for an arbitrary number of people to withdraw from it, if they meet specific conditions?


Question: How do I create a wallet guard that will allow for an arbitrary number of people to withdraw from it, if they meet specific conditions?

I am trying to make a wallet with a guard that lets a user withdraw a specific amount from it depending on how long they've owned an NFT, among other factors. Right now I'm doing the most basic check: seeing if the "recipient" address passed into the claim function is the owner of a specific NFT. If they own that NFT, they can withdraw as much as they want.

   (defcap WITHDRAW (recipient:string nft-id:string)
        (with-read mledger nft-id
            {'owner-address := owner-address }
            (enforce (= recipient owner-address) "not the owner of this NFT")
        )
        (compose-capability (BANK_DEBIT))
    )

    (defun require-WITHDRAW (recipient:string nft-id:string) 
        (require-capability (WITHDRAW recipient nft-id))
    )

    (defun create-WITHDRAW-guard (recipient:string nft-id:string)
        (create-user-guard (require-WITHDRAW recipient nft-id))
    )

    (defun create-simple-user-guard (funder:string nft-id:string BANK_KDA_ACCT:string amount:decimal recipient:string)
        (coin.transfer-create funder BANK_KDA_ACCT 
          (create-WITHDRAW-guard recipient nft-id) amount)
    )

With my current code, only the very first inputs that I pass into (create-simple-user-guard) impact who can withdraw, but I do not know how to allow the guard to accept many different recipients and NFT-ids. Any advice would be appreciated.

I'm following this "tutorial" https://medium.com/kadena-io/deprecation-notice-for-module-guards-and-pact-guards-2efbf64f488f but it loses any amount of detail after it gets to making more robust debit capabilities


Solution

  • Thanks @luzzotica for your answer! It was not the perfect solution to my use-case but it did help guide me where I needed to be.

    Here's a snippet of my contract on the testnet, if you want to see the whole thing it is called free.simple-user-guard

    Basically what I needed to do was create a wallet that is guarded by the BANK_DEBIT capability which always returns true, and then from there I can use regular capabilities to determine ownership of the NFTs. The tutorial linked in my question post gave the answer, I just didn't understand it without further discussion. Note: This code has only one restriction, and that is the calling wallet must own an NFT. This does not restrict how much can be taken from the wallet, additional capabilities would need to be implemented for that.

    
        
        (defcap WITHDRAW (recipient:string nft-id:string)
            (with-read mledger nft-id
                {'owner-address := owner-address }
                (enforce (= recipient owner-address) "not the owner of this NFT")
            )
            (compose-capability (BANK_DEBIT))
            (compose-capability (ACCOUNT_GUARD recipient))
        )
    
        (defun create-simple-user-guard (funder:string BANK_KDA_ACCT:string amount:decimal)
            (coin.transfer-create funder BANK_KDA_ACCT 
              (create-BANK_DEBIT-guard) amount)
        )
    
        (defun claim (recipient:string amount:decimal BANK_KDA_ACCT:string nft-id:string)
            @doc "allows a user to withdraw x amount"
            (with-capability (WITHDRAW recipient nft-id)
                (coin.transfer BANK_KDA_ACCT recipient amount)
            )
        )
        
        ;; Capability user guard: capability predicate function
        (defun require-BANK_DEBIT () 
            (require-capability (BANK_DEBIT))
        )
        
        ;; Capability user guard: guard constructor
        (defun create-BANK_DEBIT-guard ()
            (create-user-guard (require-BANK_DEBIT))
        )
        ```