#ERROR: encountered authorization not tied to the root contract invocation

1 messages · Page 1 of 1 (latest)

vale scaffold
#

I have a contract which calls the swap_tokens_for_exact_tokens function of soroswap router to swap tokens .

To execute the swap, the swap_tokens_for_exact_tokens function of soroswap router also calls .transfer of the specified output token to send the tokens to my contract.

The flow of the call to swap tokens is like this

My-contract --> fn swap_tokens_for_exact_tokens (soroswap_router) --> fn transfer (transfers token form token_in to pair)

Therefore since the transfer function of the input token called by soroswap router authorizes the from (my router contract) parameter using from.require_auth() I have used env.authorize_as_current_contract(auth_entries) in my contract before calling swap_tokens_for_exact_tokens function of soroswap to authorize the deeper call (which is the transfer call of the input token)

however I get this error below coming from the transfer function of the output token

topics:[error, Error(Auth, InvalidAction)], data:["[recording authorization only] encountered authorization not tied to the root contract invocation for an address. Use require_auth() in the top invocation or enable non-root authorization.",

this is my auth code

                    token_out_id.address.clone(),  //address
                    my_contract.clone(), //address
                );

   e.authorize_as_current_contract(auth_entries);
               e.invoke_contract::<Val>(router_id, &function, args);```

This is a slim version of the code , 
I have significantly reduced the code size and uploaded to this repo  so the invocation which produces the error is clear https://github.com/ysfkel/authorized-caontract-call/blob/434bf6beeea159560a3f588bea111ce2e88afb38/src/contract.rs#L87
GitHub

Contribute to ysfkel/authorized-caontract-call development by creating an account on GitHub.

calm cedar
#

the message tries to tell you what's wrong, but I realize how it still might be confusing. basically an authorization for some address (it should also be included in the message) is 'frontrunnable'. say, your test calls something like contract_a.do_stuff(from_address, ...) that calls token_b.transfer(from_address, ...), but you don't call from_address.require_auth() in contract_a.do_stuff call. this means that just the signature for token_b.transfer is required, thus it will be possible to execute the transfer out of context.
now, that's not always an error, which is why this message only occurs in tests/simulation (basically when authorization is in the 'recording' mode). e.g. you might just want to execute a couple of transfers at the same time and you don't really care if one of them gets frontrun. but this is a more rare case and you should have a good idea of what you're doing - if in doubt, you probably want to call require_auth at the 'top-level' contract call (i.e. the contract call that you intend your users to interact with)

vale scaffold
#

@calm cedar Thanks for the response (If it is not an error, how can i make the test ignore it.) My test actually calls the top level contract which users will interact with like so top_level_contract.swap(..) the top level contract then calls the DEX to perform a swap dex.swap(..) and finally the dex calls the token.tranfer(..) . A reduced version of the contract is at this repo , and you should see that I have authorized user / caller with to.require_auth() and I have also provided authorization for my contract before calling yhe dex so that the final dex transfer is authorized https://github.com/ysfkel/authorized-caontract-call/blob/434bf6beeea159560a3f588bea111ce2e88afb38/src/contract.rs#L64. I have pleaced the entire error log in this gist https://gist.github.com/ysfkel/0b98905600c0c38172cfdb41ac62e09c

Gist

ERROR: encountered authorization. GitHub Gist: instantly share code, notes, and snippets.

GitHub

Contribute to ysfkel/authorized-caontract-call development by creating an account on GitHub.

calm cedar
#

the error message should contain the address that has an unauthorized call

#

another case how this could occur is when your authorize_as_current_contract is not built correctly. if we couldn't find the invoker contract authorization, we'll try to find external authorization for it, which in you scenario will be reflected as non-root authorization (i.e. your top-level contract still requires a deep authorization that is not attached to anything). it's a tricky case and I'm not sure how we can improve error messaging for it 😦

vale scaffold
#

@calm cedar this is how I built the auth entries which i called from my contract , it specifes that the dex will call the transfer of the token
`pub fn create_sub_auth(
e: &Env,
amount_in: i128,
token_in: Address,
token_out: Address,
to: Address,
) -> Vec<InvokerContractAuthEntry> {
let router_id = require_exchange_router(e);
let pair = router_pair_for(e, &router_id, &token_in.clone(), &token_out.clone());

let mut args: Vec<Val> = vec![e];
args.push_back(to.into_val(e));
args.push_back(pair.into_val(e));
args.push_back(amount_in.into_val(e));

let mut sub_auth_vec = vec![&e];
let pre_auth_entry = InvokerContractAuthEntry::Contract(SubContractInvocation {
    context: ContractContext {
        contract: token_in.clone(),
        fn_name: Symbol::new(&e, TRANSFER),
        args: args.clone(),
    },
    sub_invocations: vec![&e],
});

sub_auth_vec.push_back(pre_auth_entry);
sub_auth_vec
}`

calm cedar
#

yeah, I've seen this code ,but I can't tell if it's wrong just by looking at this

#

that's how I would recommend debugging this: call env.mock_all_auths_allowing_non_root_auth() before calling your contract (this will just get rid of the test error; note that there is still a bug somewhere), then call dbg!(env.auths()) to see what you're trying to authorize

#

you'll see which auths are being recorded and if my hypothesis is correct you'll see the top contract's auth that hasn't been satisfied

#

if you paste the output here I might be able to help as well

vale scaffold
#

okay , which crate is dbg! located

calm cedar
#

std

#

you need to add extern crate std; to your test file if you haven't done so yet

vale scaffold
#

okay

calm cedar
#

I also recommend using rust-analyzer plugin for your IDE, it tremendously helps dealing with deps

vale scaffold
#

got it

#

its not showing the errors

#

it says test passed due to mock_all_auths_allowing_non_root_auth

calm cedar
#

right, that's expected

#

but what we need to see is the output of env.auths()

vale scaffold
#

std::dbg!(test.env.auths()); didnt print anything

calm cedar
#

try adding --show-output to the test command line

vale scaffold
#

@calm cedar dbg! prints this test.env.auths() = [ ( Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M), AuthorizedInvocation { function: Contract( ( Contract(CBRIAA73VOIKPZYM5G3LGPF3NGCFXLR3IW22MKEYJAB3QBOMTUTRCASK), Symbol(add_liquidity), Vec(Ok(Address(obj#3609)), Ok(Address(obj#3613)), Ok(I128(4000000000)), Ok(I128(4000000000)), Ok(I128(0)), Ok(I128(0)), Ok(Address(obj#3625)), Ok(U64(1000))), ), ), sub_invocations: [ AuthorizedInvocation { function: Contract( ( Contract(CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45), Symbol(transfer), Vec(Ok(Address(obj#3637)), Ok(Address(obj#3641)), Ok(I128(4000000000))), ), ), sub_invocations: [], }, AuthorizedInvocation { function: Contract( ( Contract(CD6J4NTZEADXNETD2UY7ALD2N26RY35GGC73JSXXZB2GKB3ZTLR3Y4FH), Symbol(transfer), Vec(Ok(Address(obj#3651)), Ok(Address(obj#3655)), Ok(I128(4000000000))), ), ), sub_invocations: [], }, ], }, ), ]

calm cedar
#

Symbol(add_liquidity), that's the first function that's being called, where does it come from?

vale scaffold
#

i am not sure, will check , however my contract calls fn swap_tokens_for_exact_tokens on the DEX , then the DEX calls the transfer function of the token, thats why you see 2 transfer, because it builds it in a loop, ill check the dex swap_tokens_for_exact_tokens to see if it calls add_lquidity but i doubt

calm cedar
#

right, so that's not swap_tokens_for_exact_tokens function at the top, which is why you were getting the error. you need to figure out how you're building add_liquidity call

#

what's weird is that I only see add_liquidity in your test snapshots, but I don't see it actually being defined...

#

maybe you forgot to add it to your repo? I also don't see test.rs

vale scaffold
#

the repo doesnt have the complete code

#

i will update

#

add liquidity is only part of the test setup, so i use it to add liquidity to the dex before calling my contract to execute the swap,

#

so add liquidity is only part of the test setup not part of the contract

#

thats why i dont understand why its showing in auths and not the swap_tokens_for_exact_tokens

calm cedar
#

well, maybe you did dbg! after the setup call?

vale scaffold
#

yes

calm cedar
#

also, if that's just test setup and you don't care about anything being actually signed etc., you should just disable the check; as I've mentioned it's informational and it's aim is to prevent footguns

#
   client
        .mock_all_auths_allowing_non_root_auth()
        .approve(&from, &spender, &20, &200);

is how you can disable the check for you setup call only

vale scaffold
#

add liquidity is ust setup but the swap which causes the error is not

calm cedar
#

this way you want have the error for the setup, but still will get the check for you main logic (which might help catching the real bugs)

#

hmm, then you need to call dbg!(env.auths()) after calling swap_tokens_for_exact_tokens

vale scaffold
#

due the the panic after swap_tokens_for_exact_tokens , it does not print dbg!

calm cedar
#

why does it panic though?

#

the error should no longer happen since you've called env.mock_all_auths_allowing_non_root_auth(), right?

vale scaffold
#

yes , i uncommented it, let me comment back

#

doesnt print out

calm cedar
#

that's not possible

#

it should print out at least an empty vector

#

again, make sure you're using --show-output, as you did above

vale scaffold
#

test.env.auths() = [
(
Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M),
AuthorizedInvocation {
function: Contract(
(
Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5),
Symbol(mint_exact_constellation),
Vec(Ok(I128(1000000)), Ok(I128(10)), Ok(Address(obj#4559)), Ok(Address(obj#4563)), Ok(Address(obj#4567)), Ok(U64(10000000))),
),
),
sub_invocations: [],
},
),
(
Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5),
AuthorizedInvocation {
function: Contract(
(
Contract(CCVQTUQIJR624NNEI5TORM2BHEXTSDMY5ZB3CYJKAATGJQCY7LU2MD45),
Symbol(transfer),
Vec(Ok(Address(obj#4581)), Ok(Address(obj#4585)), Ok(I128(10064))),
),
),
sub_invocations: [],
},
),
]

#

the authorization looks correct ?

calm cedar
#

the second entry is for your top contract

vale scaffold
#

The first is the contract the users intrrect with, the second is the token transfer called by the dex

calm cedar
#

which confirms my hypothesis - your subcontract auth is not correct

#

ah, I think I got it

valid ingot
#

Im pretty sure soroswaps router does the transfer in itself

calm cedar
#
  args.push_back(to.into_val(e));
    args.push_back(pair.into_val(e));
    args.push_back(amount_in.into_val(e));

these are not correct args for transfer

valid ingot
#

U might wanna try calling the pair instead

calm cedar
#

transfer args are from, to, amount

#

like here Vec(Ok(Address(obj#4581)), Ok(Address(obj#4585)), Ok(I128(10064))),1

#

(sorry, address rendering kind of sucks)

#

but you clearly do something else, because from in your case must be env.current_contract_address() and instead of to you have a pair

vale scaffold
#

to is actually my contract which holds the tokens , pair is the dex pair contract which receives the transfer

calm cedar
vale scaffold
#

so to is from at the dex

calm cedar
#

hmm, yet something is still wrong

vale scaffold
#

to is = e.current_contract_address()

#

which is the owner of the tokens being swapped

calm cedar
#

maybe amount is wrong then?

vale scaffold
#

possibly let me check

vale scaffold
#

amount is correct

vale scaffold
calm cedar
#

sorry, I can't really debug the code for you. the links above are also incomplete which makes it hard to understand what's could be wrong (e.g. I'm not sure how did you determine that the amount is correct). I would suggest to either use the debugger, or to use the debug logging in order to see what exactly are you authorizing with the invoker auth and compare it to what has actually been required (you should be able to add dbg! in your contract code as well)