10

When I perform a token transfer from the cleos command line, it works with no problems:

$ cleos push action eosio.token transfer '["rider2","vehicle1","1000.0000 TOK","initial"]' -p rider2 
executed transaction: 44412ed34b9008c0802d22c2e52174428c377467e2cf633ffcf8510cd35eb6f9  136 bytes  719 us
#   eosio.token <= eosio.token::transfer        {"from":"rider2","to":"vehicle1","quantity":"1000.0000 TOK","memo":"initial"}
#        rider2 <= eosio.token::transfer        {"from":"rider2","to":"vehicle1","quantity":"1000.0000 TOK","memo":"initial"}
#      vehicle1 <= eosio.token::transfer        {"from":"rider2","to":"vehicle1","quantity":"1000.0000 TOK","memo":"initial"}

However, when I try to perform the same action inside of a smart contract, I get a problem with permissions (even through I'm signing the transaction with the appropriate transactions).

Here's the code within my action:

eosio::action(
      std::vector<eosio::permission_level>(2,{rider, N(active)}),
      N(eosio.token),   // contract
      N(transfer),      // action
      xfer{rider, gate, fee, std::to_string(breachId) } )
      .send();

Here's how I'm invoking the action (essentially, tokens should be transferred from rider1 to gate1.

$ cleos push action eosride breachgate '[47539,"gate1","vehicle1"]' -p gate1 -p vehicle1 -p rider3

But I'm getting an error claiming that it does not have permissions from rider3 even though I am signing it.

Error 3090003: provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.
Error Details:
transaction declares authority '{"actor":"rider3","permission":"active"}', but does not have signatures for it under a provided delay of 0 ms                                

3 Answers 3

18

Inline actions sent by a contract are executed with a virtual "signature" that satisfies the contract@code permission where contract is the account where the code is installed.

Your inline action declares the permission rider@active however, contract@code does not satisfy rider@active by default. In order to prevent nefarious contracts from doing "bad things" with inline actions we require some sort of positive affirmation of that permission.

More particularly, the rider user would have to set up some chain of permission levels that makes it possible for you to withdraw from their account.

This can be done a few ways (ordered from most risky for the user to least):

  1. the user may allow contract@active to satisfy their @active permission

  2. The user may set up a sub-permission (for example @xfer), link that as the minimum permission to eosio.token::transfer and allow contract@active to satisfy that permission

  3. require that the transfer be explicit (not inline) and then use the notification that is sent to your contract to (temporarily) record the details and read/assert/process them in a second action directly to your contract.

There are, of course, many other ways to accomplish what you are aiming for as well. These are just the ones from the top of my head.

3
  • Great answer! Would be even better if you could point to some implementation examples for the 3 ways of doing this.
    – ken
    Jun 22, 2018 at 3:09
  • I found that the first method will be a great solution when a contract call an inline action to transfer it's own token to others, I can add the code permission to the contract account.
    – Jimmy Guo
    Jun 28, 2018 at 10:18
  • I have been trying to verify that inline actions use the contract@code permission, but when I try to use require_auth2( N(contract), N(code) ); inside another contract's action, I get Exception Details: 3090004 missing_auth_exception: Missing required authority missing authority of contract/code The same is true if I use eosio.code. Is this due to the permission being "virtual?"
    – Tyrick
    Jul 23, 2018 at 22:02
0

You could find related example code from the "dice" contract. I get following code from the "deposit" function of the "dice" contract.

action(
 permission_level{ from, N(active) },
 N(eosio.token), N(transfer),
 std::make_tuple(from, _self, quantity, std::string(""))
).send();
0

Assume your contract account is contractuser, when calling inline transfer action, the permission is contractuser with permission eosio.code. You need to set account ride's permission so that user for the contract with permission "eosio.code" have the active permission. cleos set account permission rider active '{"threshold":1, "keys":[{"key":"...", "weight":1}], "accounts": [{"permission":{"actor":"contractuser","permission":"eosio.code"},"weight":1}]}' owner -p rider

1
  • this is a solution for deposit, but risky, you contract can listen to the token transfer, and do left things
    – Jimmy Guo
    Jun 28, 2018 at 10:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.