I'm writing a simple DApp on TON blockchain and using tonweb to interact with it.
I need to send some transaction and after it confirms on chain, perform some other things in JS.
Example:
await ton.send('ton_sendTransaction', [{
to: 'some address',
value: '1000'
}]
)
// wait for tx to confirm on chain
console.log('Done!')
But I don't understand how can I do that using tonweb
Simple approach would be saving last user's transaction and run a loop until user's address will have new transacations.
You can do it like that with tonweb:
// Get user's wallet address from TON wallet browser extension
const address = (await ton.send('ton_requestAccounts'))[0]
// Get user's last transaction hash using tonweb
const lastTx = (await tonweb.getTransactions(address, 1))[0]
const lastTxHash = lastTx.transaction_id.hash
// Send your transaction
await ton.send('ton_sendTransaction', [{
to: 'some address',
value: '1000'
}]
)
// Run a loop until user's last tx hash changes
var txHash = lastTxHash
while (txHash == lastTxHash) {
await sleep(1500) // some delay between API calls
let tx = (await tonweb.getTransactions(address, 1))[0]
txHash = tx.transaction_id.hash
}
console.log('Done!')
Related
I am not able to transfer NFT using #hashgraph/SDK hedera npm. How to transfer nft using #hashgraph/SDK's inbuilt method?
Also, Nft is already associated with the seller account.
Also, I am transferring token which is available in hashpack nft section.
I am getting the below error on calling the TransferTransaction method of #hashgraph/sdk#2.17.1.
Code:
const TransferNft = async (tokenId, sellerAccount, sellerId, buyerAccount, buyerId) => {
try {
const client = await getClient();
tokenId = TokenId.fromString(tokenId);
let sellerKey = await getPrivateKey(sellerId);
sellerKey = PrivateKey.fromString(sellerKey);
let buyerKey = await getPrivateKey(buyerId);
buyerKey = PrivateKey.fromString(buyerKey);
// 2nd NFT TRANSFER NFT Alice->Bob
let tokenTransferTx2 = await new TransferTransaction()
.addNftTransfer(tokenId, 2, sellerAccount, buyerAccount)
.addHbarTransfer(sellerAccount, Hbar.fromTinybars(100))
.addHbarTransfer(buyerAccount, Hbar.fromTinybars(-100))
.freezeWith(client)
.sign(sellerKey);
let tokenTransferTx2Sign = await tokenTransferTx2.sign(buyerKey);
let tokenTransferSubmit2 = await tokenTransferTx2Sign.execute(client);
let tokenTransferRx2 = await tokenTransferSubmit2.getReceipt(client);
console.log(`\n NFT transfer Alice->Bob status: ${tokenTransferRx2.status} \n`);
return tokenTransferRx2.status;
} catch (error) {
console.log('Error in HederaToken/TransferNft/TransferNft: \n', error)
}
};
receipt for transaction 0.0.40217130#1663228521.315536859 contained error status TOKEN_NOT_ASSOCIATED_TO_ACCOUNT
Error:
Before an account that is not the treasury for an HTS token can receive or send a specific token ID, they must be “associated” with the token — this helps reduce unwanted spam and other concerns from users that don’t want to be associated with any of the variety of tokens that are created on the Hedera network.
This association between an account and a token ID can be done in two ways, manually or automatically. Note that automatic associations can be done for both existing and newly created accounts.
Here's how you would do an auto-association for an existing account via an account update:
// AUTO-ASSOCIATION FOR ALICE'S ACCOUNT
let associateTx = await new AccountUpdateTransaction()
.setAccountId(aliceId)
.setMaxAutomaticTokenAssociations(100)
.freezeWith(client)
.sign(aliceKey);
let associateTxSubmit = await associateTx.execute(client);
let associateRx = await associateTxSubmit.getReceipt(client);
console.log(`Alice NFT Auto-Association: ${associateRx.status} \n`);
Here's how you would do a manual association for an existing account:
// MANUAL ASSOCIATION FOR BOB'S ACCOUNT
let associateBobTx = await new TokenAssociateTransaction()
.setAccountId(bobId)
.setTokenIds([tokenId])
.freezeWith(client)
.sign(bobKey);
let associateBobTxSubmit = await associateBobTx.execute(client);
let associateBobRx = await associateBobTxSubmit.getReceipt(client);
console.log(`Bob NFT Manual Association: ${associateBobRx.status} \n`);
We have a transaction that transfers SOL & an NFT from the wallet's owner to our wallet.
The transaction contains simple instructions:
Token.createAssociatedTokenAccountInstruction
Conditionnal, depending on the destination (our wallet)
Token.createTransferInstruction
Transfers from the owner's token account to our token account.
SystemProgram.transfer
SOL transfer.
Here is the transfer code (spl-token v0.1.8)
let createTransferInstructions = async function (mint, from, to, cluster) {
let connection = new Connection(cluster, "confirmed")
const mintPublicKey = new PublicKey(mint);
const ownerPublicKey = new PublicKey(from);
const destPublicKey = new PublicKey(to);
// GET SOURCE ASSOCIATED ACCOUNT
const associatedSourceTokenAddr = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
mintPublicKey,
ownerPublicKey
);
// GET DESTINATION ASSOCIATED ACCOUNT
const associatedDestinationTokenAddr = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
mintPublicKey,
destPublicKey
);
const receiverAccount = await connection.getAccountInfo(associatedDestinationTokenAddr);
const instructions = [];
if (receiverAccount === null)
instructions.push(
Token.createAssociatedTokenAccountInstruction(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
mintPublicKey,
associatedDestinationTokenAddr,
destPublicKey,
ownerPublicKey
)
)
instructions.push(
Token.createTransferInstruction(
TOKEN_PROGRAM_ID,
associatedSourceTokenAddr,
associatedDestinationTokenAddr,
ownerPublicKey,
[],
1
)
);
return instructions;
}
Here is the transaction code:
var transaction = new this.solana.Transaction({
feePayer: new this.solana.PublicKey(from),
recentBlockhash: await blockhashObj.blockhash
});
for (let transferInstruction of transferInstructions) {
transaction.add(transferInstruction);
}
transaction.add(this.solana.SystemProgram.transfer({
fromPubkey: new this.solana.PublicKey(this.provider.publicKey),
toPubkey: new this.solana.PublicKey(farm.address),
lamports: 10000000
}));
The owners have to validate the transaction with their wallets (phantom, solflare etc..)
And everything works great, for most NFTs.
Some NFT owners are complaining about not being able to validate the transaction on the wallet, an error message indicates that the transaction can't be confirmed.
However, the NFT can be transferred successfully using the wallet's interface, can be listed on marketplaces etc. Even when the NFT is transferred to another wallet, the transaction above works perfectly.
I'm wondering if this has anything to do with the associated token account?
If so, what is the solution?
I'm trying to issue some Fabtokens to users and then use them in various scenarios such as transferring, redeeming, etc. I follow the Node SDK documentation here: https://fabric-sdk-node.github.io/master/tutorial-fabtoken.html
This is how they do the Fabtoken operations:
// create a TokenClient instance from client
const tokenclient = client.newTokenClient(mychannel);
// create a transaction ID for "issuer"
const txId = client.newTransactionID();
// create two parameters for issue, one for user1 and one for user2
const param1 = {
owner: user1.getIdentity().serialize(),
type: 'USD',
quantity: '500',
};
const param2 = {
owner: user2.getIdentity().serialize(),
type: 'EURO',
quantity: '300',
};
// create the token request for issue
const issueRequest = {
params: [param1, param2],
txId: txId,
};
// issuer calls issue method to issue tokens to user1 and user2
const result = await tokenClient.issue(issueRequest);
And then use a different tokenClient to list the tokens of user 1:
const user1Tokenclient = client1.newTokenClient(mychannel);
// user1 lists tokens
const mytokens = await user1TokenClient.list();
// iterate the tokens to get token id, type, and quantity for each token
for (const token of tokens) {
// get token.id, token.type, and token.quantity
// token.id will be used for transfer and redeem
}
It's mentioned on the Node SDK's Client class page here: https://fabric-sdk-node.github.io/master/Client.html that switching userContexts with the same client instance is an anti-pattern and not recommended since client instances are stateful.
As they suggest, I create my client instances with different user contexts. This is how I create my clients, set their user context and create my tokenClient instances:
const adminClient = new Fabric_Client();
const admin = await adminClient.createUser(user_opts);
adminClient.setUserContext(admin, true);
let adminConfig = {
admin: admin,
adminClient: adminClient,
adminTokenClient: adminClient.newTokenClient(channel)
}
const server = await serverClient.createUser(server_opts);
serverClient.setUserContext(server, true);
let serverConfig = {
server: server,
serverClient: serverClient,
serverTokenClient: serverClient.newTokenClient(channel)
}
Later on, I'm using these config objects to issue some tokens to different users. How I issue tokens to my server account from my issuer (admin) account:
const txId = adminConfig.adminClient.newTransactionID();
let issueQuery = {
tokenClient: adminConfig.adminTokenClient,
txId: txId,
channel: channel,
params: []
}
for(let i=0; i < 3; ++i) {
let param = {
owner: serverConfig.server.getIdentity().serialize(),
type: 'test',
quantity: '1'
}
issueQuery.params.push(param);
}
let issueTx = await waitForIssue(issueQuery);
This successfully issue three tokens to the server as expected. The problem is that when I try to access to the tokens of my server like the example they provide using a similar code:
let server_tokens = await serverConfig.serverTokenClient.list();
for (let server_token of server_tokens) {
console.log(server_token.id);
}
Result is just empty and I don't get any error messages. However, when I check the transaction using queryTransaction(txId) for the token issue transaction I generate, I can see that owner of the issued tokens in that transaction is the server and that's how I can be sure that I can successfully issue the tokens to the server. Is there any other way to check the tokens of my server? Or shouldn't I use a different client and user context per each user as they suggest? Because, previously I was able to see the tokens of the server when I used a single client and single user context to issue and list tokens. But this approach caused me problems when I was trying to transfer my tokens asynchronously.
As far as I know FabTokens are removed from the Master branch of Fabric 2.0 now as described in these links:
https://gerrit.hyperledger.org/r/c/fabric/+/32979
https://lists.hyperledger.org/g/fabric/topic/fabtoken/34150195?p=,,,20,0,0,0::recentpostdate%2Fsticky,,,20,2,0,34150195
I would expect the tutorial and the information in the Fabric docs to be removed in due course.
I can not understand how I can prepare the transaction, sign it and then send it using INFURA. I expect to get something wrong with the NodeJS code I posted. Can anyone kindly help me solve this problem?
Note: I am able to send the transaction on my smart contract, but the EVM reverts the execution.
Part of the Smart Contract (Solidity) (the function that I want to call):
function testFunction() public pure returns (string memory) {
return "testSuccess";
}
The function used in NodeJS:
const printRequestTransaction = async (gcodeHash) => {
log(`Preparing a transaction to register that the gcode has been sent`.yellow);
// With every new transaction send using a specific wallet address, it needed to increase a nonce which is tied to the sender wallet
let nonce = await web3.eth.getTransactionCount(web3.eth.defaultAccount);
// Fetch the current transaction gas prices from https://ethgasstation.info/
let gasPrices = await getCurrentGasPrices();
// Build a new transaction object and sign it locally
let details = {
"to": process.env.SMART_CONTRACT_ADDRESS,
//"value": web3.utils.toHex(web3.utils.toWei(amountToSend.toString(), 'ether')),
"gas": 210000,
"gasPrice": gasPrices.low * 1000000000, // converts the gwei price to wei
"nonce": nonce,
//"data": gcodeHash,
"function": "testFunction",
"chainId": 3 // EIP 155 chainId - mainnet: 1, ropsten: 3, rinkeby: 4 (https://ethereum.stackexchange.com/questions/17051/how-to-select-a-network-id-or-is-there-a-list-of-network-ids/17101#17101)
};
const transaction = new EthereumTx(details);
// This is where the transaction is authorized on Local PC behalf. The private key is what unlocks Local PC wallet.
transaction.sign(Buffer.from(process.env.WALLET_PRIVATE_KEY, 'hex'));
// Compress the transaction info down into a transportable object
const serializedTransaction = transaction.serialize();
// The Web3 library is able to automatically determine the "from" address based on Local PC private key
// Submit the raw transaction details to the provider configured above (INFURA)
const transactionHash = await web3.eth.sendSignedTransaction('0x' + serializedTransaction.toString('hex')).then(function (receipt) {
log(`result of the invokation: ${receipt})`.red);
return receipt.transactionHash;
}).catch((err) => {
log(`error occurred: ${err})`.red);
});
//const transactionHash = "exampleHash";
_transactionHash = transactionHash;
// Now it is known the transaction ID, so let's build the public Etherscan url where the transaction details can be viewed.
const url = `https://ropsten.etherscan.io/tx/${transactionHash}`;
log(url.cyan);
log(`Note: please allow for 30 seconds before transaction appears on Etherscan`.yellow)
};
Output:
error occurred: Error: Transaction has been reverted by the EVM:
{
"blockHash": "0x6205c1b8ce2fde3693e85843dce684ff11472e9df01fd850bc99e5771b3262d5",
"blockNumber": 5024524,
"contractAddress": null,
"cumulativeGasUsed": "0x50170f",
"from": "0x8882528C7104e146E0500203C353C09922575385",
"gasUsed": "0x5236",
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x0",
"to": "0x1732089F6F6EeFA942871707c5AE1909B60774EB",
"transactionHash": "0xf748c9a6bdb19e776db4d8a9802d2bf5f8b588158da387029529249aca00a499",
"transactionIndex": 1
})
I would like to pay particular attention to the let details = {...}; object for which I am not very sure it is correct, I have a little confusion about this.
I am trying to create a website where ethereum transaction can be made.
If I make an eth transaction using eth.sendTransaction({from:sender, to:receiver, value: amount}) can this transaction be cancelled?
I am asking this because I see that it doesn't take any promise or callback parameters, meaning I would have no idea whether the transaction has been made successfully or not?
Is it possible for web3 transactions to be cancelled? and if it is, how do I make sure that I get notified whether transaction has been made or not? (preferrably using promises or callback rather than having to check wallet every time)
It’s technically possible to cancel a transaction, but highly unlikely. Essentially, the only way to do so would be to try to send another transaction using the same account/nonce combination and hope it gets mined before the transaction you want to cancel is mined. It’s merely a side effect of how nonce is used to guarantee transaction order rather than a cancellation feature.
For your other question, web3.eth.sendTransaction does take a callback. It’s an optional second parameter after the options object and uses the error/result callback style. From the Web3js API:
web3.eth.sendTransaction(transactionObject [, callback])
Function - (optional) If you pass a callback the HTTP request is made asynchronous.
Using callbacks
If you want to make an asynchronous request, you can pass an optional callback as the last parameter to most functions. All callbacks are using an error first callback style
To cancel some ethereum pending transaction you need to:
Create a new transaction where you will send 0 ETH to yourselves.
You should increase the value of gas fees
You need to use the same nonce of the pending transaction.
Using web3js you can do something like this:
async function main() {
require('dotenv').config();
const { API_URL, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const myAddress = //put your address
const nonce = //put the nonce of pending tx
const maxFeePerGas = //increase the gas fee value comparated to the pending tx
const transaction = {
'to': myAddress,
'value': 0,
'gas': 21000,
'maxFeePerGas': maxFeePerGas,
'nonce': nonce
};
const signedTx = await web3.eth.accounts.signTransaction(transaction, PRIVATE_KEY);
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function(error, hash) {
if (!error) {
console.log("The hash of your transaction is: ", hash);
} else {
console.log(error)
}
});
}
main();