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? - pact-lang

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

Assuming that your nft-id can reference a collection, and not just an individual NFT, this code should work to allow any individual who owns one to claim something from the contract.
(defcap WITHDRAW (nft-id:string)
(with-read mledger nft-id
{'owner-address := owner-address }
(enforce (= (read-msg "recipient") owner-address) "not the owner of this NFT")
)
(compose-capability (BANK_DEBIT))
)
(defun require-WITHDRAW (nft-id:string)
(require-capability (WITHDRAW nft-id))
)
(defun create-WITHDRAW-guard (nft-id:string)
(create-user-guard (require-WITHDRAW nft-id))
)
(defun create-simple-user-guard (funder:string nft-id:string BANK_KDA_ACCT:string amount:decimal)
(coin.transfer-create funder BANK_KDA_ACCT
(create-WITHDRAW-guard nft-id) amount)
)
Keep in mind that this code requires you to pass in a recipient into the env data of the transaction in this manner:
{ "recipient": "k:abcd..." }
Hope this helps!

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))
)
```

Related

How can i check the module-name in `cont` type transactions for gas-station?

I have implemented a gas-station for my project.
When i tried to pay gas for a cont type txn, and it threw me Inside an exec error.
(defcap GAS_PAYER:bool
( user:string
limit:integer
price:decimal
)
(enforce (= "exec" (at "tx-type" (read-msg))) "Inside an exec")
(enforce (= 1 (length (at "exec-code" (read-msg)))) "Tx of only one pact function")
(enforce (= "(free.anedak." (take 13 (at 0 (at "exec-code" (read-msg))))) "only free.anedak smart contract")
(compose-capability (ALLOW_GAS))
)
While looking at the code, I found that only exec type transactions are supported in gas-stations and the ability to check that the txn call is made only by my contract is only possible in exec type txns.
The data to check for module name and txn types are added in this block of code.
https://github.com/kadena-io/chainweb-node/blob/0b24dd35a7fda27719ea7a2ca54db2cfd1196d95/src/Chainweb/Pact/TransactionExec.hs#L876.
I don't think i can use the existing props for checking the module name in cont type txns.
https://github.com/kadena-io/chainweb-node/blob/8c32fcfff85c4e5b61a9554f0180ca6c90840e42/test/pact/continuation-gas-payer.pact
I found this one for cont type txns, but no checks on module name.
Any solutions on how i can check the module-name in cont type txns will be helpful as this will prevent my gas-station from spamming.

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)?
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.

Format does not except Amount:Decimal

I was writing over a simple payment contract and noticed that I was receiving the warning:
We can only analyze calls to format formatting {string,integer,bool}
(not amount)
Below is my code, I realized if I remove the amount parameter on the bottom of my code I no longer receive the warning.. is there a way to adjust it?
(defun pay (from:string to:string amount:decimal)
(with-read payments-table from { "balance":= from-bal, "keyset":= keyset }
(enforce-keyset keyset)
(with-read payments-table to { "balance":= to-bal }
(enforce (> amount 0.0) "Negative Transaction Amount")
(enforce (>= from-bal amount) "Insufficient Funds")
(update payments-table from
{ "balance": (- from-bal amount) })
(update payments-table to
{ "balance": (+ to-bal amount) })
(format "{} paid {}" [from to] ))))
)
The Pact property checking system doesn't currently support analysis of formatting decimals. Your example as written should actually be okay, but if we look at the simple payment example it includes this line: (format "{} paid {} {}" [from to amount]), where amount is a decimal.
If you need to check properties of code like this, the easiest way would be to use integer instead of decimal, since we can analyze the formatting of integers.
We can't currently analyze formatting of integers for a technical reason that should be fixable.

How to query all association (all level) in Neo4j?

I am new to study graph databases (using Neo4j), I am modelling for my project. The business rule is that we need to store the users and devices they used, and this is my current model:
Account A uses device D: (Account A) -[USED]-> (Device D)
Account B also uses device D: (Account B) -[USED]-> (Device D)
=>
(Account A) -[USED]-> (Device D) <-[USED]- (Account B)
In future, account B will use other devices & be related with other accounts.
My questions are:
Is it a good model in this case?
With this model, how to find all associated account with account A?
Thank for you help.
Yes, this model works, as :Account and :Device nodes are distinct in the graph.
As for figuring out what other accounts that are associated with :Account a (only considering the link via using the same device), simple queries should do the trick. Assuming we have :Account(name) in the graph, and that there's an index on this (for fast lookup of :Account nodes by name), we could use this:
MATCH (:Account {name:'A'})-[:USED]->()<-[:USED]-(other:Account)
RETURN DISTINCT other
If :USED relationships always are incoming to :Device nodes, we could simplify this to:
MATCH (:Account {name:'A'})-[:USED*2]-(other:Account)
RETURN DISTINCT other
If we also needed the devices used in common between the two connected account nodes, we could include that as a collection of the nodes between the two linked accounts:
MATCH (:Account {name:'A'})-[:USED]->(device)<-[:USED]-(other:Account)
RETURN other, collect(device) as sharedDevices

Alexa Intent Schema: Random input being identified as intents

I have two intents that use the same slot types. However, if the input is a random string, Alexa automatically identifies an intent in its JSON request even though it is not part of the utterances. For example, in the example below, if the user input was 'bla bla bla', GetAccountBalance is identified as the intent with no slot value even though it is not part of provided utterances.
What is the way to error-check for these cases and what is the best practice to avoid cases like this when developing the intent schema? Is there a way to create an intent that can handle all random inputs?
Example Schema:
{
"intents": [
{
"intent": "GetAccountBalance",
"slots": [
{
"name": "AccountType",
"type": "ACCOUNT_TYPE"
}
]
},
{
"intent": "GetAccountNumber",
"slots": [
{
"name": "AccountType",
"type": "ACCOUNT_TYPE"
}
]
}
]
}
Utterances:
GetAccountBalance what is my account balance for {AccountType} Account
GetAccountBalance what is my balance for {AccountType} Account
GetAccountBalance what is the balance for my {AccountType} Account
GetAccountBalance what is {AccountType} account balance
GetAccountBalance what is my account balance
GetAccountBalance what is account balance
GetAccountBalance what is the account balance
GetAccountBalance what is account balance
GetAccountNumber what is my account number for {AccountType} Account
GetAccountNumber what is my number for {AccountType} Account
GetAccountNumber what is the number for my {AccountType} Account
GetAccountNumber what is {AccountType} account number
GetAccountNumber what is my account number
GetAccountNumber what is account number
GetAccountNumber what is the account number
GetAccountNumber what is account number
There is one hack to resolve this problem:
If no any match found(random string) Amazon always take intent with highest no of utterances.
So I created one intent 'DidNotUnderstand' and add as many random utterances as I can (Be moderate enough) in result if no any match found alexa will call 'DidNotUnderstand' intent.
Please refer first reply from below link:
https://forums.developer.amazon.com/questions/4856/intent-triggering-without-utterance-match.html
When developing an Alexa skill, Alexa will always pick an intent to fire even if the user speaks pure gibberish. As far as I'm aware, there isn't a way to set a default / catch-all intent.
In terms of error handling, the following passage from the docs is really important.
Note that a custom slot type is not the equivalent of an enumeration. Values outside the list are still returned if recognised by the spoken language understanding system. Although input to a custom slot type is weighted towards the values in the list, it is not constrained to just the items on the list. Your code still needs to include validation and error checking when using slot values.
The link above also has a few follow up links which dive further into the topic error handling.
As per the documentation:
The AMAZON.FallbackIntent (available in English (US) only) is triggered when the user's spoken input does not match any of the other intents in the skill. AMAZON.FallbackIntent is matched to an automatically-generated out-of-domain model.
Code snippet:
'AMAZON.FallbackIntent': function (intent, session, response) {
response.ask("Optimus Prime didn't get that one....","");
}
A slot with SearchQuery slot type may help you.but it would need some extra phrases with it.like
Fname->where Fname is a slot of type Amazon.SearchQuery
my name is {Fname}
it will work for the example
my name is bffblselsk
my name is snfdslnel
etc...
visit Amazon.SearchQuery Slot for more referrences....

Resources