Calling get_esdt_token_data for account that does not have the esdt - elrond

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.

Related

Is it possible to transfer tokens to account declared in remaining accounts?

Try to transfer tokens to one of the account declared in remaining_accounts list.
Here's the way I create CpiContext:
let cpi_context = CpiContext::new(ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.account_x.to_account_info(),
to: ctx.remaining_accounts.first().unwrap().to_account_info(),
authority: ctx.accounts.owner.to_account_info().clone()
});
I got error related to CpiContext lifetime mismatch. Exact log error: lifetime mismatch ...but data from ctx flows into ctx here.
Why I want to use remaining accounts to transfer tokens? This transfer is optional depending on whether user decides to pass the account (referral links/affiliation). Other methods than passing account as remaining accounts to implement the optional transfer will be also highly appreciated.
Make sure that you help out Rust's lifetime inference in your instruction:
pub fn your_instruction<'info>(ctx: Context<'_, '_, '_, 'info, YourContext<'info>>, ...)

Calling a payable method using near-api-js when the current access key has low allowance

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: [...],
}
);

Near protocol equivalent of buring NEAR tokens by sending to address(0)

I want to delete the current contract and burn its NEAR balance when a condition is triggered.
Here's the Solidity version:
selfdestruct(address(0));
I found Promise::delete_account in the Rust SDK but it has a beneficiary_address field. Ideally the funds should be gone forever and not transferred to an owned address.
Promise::new(env::current_account_id()).delete_account(beneficiary_address);
address(0) is address 0x0, a black hole address used to burn Ether.
Currently there is no API to burn NEAR tokens directly. One workaround is to set the beneficiary account id to system. system is an account that can never be created and is used internally for refunds. When the beneficiary account does not exist, the tokens transferred through account deletion are automatically burnt.
I think that is like this:
#[payable]
pub fn burn() {
Promise::new("system".to_string()).transfer(env::attached_deposit());
}
First importing:
use near_sdk::{Promise};

Transferring user token balance to contract in cross contract call

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.

do all NEAR blockchain transactions require a receiver account?

reading through some documentation here and saw that part of the definition of a transaction is that all actions are performed "on top of the receiver's account" and also that the receiver account is "the account towards which the transaction will be routed."
also in the nearlib SDK, the transactions interface includes a method called signTransaction that requires receiverId as a parameter
async function signTransaction(receiverId: string, nonce: number, actions: Action[], blockHash: Uint8Array, signer: Signer, accountId?: string, networkId?: string): Promise<[Uint8Array, SignedTransaction]> {
but looking over the list of transactions supported by nearcore I wonder why do some of these transaction require a receiver.
why would any transactions require a "receiver" except for maybe Transfer, AddKey, DeleteKey, and DeleteAccount?
amd I think of the idea of "receiver" too literally, as in "they receive the outcome or impact of the transaction"? and instead it's not the right way to think about it?
or is receiverId optional in some cases but the interface just requires a value to avoid validation cruft?
here's what I believe to be the full list of supported transactions
pub enum Action {
CreateAccount(CreateAccountAction),
DeployContract(DeployContractAction),
FunctionCall(FunctionCallAction),
Transfer(TransferAction),
Stake(StakeAction),
AddKey(AddKeyAction),
DeleteKey(DeleteKeyAction),
DeleteAccount(DeleteAccountAction),
}
Conceptually every transaction always has a sender and a receiver, even though sometimes they might be the same. Because we always convert a transaction to a receipt that is sent to the receiver, it doesn't matter conceptually whether they are the same, even though in implementation there might be a difference.
Unfortunately, we don't have a good name to denote what we call "receiver". In some places in our code, we also call it "actor", because it actually refers to an account on which the action is performed opposed to an account which issued the action to perform (a.k.a "sender").
DeployContract, Stake, AddKey, DeleteKey require receiver==sender, in other words only an account itself can add/delete keys, stake and deploy a contract on itself, no other account can do it for it.
DeleteAccount is the same, it requires receiver==sender with one exception: If account is about to run out of balance due to storage rent and is below certain system-defined treshold any other account can delete it and claim the remaining balance.
CreateAccount, FunctionCall, and Transfer do not require receiver==sender. In case of the CreateAccount receiver should not exist at the moment of execution and will actually be created.
See the code that implements this logic: https://github.com/nearprotocol/nearcore/blob/ed43018851f8ec44f0a26b49fc9a863b71a1428f/runtime/runtime/src/actions.rs#L360

Resources