pact-lang

How do I grant a capability to an account after a module has been deployed?


I have my contract deployed on the testnet and I am trying to call (mint-nft) which has no arguments, but it does have a require-capability ACCOUNT_GUARD. I am getting error as follows:

Error from (api.testnet.chainweb.com): : Failure: require-capability: not granted: 
(free.slightly-less-shitty-nft-project.ACCOUNT_GUARD "k:ab7cef70e1d91a138b8f65d26d6917ffcb852b84525b7dc2193843e8dfebf799")

ACCOUNT_GUARD capability defined: (taken from kitty-kad contract)

(defcap ACCOUNT_GUARD(account:string) ; Used for admin functions
    @doc "Verifies account meets format and belongs to caller"
    (enforce (= "k:" (take 2 account)) "For security, only support k: accounts")
    (enforce-guard   
        (at "guard" (coin.details account))
    )
  ) 

A simplified mint-nft is defined as follows:

   (defun mint-nft()
    @doc "Allows any k: address to mint an nft"
    (require-capability (ACCOUNT_GUARD (at "sender" (chain-data))))
      (insert nft-main-table id {
          "id":id,
          "generation":0,
          "owner-address":(at "sender" (chain-data))
       })
  ) 

From my understanding, ACCOUNT_GUARD is simply checking to see that the account calling the contract is a valid k: address and that the account calling it is the owner of said account.

Why would the ACCOUNT_GUARD not be granted to the caller of (mint-nft) in this case?

How do I grant the ACCOUNT_GUARD capability to any user that calls (mint-nft)?


Solution

  • Capabilities must be acquired before require-capability will succeed. See https://pact-language.readthedocs.io/en/stable/pact-reference.html#expressing-capabilities-in-code-defcap for discussion of this.

    Capabilities are acquired with with-capability such that code within the body of the with-capability expression have the granted capability in scope. At that point you could call mint-nft, if that was a "protected function" that you did not want users to call at top level.

    However if mint-nft is the intended top-level call, then use with-capability instead of require-capability, and include the insert call in the body. (Your indentation already looks like this even though there is no body in require-capability btw).

    Lastly, you should not base anything on sender -- this is the gas payer only and in Kadena gas can be paid by a gas station or really anybody. Instead you should manage a user table with the associated guard, and validate the principal when creating the user. k: without any associated guard is meaningless.