Creé un token personalizado en Ropsten testnet usando esta guía: https://steemit.com/ethereum/@maxnachamkin/how-to-create-your-own-ethereum-token-in-an-hour-erc20-verified
Puedo enviarlo a otras cuentas usando MetaMask, pero no sé cómo hacerlo en node.js usando web3, ethereumjs-tx y Web3 JavaScript app API.
Mi código en este momento se ve así:
var count = web3.eth.getTransactionCount("0x26...");
var abiArray = JSON.parse(fs.readFileSync('mycoin.json', 'utf-8'));
var contractAddress = "0x8...";
var contract = web3.eth.contract(abiArray).at(contractAddress);
var rawTransaction = {
"from": "0x26...",
"nonce": web3.toHex(count),
"gasPrice": "0x04e3b29200",
"gasLimit": "0x7458",
"to": contractAddress,
"value": "0x0",
"data": contract.transfer("0xCb...", 10, {from: "0x26..."}),
"chainId": 0x03
};
var privKey = new Buffer('fc3...', 'hex');
var tx = new Tx(rawTransaction);
tx.sign(privKey);
var serializedTx = tx.serialize();
web3.eth.sendRawTransaction('0x' + serializedTx.toString('hex'), function(err, hash) {
if (!err)
console.log(hash);
else
console.log(err);
});
En este caso, el código solo se detiene en una contract.transfer("0xCb...", 10, {from: "0x26..."})
parte y la solicitud está pendiente. No se pudo encontrar ninguna guía para hacer cosas similares. Encontré algo de código aquí:
Y aquí:
Cómo transferir tokens ERC20 usando web3js
Pero todavía me quedé atascado, no sé lo que me estoy perdiendo.
Estoy usando la versión web3.js: 0.20.1 en la aplicación express node.js. Estoy ejecutando Parity en la máquina Virtualbox Ubuntu.
El código correcto se parece a lo siguiente:
var count = web3.eth.getTransactionCount("0x26...");
var abiArray = JSON.parse(fs.readFileSync('mycoin.json', 'utf-8'));
var contractAddress = "0x8...";
var contract = web3.eth.contract(abiArray).at(contractAddress);
var rawTransaction = {
"from": "0x26...",
"nonce": web3.toHex(count),
"gasPrice": "0x04e3b29200",
"gasLimit": "0x7458",
"to": contractAddress,
"value": "0x0",
"data": contract.transfer.getData("0xCb...", 10, {from: "0x26..."}),
"chainId": 0x03
};
var privKey = new Buffer('fc3...', 'hex');
var tx = new Tx(rawTransaction);
tx.sign(privKey);
var serializedTx = tx.serialize();
web3.eth.sendRawTransaction('0x' + serializedTx.toString('hex'), function(err, hash) {
if (!err)
console.log(hash);
else
console.log(err);
});
Encontré muchas respuestas que estaban desactualizadas o les faltaba información importante. Esto es lo que finalmente funcionó para mí en mi proyecto node.js usando web3 versión 1.0.0-beta.26. Tenga en cuenta que esto es para Ethereum Main Net. Para usar la red de prueba Robsten, cambie el chainId a 0x03
// Get private stuff from my .env file
import {my_privkey, infura_api_key} from '../.env'
// Need access to my path and file system
import path from 'path'
var fs = require('fs');
// Ethereum javascript libraries needed
import Web3 from 'Web3'
var Tx = require('ethereumjs-tx');
// Rather than using a local copy of geth, interact with the ethereum blockchain via infura.io
const web3 = new Web3(Web3.givenProvider || `https://mainnet.infura.io/` + infura_api_key)
// Create an async function so I can use the "await" keyword to wait for things to finish
const main = async () => {
// This code was written and tested using web3 version 1.0.0-beta.26
console.log(`web3 version: ${web3.version}`)
// Who holds the token now?
var myAddress = "0x97...";
// Who are we trying to send this token to?
var destAddress = "0x4f...";
// If your token is divisible to 8 decimal places, 42 = 0.00000042 of your token
var transferAmount = 1;
// Determine the nonce
var count = await web3.eth.getTransactionCount(myAddress);
console.log(`num transactions so far: ${count}`);
// This file is just JSON stolen from the contract page on etherscan.io under "Contract ABI"
var abiArray = JSON.parse(fs.readFileSync(path.resolve(__dirname, './tt3.json'), 'utf-8'));
// This is the address of the contract which created the ERC20 token
var contractAddress = "0xe6...";
var contract = new web3.eth.Contract(abiArray, contractAddress, { from: myAddress });
// How many tokens do I have before sending?
var balance = await contract.methods.balanceOf(myAddress).call();
console.log(`Balance before send: ${balance}`);
// I chose gas price and gas limit based on what ethereum wallet was recommending for a similar transaction. You may need to change the gas price!
var rawTransaction = {
"from": myAddress,
"nonce": "0x" + count.toString(16),
"gasPrice": "0x003B9ACA00",
"gasLimit": "0x250CA",
"to": contractAddress,
"value": "0x0",
"data": contract.methods.transfer(destAddress, transferAmount).encodeABI(),
"chainId": 0x01
};
// Example private key (do not use): 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109'
// The private key must be for myAddress
var privKey = new Buffer(my_privkey, 'hex');
var tx = new Tx(rawTransaction);
tx.sign(privKey);
var serializedTx = tx.serialize();
// Comment out these three lines if you don't really want to send the TX right now
console.log(`Attempting to send signed tx: ${serializedTx.toString('hex')}`);
var receipt = await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'));
console.log(`Receipt info: ${JSON.stringify(receipt, null, '\t')}`);
// The balance may not be updated yet, but let's check
balance = await contract.methods.balanceOf(myAddress).call();
console.log(`Balance after send: ${balance}`);
}
main();
Tenga en cuenta que a veces el envío "fallará" porque el tx no se extrajo dentro de los 50 bloques. En mi copia del código fuente de web3, cambié TIMEOUTBLOCK de 50 a 500 para no tener que lidiar con eso.
Tenga en cuenta que este código no tiene manejo de errores; es posible que desee agregar algunos.
getTransactionCount
.Si solo queremos enviar una transacción con el método erc20 transfer
, podemos construir un objeto de contrato usando minABI y la dirección del contrato, como el siguiente código:
let minABI = [
// transfer
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
];
let contractAddres="put the erc20 contract address here";
let contract = await new web3.eth.Contract(minABI, contractAddr);
contract.methods.transfer("your account", "amount of erc20 tokens you want transfer").send({
from: "your account"
});
y esto es trabajo para mí, espero que esto ayude. :D
Puede verificar este ejemplo de trabajo con mi token: https://github.com/religion-counter/onlyone/blob/main/helper-scripts/send-onlyone.js
También puede contribuir al repositorio si está interesado.
// Helper script that sends ONLYONE token to target addresses specified in targets.txt
// Target index - index in targets.txt file is specified as an argument - process.argv.splice(2)[0]
var fs = require('fs')
var targetAccounts = JSON.parse(fs.readFileSync('targets.txt', 'utf-8'));
var myAddress = JSON.parse(fs.readFileSync("my-address.json", 'utf-8'));
var targetIndex = Number(process.argv.splice(2)[0]);
console.log(`Sending ONLYONE to target ${targetIndex}.`);
async function sendOnlyone(fromAddress, toAddress) {
var Tx = require('ethereumjs-tx').Transaction;
var Web3 = require('web3');
var web3 = new Web3(new Web3.providers.HttpProvider('https://bsc-dataseed.binance.org/'));
var amount = web3.utils.toHex(10);
var privateKey = Buffer.from(myAddress.privateKey, 'hex');
var abiArray = JSON.parse(JSON.parse(fs.readFileSync('onlyone-abi.json','utf-8')));
var contractAddress = '0xb899db682e6d6164d885ff67c1e676141deaaa40'; // ONLYONE address
var contract = new web3.eth.Contract(abiArray, contractAddress, {from: fromAddress});
var Common = require('ethereumjs-common').default;
var BSC_FORK = Common.forCustomChain(
'mainnet',
{
name: 'Binance Smart Chain Mainnet',
networkId: 56,
chainId: 56,
url: 'https://bsc-dataseed.binance.org/'
},
'istanbul',
);
var count = await web3.eth.getTransactionCount(myAddress);
var rawTransaction = {
"from":myAddress,
"gasPrice":web3.utils.toHex(5000000000),
"gasLimit":web3.utils.toHex(210000),
"to":contractAddress,"value":"0x0",
"data":contract.methods.transfer(toAddress, amount).encodeABI(),
"nonce":web3.utils.toHex(count)
};
var transaction = new Tx(rawTransaction, {'common':BSC_FORK});
transaction.sign(privateKey)
var result = await web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex'));
return result;
}
sendOnlyone(myAddress, targetAccounts[targetIndex]);
Encontré esta documentación en uno de los proyectos que estoy usando en mi dapp: https://developers.fortmatic.com/docs/smart-contract-functions
La documentación explica bastante detalladamente qué hacer paso a paso para enviar transferencias de tokens ERC20 para versiones web3 anteriores a la 1.0 (0.20.x) y posteriores a la 1.0.
Aquí hay una vista previa de cómo se ve la versión 0.20.x del código web3.
// Initialize provider
import Fortmatic from 'fortmatic';
import Web3 from 'web3';
const fm = new Fortmatic('YOUR_API_KEY');
window.web3 = new Web3(fm.getProvider()); // Can replace with MetaMask web3 provider
// Get the contract ABI from compiled smart contract json
const erc20TokenContractAbi = [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"tokens","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"tokens","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdrawEther","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"_totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"tokenOwner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"acceptOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeSub","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"tokens","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeDiv","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"tokens","type":"uint256"},{"name":"data","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeMul","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"newOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenAddress","type":"address"},{"name":"tokens","type":"uint256"}],"name":"transferAnyERC20Token","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"tokenOwner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"}],"name":"safeAdd","outputs":[{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"tokenOwner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"tokens","type":"uint256"}],"name":"Approval","type":"event"}];
// Create contract object
const tokenContract = web3.eth.contract(erc20TokenContractAbi);
// Instantiate contract
const tokenContractInstance = tokenContract.at('0x8EBC7785b83506AaA295Bd9174e6A7Ad5681fb80');
const toAddress = '0xE0cef4417a772512E6C95cEf366403839b0D6D6D';
// Calculate contract compatible value for transfer with proper decimal points using BigNumber
const tokenDecimals = web3.toBigNumber(18);
const tokenAmountToTransfer = web3.toBigNumber(100);
const calculatedTransferValue = web3.toHex(tokenAmountToTransfer.mul(web3.toBigNumber(10).pow(tokenDecimals)));
// Call contract function (non-state altering) to get total token supply
tokenContractInstance.totalSupply.call(function(error, result) {
if (error) throw error;
console.log(result);
});
// Get user account wallet address first
web3.eth.getAccounts(function(error, accounts) {
if (error) throw error;
// Send ERC20 transaction with web3
tokenContractInstance.transfer.sendTransaction(toAddress, calculatedTransferValue, {from: accounts[0]}, function(error, txnHash) {
if (error) throw error;
console.log(txnHash);
});
});
Así es como se hace con ethers.js
. No sé por qué la mayoría de las otras respuestas usan una importación de un ABI json no especificado cuando ERC20 sigue la misma interfaz para cualquier token, y es poco probable que tenga el ABI de un token público.
Tenga en cuenta que en este ejemplo asumo que la cantidad debe multiplicarse por una potencia de 6 ( utils.parseUnits(n, 6)
) que se aplica a USDT y USDC, por ejemplo. Otros tokens ERC20 pueden variar.
import { Contract, providers, utils } from 'ethers'
const erc20abi = [
/**
* @dev Returns the amount of tokens in existence.
*/
'function totalSupply() external view returns (uint256)',
/**
* @dev Returns the amount of tokens owned by `account`.
*/
'function balanceOf(address account) external view returns (uint256)',
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
'function transfer(address recipient, uint256 amount) external returns (bool)',
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
'function allowance(address owner, address spender) external view returns (uint256)',
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
'function approve(address spender, uint256 amount) external returns (bool)',
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
'function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)',
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
'event Transfer(address indexed from, address indexed to, uint256 value)',
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
'event Approval(address indexed owner, address indexed spender, uint256 value)',
]
export async function approveERC20(
provider: providers.Web3Provider,
tokenAddr: string,
spenderAddr: string,
amount: string
) {
const signer = provider.getSigner()
const contract = new Contract(tokenAddr, erc20abi, signer)
contract.approve(spenderAddr, utils.parseUnits(amount, 6))
}
export async function transferERC20(
provider: providers.Web3Provider,
tokenAddr: string,
recipientAddr: string,
amount: string
) {
const signer = provider.getSigner()
const contract = new Contract(tokenAddr, erc20abi, signer)
contract.transfer(recipientAddr, utils.parseUnits(amount, 6))
}
Adrien Forbú
ismael
getData
para generar los datos de transacción sin procesar, como este"data": contract.transfer.getData("0xCb...", 10, {from: "0x26..."}),
.ismael
getData
ethereum.stackexchange.com/a/12932Trav L
v0.20.x
? ¿Puede publicar un ejemplo completo como respuesta y marcarlo como resuelto? Gracias.Víbora
Tomás Navickas
Víbora
LF00
Juan Murphy