I have minted some NFTs on opensea. These are on Polygon Mumbai network. Now I want to transfer these to token to other addresses using alchemy web3. Here is the code I am using.
Note: This is supposed to run in nodejs restful API, so there is no wallet available that why I am manually signing the transaction.
async function main() {
require('dotenv').config();
const { API_URL,API_URL_TEST, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL_TEST);
const myAddress = '*************************'
const nonce = await web3.eth.getTransactionCount(myAddress, 'latest');
const transaction = { //I believe transaction object is not correct, and I dont know what to put here
'asset': {
'tokenId': '******************************',//NFT token id in opensea
},
'gas': 53000,
'to': '***********************', //metamask address of the user which I want to send the NFT
'quantity': 1,
'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, "\n Check Alchemy's Mempool to view the status of your transaction!");
} else {
console.log("âť—Something went wrong while submitting your transaction:", error)
}
});
}
main();
Assumed that you have Metamask installed in your browser, and that the NFT smart contract follows ERC721 Standard
const { API_URL,API_URL_TEST, PRIVATE_KEY } = process.env;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const {abi} = YOUR_CONTRACT_ABI
const contract_address = CONTRACT ADDRESS
require('dotenv').config();
async function main() {
const web3 = createAlchemyWeb3(API_URL_TEST);
web3.eth.getAccounts().then(accounts => {
const account = account[0]
const nameContract = web3.eth.Contract(abi, contract_address);
nameContract.methods.transfer(account, ADDRESS_OF_WALLET_YOU_WANT_TO_SEND_TO, TOKEN_ID).send();
})
.catch(e => console.log(e));
}
main();
Had the same problem, because there is no example on transferring NFT token.
There is a well explained 3-parts-example on ethereum website to mint an NFT.
In the first part, step 10, it explains how to write a contract and also mentions the existing methods in the contract object extended:
After our import statements, we have our custom NFT smart contract, which is surprisingly short — it only contains a counter, a constructor, and single function! This is thanks to our inherited OpenZeppelin contracts, which implement most of the methods we need to create an NFT, such as ownerOf which returns the owner of the NFT, and transferFrom, which transfers ownership of the NFT from one account to another.
So, with these informations, I made an NFT transfer transaction between two addresses with my metamask mobile app. Then I searched the JSON of this transaction through etherscan API.
In this way, I was able to transfer tokens to other addresses using alchemy web3 with this script:
require("dotenv").config()
const API_URL = process.env.API_URL; //the alchemy app url
const PUBLIC_KEY = process.env.PUBLIC_KEY; //my metamask public key
const PRIVATE_KEY = process.env.PRIVATE_KEY;//my metamask private key
const {createAlchemyWeb3} = require("#alch/alchemy-web3")
const web3 = createAlchemyWeb3(API_URL)
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json")//this is the contract created from ethereum example site
const contractAddress = "" // put here the contract address
const nftContract = new web3.eth.Contract(contract.abi, contractAddress)
/**
*
* #param tokenID the token id we want to exchange
* #param to the metamask address will own the NFT
* #returns {Promise<void>}
*/
async function exchange(tokenID, to) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest');
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'input': nftContract.methods.safeTransferFrom(PUBLIC_KEY, to, tokenID).encodeABI() //I could use also transferFrom
};
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
signPromise
.then((signedTx) => {
web3.eth.sendSignedTransaction(
signedTx.rawTransaction,
function (err, hash) {
if (!err) {
console.log(
"The hash of your transaction is: ",
hash,
"\nCheck Alchemy's Mempool to view the status of your transaction!"
)
} else {
console.log(
"Something went wrong when submitting your transaction:",
err
)
}
}
)
})
.catch((err) => {
console.log(" Promise failed:", err)
})
}
I Had the same problem. I need to transfer NFT in node.js back-end.
I use network provider to use Moralis NetworkWeb3Connector.
here's my repository for example:
https://github.com/HanJaeJoon/Web3API/blob/2e30e89e38b7b1f947f4977a0fe613c882099fbc/views/index.ejs#L259-L275
await Moralis.start({
serverUrl,
appId,
masterKey,
});
await Moralis.enableWeb3({
// rinkeby
chainId: 0x4,
privateKey: process.env.PRIVATE_KEY,
provider: 'network',
speedyNodeApiKey: process.env.MORALIS_SPEEDY_NODE_API_KEY,
});
const options = {
type,
receiver,
contractAddress,
tokenId,
amount: 1,
};
try {
await Moralis.transfer(options);
} catch (error) {
console.log(error);
}
you can get speed node api key in
Moralis dashboad > Networks > Eth Rinkeby(in my case) > Settings
screenshot
Related
I have a smart contract for sending money user-to-user.
In this smart contract, I pass the smart contract address from the Kovan network when the user selects a coin.
In this case, I pass the ChainLink contract address to my contract for sending the Chain Link token to the user.
This is ChainLink contract address in the Kovan network: 0xa36085f69e2889c224210f603d836748e7dc0088.
Now I want to test contract functions with this code:
const assert = require("assert");
const Dex = artifacts.require("Dex");
contract("Dex", (accounts) => {
let dex;
let contractOwner = null;
let buyer = null;
beforeEach(async () => {
contractOwner = accounts[0];
buyer = accounts[1];
dex = await Dex.new();
contractOwner = accounts[0];
// sometimes you need to test a user except contract owner
buyer = accounts[1];
});
// it("Add tokens and contract address Success", async () => {
// const tokenInfo = await dex.getTokenContractAddress("USDT");
// assert(tokenInfo == "0xdac17f958d2ee523a2206206994597c13d831ec7");
// })
// it("Add Token", async () => {
// await dex.addToken(web3.utils.fromAscii("LINK"), "0xa36085f69e2889c224210f603d836748e7dc0088", "0xa36085f69e2889c224210f603d836748e7dc0088")
// })
it("Deposit", async () => {
await dex.deposit("0xa36085f69e2889c224210f603d836748e7dc0088", "0x5226a51522C23CcBEFd04a2d4C6c8e281eD1d680", "0xB643992c9fBcb1Cb06b6C9eb278b2ac35e6a2711", 1,
// you were missing this
{ from: accounts[0] });
})
})
I Using Truffle in Project:
Truffle Config:
const HDWalletProvider = require('#truffle/hdwallet-provider');
const fs = require('fs');
const mnemonic = fs.readFileSync(".secret").toString().trim();
const secrets = JSON.parse(
fs.readFileSync('.secrets.json').toString().trim()
);
module.exports = {
networks: {
kovan: {
networkCheckTimeout: 10000,
provider: () => {
return new HDWalletProvider(
mnemonic,
`https://kovan.infura.io/v3/1461728202954c07bd5ed5308641a054`,
0,
20
);
},
network_id: "42",
},
},
// Set default mocha options here, use special reporters, etc.
mocha: {
},
// Configure your compilers
compilers: {
solc: {
version: "0.8.10",
docker: false,
settings: {
optimizer: {
enabled: false,
runs: 200
},
evmVersion: "byzantium"
}
}
},
};
My Contract:
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
// import "./UserCoins.sol";
contract Dex {
event Approval(
address indexed tokenOwner,
address indexed spender,
uint256 tokens
);
event Transfer(address indexed from, address indexed to, uint256 tokens);
constructor() {}
function deposit(
address ticker,
address sender,
address recipient,
uint256 amount
) external payable {
IERC20 token = IERC20(ticker);
IERC20(ticker).approve(sender, amount);
// Transfer Token To User
token.transferFrom(sender, recipient, amount);
emit Transfer(sender, recipient, amount);
}
}
it shows me this error:
Warning: Could not decode event!
execution failed due to an exception. Reverted
How can I solve this problem?
function deposit(address ticker,address sender,address recipient,uint256 amount
)
external payable
this function takes 4 args, you passed all the parameters but since it is payable, you have to also make sure from which account you are calling, so you need to add 5th parameter to the function:
await dex.deposit("0xa36085f69e2889c224210f603d836748e7dc0088", "0x5226a51522C23CcBEFd04a2d4C6c8e281eD1d680", "0xB643992c9fBcb1Cb06b6C9eb278b2ac35e6a2711", "1",
// you were missing this
{from:accounts[0])
In truffle test suite, when you create the contract, it passes the accounts as the first argument to the callback. You have to define accounts in before statement so when you run the tests those will be available on top level.
contract("Dex", (accounts) => {
let contractOwner = null;
let buyer = null;
let _contract = null;
before(async () => {
// Instead of Dex.new() try Dex.deployed()
// I am not sure if "new()" is still supported
_contract = await Dex.deployed();
contractOwner = accounts[0];
// sometimes you need to test a user except contract owner
buyer = accounts[1];
});
}
You need to approve
Inside ERC20.sol you are calling this:
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
uint256 currentAllowance = _allowances[sender][_msgSender()];
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
}
_transfer(sender, recipient, amount);
return true;
}
So you want DEX to transfer coin from another contract. So this another contract has to approve this transaction first. So I need to have another contract's address to be able to call approve
When I run the following code, it accurately gets the token balance for both addresses and the transaction even goes through (I can see it on the testnet), although no tokens are sent.
I've tried a variety of things including replacing the signed transaction piece with this:
await contract.methods.transfer(toAddress, 100000).send({
from: fromAddress
});
but that fails with an unknown account error.
My Code for sending tokens:
const Web3 = require("web3");
const { hdkey } = require("ethereumjs-wallet");
const bip39 = require("bip39");
const token = require("./token.json");
const mnemonic = "12 word phrase";
const provider = "https://apis.ankr.com/.../binance/full/test";
(async() => {
try {
const seed = await bip39.mnemonicToSeed(mnemonic);
const root = hdkey.fromMasterSeed(seed);
const web3 = new Web3(provider);
const addrNode = root.derivePath(`m/44'/60'/0'/0/0`);
const wallet = addrNode.getWallet();
// #0 in the hdwallet, the owner of the tokens
const fromAddress = wallet.getAddressString();
// #1 in the hdwallet, already has a token balance
const toAddress = "0x...";
const contract = new web3.eth.Contract(token.abi, token.contract_address);
let fromAddressBalance = await contract.methods.balanceOf(fromAddress).call();
let toAddressBalance = await contract.methods.balanceOf(toAddress).call();
console.log(`Pre Transaction: Sender: ${fromAddressBalance} TOKENS / Wallet: ${toAddressBalance} TOKENS`);
// token has 3 decimal places, this is 100.000
const encodedABI = contract.methods.transfer(toAddress, 100000).encodeABI();
const tx = {
from: fromAddress,
to: toAddress,
gas: 2000000,
value: 0x0,
data: encodedABI
};
const signed = await web3.eth.accounts.signTransaction(tx, wallet.privateKey.toString("hex"));
const trans = await web3.eth.sendSignedTransaction(signed.rawTransaction);
fromAddressBalance = await contract.methods.balanceOf(fromAddress).call();
toAddressBalance = await contract.methods.balanceOf(toAddress).call();
console.log(`Post Transaction: Sender: ${fromAddressBalance} TOKENS / Wallet: ${toAddressBalance} TOKENS`);
} catch (err) {
console.error(err.stack);
}
process.exit();
})();
There were a few things wrong that once fixed resolved my issue. I haven't gone back through and tested which did it, or if all were required, but wanted to leave this for future explorers.
I was creating a wallet with ethereum-js-wallet, and then using it with web3. You have to let web3 know about the wallet.
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
web3.eth.accounts.wallet.create();
web3.eth.accounts.wallet.add(account);
Private key addresses need to start with 0x.
Source: https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#privatekeytoaccount
const privateKey = `0x${wallet.privateKey.toString("hex")}`;
This ended up not mattering, and not being the correct way to transfer, but it's still a good note that I was sending a signed transaction to a 3rd party, NOT to the contract, the correct way to send the transaction would have been
const tx = {
from: fromAddress,
to: token.contract_address,
gas: 2000000,
value: 0x0,
data: encodedABI
};
Ultimately, I needed to take advantage of the contract's transfer method vs signing/sending a transaction
const result = await contract.methods.transfer(toAddress, 100).send({
from: fromAddress,
gas: 2000000
});
After a user registers on my app I want them to add a payment method. Their stripe customer account is created as soon as they register and from there they are transferred to the 'AddPaymentMethod' screen. As soon as the ' AddPaymentMethod' screen appears, I send a request to my server to create a setupIntent.
Creating Setup Intent:
exports.createSetupIntent = functions.https.onCall(async (data, context) => {
const userId = data.userId;
const snapshot = await db
.collection("development")
.doc("development")
.collection("users")
.doc(userId).get();
const customerId = snapshot.data().customer_id;
const setupIntent = await stripe.setupIntents.create({
customer: customerId,
});
const clientSecret = setupIntent.client_secret;
return {
clientsecret: clientSecret,
};
});
Calling the function when the screen appears on my client (This successfully creates the client secret key and stores it in a variable in the frontend):
FirebaseReferenceManager.functions.httpsCallable("createSetupIntent").call(["userId": Auth.auth().currentUser?.uid]) { (response, error) in
if let error = error {
print(error.localizedDescription)
}
if let response = (response?.data as? [String: Any]) {
let clientSecretKey = response["clientsecret"] as! String?
self.clientSecret = clientSecretKey ?? "-"
print("created client secret key: \(clientSecretKey!)")
}
}
Next, the user enters their credit card information and creates a payment method. Here is the function on my server:
exports.createPaymentMethod = functions.https.onCall(async (data, context) => {
const number = data.number;
const expMonth = data.expMonth;
const expYear = data.expYear;
const cvc = data.cvc;
const paymentMethod = await stripe.paymentMethods.create({
type: "card",
card: {
number: number,
exp_month: expMonth,
exp_year: expYear,
cvc: cvc,
},
});
const pmId = paymentMethod.id;
return {
paymentMethodId: pmId,
};
});
I call this function from the frontend when the user presses the "Save payment method" button. This successfully creates a payment method and returns the payment method id which is stored in a variable on the front end.
Lastly, using the client secret id and payment method id that was returned from the previous functions, I call the last function to confirm the setupIntent.
This function is called when a payment method is created successfully:
exports.confirmSetupIntent = functions.https.onCall(async (data, context) => {
const clientSecretKey = data.clientSecretKey;
const paymentMethodId = data.paymentMethodId;
const setupIntent = await stripe.setupIntents.confirm(
clientSecretKey,
{payment_method: paymentMethodId}
);
});
This is how the createPaymentMethod and confirmSetupIntent functions are called from the frontend:
FirebaseReferenceManager.functions.httpsCallable("createPaymentMethod").call(["number": self.cardNumber, "expMonth": self.expMonth, "expYear": "20\(self.expYear)", "cvc": self.cvvCode]) { (response, error) in
if let error = error {
print("error occured when creating payment method: \(error.localizedDescription)")
}
if let response = response?.data as? [String: Any] {
let paymentMethodId = response["paymentMethodId"] as! String?
self.paymentMethodID = paymentMethodId ?? "-"
print(paymentMethodId!)
FirebaseReferenceManager.functions.httpsCallable("confirmSetupIntent").call(["clientSecretKey": self.clientSecret, "paymentMethodId": self.paymentMethodID]) { (response, error) in
if let error = error {
print("error occured when confirming setup intent: \(error.localizedDescription)")
}
print("setup intent confirmed")
}
}
}
In the debug console on the frontend it says that the error from confirming the setupIntent was INTERNAL. When I check the logs on my server I it says:
StripeInvalidRequestError: No such setupintent: 'seti_...'
Note that I am using SwiftUI and custom screens/textfields for the stripe integration.
Any help is appreciated!
The No such setupintent error indicates you have a mismatch in your API keys, and you should double check that your server secret key and client publishable are a matched pair for the same account and both for test mode, eg.
Of greater concern is that you appear to be passing payment details to your server to create the payment method. This is not recommended, and has significant PCI Compliance implications. Instead of creating the payment method like this on your server, you should use Elements and provide a reference to the Card Element when you use confirmCardSetup (docs):
stripe.confirmCardSetup(
clientSecret,
{
payment_method: {
card: cardElement,
},
}
)
I am trying to deploy a solidity contract using web3 and nodejs and I get an error on all testnets:
If I try to run on the local testrpc, everything works fine.
Can you spot any error in the code that might cause this error, or is there an issue with the testnets?
const path = require('path');
const fs = require('fs');
const solc = require('solc');
var Web3 = require('web3');
// Infura test network (kovan)
var web3 = new Web3(new Web3.providers.HttpProvider('https://kovan.infura.io/v3/3e0f68cb39c64417b15cf55e486479dd'));
var myAddress = '0x362aa2Bf4b6fB733C4EF41F4d2833E8e5aDc54ed';
var myPrivateKey = new Buffer('a288c7c873f09e96b7f0e404759288606e2ffc0edf58874aeb5a0fe4bcd9c262', 'hex')
// Compile contract from file
const contractPath = path.resolve(__dirname, 'contracts', 'HDS.sol');
const contractSourceCode = fs.readFileSync(contractPath, 'UTF-8');
const compiledContract = solc.compile(contractSourceCode, 1).contracts[':HDS']
var newContractAddress = web3.utils.toChecksumAddress(web3.utils.randomHex(20));
// Create a transaction
var rawTx = {
from: myAddress,
nonce: web3.utils.toHex('13'),
gasPrice: web3.utils.toHex(web3.utils.toWei('1', 'gwei')),
gas: web3.utils.toHex('892413'),
gasLimit: web3.utils.toHex('892413'),
data: compiledContract.bytecode
};
// // Unlock account to sign transaction
// web3.eth.personal.unlockAccount(myAddress, myPrivateKey, 600)
// .then(console.log('Account unlocked!'))
// .catch((error) => { console.log(error); });
web3.eth.getBalance(myAddress)
.then(function(balance) { console.log("My balance: ", balance); })
.catch(function(error) { console.log(error); });
web3.eth.accounts.signTransaction(rawTx, myPrivateKey)
.then(function(signResult) {
web3.eth.sendSignedTransaction(signResult.rawTransaction)
.on('error', function (error) { console.log("Error deploying contract: " + error); })
.on('transactionHash', function (transactionHash) { console.log("Transaction hash: " + transactionHash); })
.on('receipt', function (receipt) { console.log("Receipt contract address: " + receipt.contractAddress); })
.on('confirmation', function (confirmationNumber, receipt) {
console.log("Confirmation number: " + confirmationNumber);
console.log("Confirmation receipt: " + receipt);
})
.catch(function (error) { console.log(error); });
});
Here's the account on Kovan testnet, if it helps: https://kovan.etherscan.io/address/0x362aa2bf4b6fb733c4ef41f4d2833e8e5adc54ed
You need to sign the transaction before you send it to the network. The easiest way to do this would be to unlock one account using your mnemonic. You can do this when you initialize web3 and using truffle-hdwallet-provider, after that you can send transaction from your account without the need to manually sign them, in my opinion this is the easiest way to do this. Another option is to manually sign each transaction before you send it using your private key, you can read here how you can do this. There is no difference in terms of functionality between the two ways but the first on is a little bit easier if you are new to this.
I'm building a React Native app that is using redux-persist and redux-persist-transform-encrypt to store app state encrypted on the device. redux-persist-transform-encrypt uses CryptoJs to encrypt data via AES: CryptoJS.AES.encrypt('serialized app state', 'some-secret-key').
My question: (I'm a crypto novice) is there a best practice for generating some-secret-key? My plan is to generate this key randomly when the app boots for the first time and store it securely in the device keychain.
We use React Native Keychain to store the random generate key, to ensure the key is harder to get even if the attacker have access to file system:
let secretKey
async function getSecretKey() {
let {password} = await Keychain.getGenericPassword()
if (!password) {
password = generateRandomKey()
await Keychain.setGenericPassword(appName, password)
}
return password
}
const encrypt = async function(state) {
if (!secretKey) {
secretKey = await getSecretKey()
}
if (typeof state !== 'string') {
state = stringify(state);
}
return CryptoJS.AES.encrypt(state, secretKey).toString()
}
You can use some uuid generator to generate random secret key. then store it in the keychain as #jimchao said. If you are using expo you can write something as follows:
import Expo from 'expo';
export const getFromSecureStore = (key, options) =>
Expo.SecureStore.getItemAsync(key, options);
export const saveToSecureStore = (key, value, options) =>
Expo.SecureStore.setItemAsync(key, value, options);
then use it as:
// while signing in
function* handleSignInRequest(action) {
try {
const resp = yield RequestService.post(
action.payload.url,
action.payload.body,
);
yield put({ type: SIGN_IN_SUCCEEDED, payload: resp.data });
const { username, password } = action.payload.body;
yield saveToSecureStore('username', username, {
keychainAccessible: Expo.SecureStore.ALWAYS, // can be changed as per usage
});
yield saveToSecureStore('password', password, {
keychainAccessible: Expo.SecureStore.ALWAYS,
});
} catch (error) {
console.log('======>> signin error', error);
yield put({ type: SIGN_IN_FAILED, error: error.response });
}
}
// at app start
async handleSignInAtBootstrap() {
try {
const username = await getFromSecureStore('username');
const password = await getFromSecureStore('password');
this.props.signInUser(username, password); //dispatch an action
} catch (error) {
console.log('======>>>', error);
}
}
here's Expo secure store docs to explore various configs