Contract A
#[payable]
pub fn add_liquidity(&mut self, tokens: u128, avrit_id: AccountId) -> u128 {
let amount = env::attached_deposit();
Promise::new(avrit_id).function_call(
b"transfer".to_vec(),
json!({"new_owner_id":"avrit.testnet", "amount": U128(tokens)}).to_string().as_bytes().to_vec(),
38600000000000000000000,
env::prepaid_gas() - GAS_FOR_SWAP,
);
amount
}
Promise function call is done by contract A instead of the person who calls the add_liquidity.
How to call the Promise where predecessor is user who calls add_liquidity instead of contract A address? Explorer
You're right. It's not possible to make a Promise function call where the predecessor is some other account than the current one.
This is an important security feature of contract development on NEAR. Because if you could have spoofed the predecessor account ID, then you'd be able to pretend to be this account and do actions on behalf of this account.
Instead when a contract makes a cross-contract call, the predecessor becomes this contract instead of the transaction signer. The receiving contract will be able to see which account called it directly.
Related
I´m trying to write a function that returns the balance of the account where the contract is deployed.
Something with the following form:
pub fn get_balance(&self) -> Balance {
env::account_balance()
}
The above code returns the balance of the account that is calling the method, the signer. Is there a way to make it returns the balance of the contract account?
I got the answer. near_sdk:env::account_balance() already do what i was looking for. It retrieves the balance of the contract account.
I was lacking a way to check it properly. To do it, i used near state [account_name] on the cli to verify if the balance returned was the master or subaccount (where the contract was deployed) balance.
So the answer would be:
pub fn get_balance(&self) -> Balance {
env::account_balance()
}
noting that to have the transferible balance, it would have to take into account the min balance used for storage.
Considering that
get_esdt_token_data(address: &ManagedAddress, token_id: &TokenIdentifier, nonce: u64) -> EsdtTokenData<Self::Api>
always returns an EsdtTokenData rather than an option. What will this object look like if the address does not own the specified token?
The execution will fail as the VM will not return anything to the smart contract if it doesn't find the token.
The typical usage for this function is to get the data for the payment tokens the smart contract receives from the caller. If you're trying to use it freely, you might get into this situation, so this type of "free" usage is not really advised.
I have a NEAR application in which most of the methods do not transfer any tokens. Users go through the standard login flow with NEAR wallet, have their 0.25N allowance for gas, and then interact with the application.
I now have a new end-point that I want to be callable from the front-end, which expects the user to pay an amount significantly exceeding 0.25N:
#[payable]
pub fn buy_stuff() {
When I use the standard near-api-js way to call it:
window.contract.buy_stuff({}, undefined, price).then(m => window.location.href='/');
it fails, because it tries to spend the price from the allowance of the aceess key logged in, which it doesn't have -- it naturally only has 0.25N it has for gas.
The specific error is "Access Key {account_id}:{public_key} does not have enough balance 247864837491516400000000 for transaction costing 5004231023352653388973496"
What I want instead is to get the user redirected to the wallet, and authorize this particular transaction using their full access key in the wallet. Is there a way to do that with near-api-js?
It looks like the issue is that the contract API doesn't consider a wallet redirect. One reason might be the initialization of the contract API.
In order for a contract API to be able to redirect to a wallet, the initialization should be done with the ConnectedWalletAccount. It can be done using the following code:
const nearConnection = await nearAPI.connect(...);
const walletConnection = new nearAPI.WalletConnection(
nearConnection,
ContractName
);
const contract = new nearAPI.Contract(
walletConnection.account(),
ContractName,
{
viewMethods: [...],
changeMethods: [...],
}
);
The docs says:
Payable methods. We can allow methods to accept token transfer
together with the function call. This is done so that contracts can
define a fee in tokens that needs to be payed when they are used. By
the default the methods are not payable and they will panic if someone
will attempt to transfer tokens to them during the invocation. This is
done for safety reason, in case someone accidentally transfers tokens
during the function call.
https://github.com/near/near-sdk-rs
Does the token here mean only native near tokens?
Whey this function have to be payable? To pay near tokens for storage?
/// Transfer `amount` of tokens from the caller of the contract (`predecessor_id`) to
/// `new_owner_id`.
/// Act the same was as `transfer_from` with `owner_id` equal to the caller of the contract
/// (`predecessor_id`).
/// Requirements:
/// * Caller of the method has to attach deposit enough to cover storage difference at the
/// fixed storage price defined in the contract.
#[payable]
pub fn transfer(&mut self, new_owner_id: AccountId, amount: U128) {
// NOTE: New owner's Account ID checked in transfer_from.
// Storage fees are also refunded in transfer_from.
self.transfer_from(env::predecessor_account_id(), new_owner_id, amount);
}
https://github.com/near-examples/FT/blob/master/contracts/rust/src/lib.rs#L211
Does the token here mean only native near tokens?
Yes
Whey this function have to be payable? To pay near tokens for storage?
If you want this function to be able to accept deposit. How the deposit is going to be used is up to the contract to decide.
If there is a chain of actions that need to be executed inside NEAR's smart contract and is chained via callbacks from given contract - how to allocate gas for them.
For example:
pub fn some_method(&mut self, ...) -> Promise {
...
Promise::new(other_contract).function_call(...).then(
ext_self::callback(env::current_account_id(), 0, ????)
)
}
pub fn callback(&mut self) -> PromiseOrValue<bool> {
if some_condition {
self.some_method(...)
} else {
PromiseOrValue::Value(true)
}
}
What should be ???? to correctly allocate gas for this chain of actions?
The correct amount of gas allocation depends on the remote method. The amount of gas is just the amount of prepaid gas that will be available at the remote function call. It doesn't include fees for calling the contract or creating a promise. They will charged separately.
The fees can be vary based on what the remote method is going to do.
Let's say it's just a simple local method without external promises. If it doesn't do a lot of compute and doesn't access a lot of storage, then the execution of it going to be fairly cheap. So you may attach about 10**13 gas.
But if the remote method is going to call other promises and make external calls, or has heavier compute, then you need to account for this.
It's also the case for the local callback that you attach using .then. Use the same logic when estimating the amount of prepaid gas needed.