My smart contract owns the SFT HAT-a1a1a1-01.
The SFT HAT-a1a1a1-02 also exists but isn't owned by the SC.
When I add local quantity to the SFT with the 02 nonce through a function, my transaction fails with this error:
new NFT data on sender
Do I need to own the SFT HAT-a1a1a1-02 to mint it?
I got an answer in another group:
Yes, you need to own at least 1 to AddQuantity, since otherwise, you wouldn't have the attributes. When you send an NFT/SFT, if your balance after is 0, the metadata is erased from your account.
Related
I am currently experimenting a bit with Solana accounts and I am wondering if it is possible to change the owner of an account.
I am curious it is possible since the security of some programs are relying on this owner check, as explained here
I am also looking into the assign function and on how it works, but I was not able to make it work yet.
Am I misunderstanding something or just was not able to make it work?
Here the Python code is used:
tx = Transaction().add(
create_account(CreateAccountParams(
from_pubkey=attacker_keypair.public_key,
new_account_pubkey=account_keypair.public_key,
lamports=client.get_minimum_balance_for_rent_exemption(0)["result"],
space=0,
program_id=attacker_keypair.public_key,
))
)
send_and_confirm_tx(client, tx, attacker_keypair, account_keypair)
print('Sending 1st tx to program')
tx = Transaction().add(TransactionInstruction(
keys=[
AccountMeta(pubkey=account_keypair.public_key, is_signer=False, is_writable=False),
],
program_id=PROGRAM_ID,
))
send_and_confirm_tx(client, tx, attacker_keypair)
print('Sending 2nd tx to program')
tx = Transaction().add(assign(AssignParams(
account_pubkey=account_keypair.public_key,
program_id=attacker2_keypair.public_key
)))
send_and_confirm_tx(client, tx, account_keypair)
Error message is: InvalidAccountForFee
account_keypair was already assigned to attacker_keypair, so when you try to use it as the fee payer in 2nd tx, the runtime yells at you, since account_keypair can only have its lamports reduced by the program living at attacker_keypair. To quickly fix your current issue, you can do:
print('Sending 2nd tx to program')
tx = Transaction().add(assign(AssignParams(
account_pubkey=account_keypair.public_key,
program_id=attacker2_keypair.public_key
)))
send_and_confirm_tx(client, tx, attacker_keypair, account_keypair)
This will cause another problem however. account_keypair is already owned by attacker_keypair, so only a program deployed to attacker_keypair can reassign ownership of account_keypair. You can read up more on the Solana Account model at: https://docs.solana.com/developing/programming-model/accounts#ownership-and-assignment-to-programs
The part that's interesting to you is:
The owner is a program id. The runtime grants the program write access to the account if its id matches the owner. For the case of the System program, the runtime allows clients to transfer lamports and importantly assign account ownership, meaning changing the owner to a different program id. If an account is not owned by a program, the program is only permitted to read its data and credit the account.
This means that to reassign ownership, you need to write an on-chain program that reassigns the ownership to attacker2_keypair, deploy it to attacker_keypair, and then send a transaction containing an instruction to attacker_keypair.
Here are some example programs that perform the assign on the AccountInfo: https://github.com/solana-labs/solana/blob/85a2e599bbbf3d51f201167f921718e52c7ce59f/programs/bpf/rust/realloc/src/processor.rs#L54
I am using near-contract-standards for NFT.
In the example, it has nft_mint which takes token owner id and token id to mint the token.
#[payable]
pub fn nft_mint(
&mut self,
token_id: TokenId,
token_owner_id: ValidAccountId,
token_metadata: TokenMetadata,
) -> Token {
self.tokens.mint(token_id, token_owner_id, Some(token_metadata))
}
}
Then token owner id can do the nft transfer as predecessor to another account.
https://github.com/near-examples/NFT/blob/master/nft/src/lib.rs
contract.nft_mint(token_id.clone(), accounts(0), sample_token_metadata());
testing_env!(context
.storage_usage(env::storage_usage())
.attached_deposit(1)
.predecessor_account_id(accounts(0))
.build());
contract.nft_transfer(accounts(1), token_id.clone(), None, None);
I want to do nft tranfer with a buy price for nft, where predecessor is the user who want to buy the nft, instead of token owner, and then transfer the money to token owner.
How can I do that? Will I use internal_transfer or I will set the price in nft_mint function, and mint the token directly to buyer account id?
If I understand the question correctly you'd like to combine nft_mint with some sort of payable action that will pay the original creator of some "NFT token type"?
You can check the NFT Market repo here: https://github.com/near-apps/nft-market
This repository uses a separate market account and contract to allow NFT Owners to put NFTs up for sale. A little more than what you are looking for I think.
There is also a variant that uses "lazy minting" to only mint the NFTs when a user buys them from the market contract: https://github.com/near-apps/gnr8/
Again, it's a little more than what I think you want because the payment and buying is happening in a separate contract and the NFT transfer and minting is happening in the NFT contract, which is called from the market contract.
If you're looking for something to add your own payable NFT method to, you could start with this:
https://github.com/near-apps/nft-series/
Which allows you to define an NFT type and you could charge users to mint 1/N of that type by making the nft_mint_type method payable and NOT require the owner of the type to be the minter.
FYI these approaches are non-standard, but work.
Feel free to reach out directly on Discord if you need any help with the examples above.
Can any one please help me on the transaction/operation types in Near where value/near is involved.I have seen multiple operation types like transfer , draw etc
There are only 7 native action kinds in NEAR Protocol:
Transfer (deposit gets transferred from a signer to a receiver account)
CreateAccount
DeleteAccount (the remaining funds on an account are transferred to the beneficiary account id)
CallFunction (tokens can be deposited [attached] to the function call, e.g. draw method expects some tokens attached)
AddKey
DeleteKey
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};
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