Reputation: 945
I have a contract called exchange
. A user, Bob
, wants to spend near tokens
to purchase positions in markets
through the means of placing an Order
.
Order
has a field called owner
, it's important that the contract has proof that a certain order
is owned by a certain address in this case Bob
.
How transactions send to exchange
are currently handled is a flow similar to:
const init = async () => {
this.near = await window.nearlib.connect(Object.assign({ deps: { keyStore: new window.nearlib.keyStores.BrowserLocalStorageKeyStore() } }, window.nearConfig));
this.walletAccount = new window.nearlib.WalletAccount(this.near);
this.accountId = this.walletAccount.getAccountId();
this.exchange = await this.near.loadContract(window.nearConfig.contractName, {
viewMethods: [],
changeMethods: ["place_new_order"],
sender: this.accountId,
});
}
await init();
// Bob logs into to near
this.walletAccount.requestSignIn(
window.nearConfig.contractName,
window.nearConfig.contractName,
);
// Login process
[...]
// Bob wants to place a new order
this.exchange.place_new_order(price, amount);
The exchange
contract imports a struct
called Order
.
Order
would look like:
use std::string::String;
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, BorshDeserialize, BorshSerialize, Clone)]
pub struct Order {
pub owner: String,
pub amount: u64,
pub price: u64,
}
impl Order {
pub fn new(owner: String, amount: u64, price: u64) -> Self {
Order {
owner,
amount,
price
}
}
}
Exchange
is, in this case, the contract that implements the mod order
. Exchange
has the place_new_order
method which is where I want to be able to make sure that Bob
is the one who sent the transaction:
use near_bindgen::{near_bindgen, env};
[...]
mod Order;
[...]
pub fn place_new_order(&mut self, amount: u64, price: u64) {
// Stuff happens here
let order = Order::new(env::current_account_id(), amount, price);
// Stuff happens here
}
Now the thing is that in case, using this nearlib code env::current_account_id()
will always return exchange
as the current_account_id
. This makes sense because all the login does is create an access_key
which allows exchange
to do a couple of things in the name of Bob
but it's still Exchange
signing off on transactions.
The question here is: How do I ensure that that exchange knows that Bob
is the one who initialized the transaction?
A way this could work that would make sense:
Bob
signs every transaction with his private key, the question I'd have here is: how?This would cause a Metamask-like UX problem where signing off on every tx is bad UX.
What I'd propose is the following:
Bob
uses a deposit
method to deposit a set amount of near-tokens
on exchange
and exchange
is allowed to spend that as long as it can proof that it's doing it through a "login-access-token" that is signed by Bob
, the question here again is: is this possible and if so how?Upvotes: 2
Views: 108
Reputation: 1078
You should use env::predecessor_account_id()
to get the account ID of a user or a contract who called a method on exchange.
In your case, even through the access key points towards the exchange
it still signed by bob. The
signer_idand the
predecessor_idduring the execution are both going to be
bob`.
There are 3 different types of accounts during the execution:
env::current_account_id()
returns the current account ID of a contract that is executing right now.env::signer_account_id()
returns the account ID of a user who signed the original transaction. Most of the time you should never rely on the signer account ID, cause the current call can be done from another contract.env::predecessor_account_id()
return the account ID of immediate predecessor who called the current contract. You should use it to verify the identity of a user who called you.Upvotes: 2