Comments (23)
USDC ERC20 token is supported in the ethereum-payments package
See: https://github.com/go-faast/coin-payments/tree/master/packages/ethereum-payments/src/erc20
from coinlib.
thanks
from coinlib.
Oh I am newbie in crypto world, I am trying to test erc20 (USDC). I have been testing the Ropsten testnet with USDC. I have received the tokens but I have not been able to create a transaction. Please have some example code with ERC20
from coinlib.
When creating the payments instance you need to provide extra fields to tell it what token to use. It would look something like this:
const usdcPayments = new HdErc20Payments({
hdKey: 'xprv....',
networkType: 'testnet',
fullNode: 'ropsten.infura.io/v3/yourkeyhere',
symbol: 'USDC',
name: 'USD Coin',
decimals: 6,
tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
})
const senderIndex = 0
const { address: senderAddress } = await usdcPayments.getPayport(senderIndex)
const unsignedTx = await usdcPayments.createTransaction(senderIndex, '0xrecipient', 1234)
const signedTx = await usdcPayments.signTransaction(unsignedTx)
const { id: txHash } = await usdcPayments.broadcastTransaction(signedTx)
from coinlib.
Thanks a lot. Thank you for helping this humble beginner of the crypto world
from coinlib.
Hi again, I'm getting this error:
Error: Cannot derive payport 1 - masterAddress is falsy
Do you know what's happening? What is the function of masterAddress?
Thanks again
from coinlib.
@kalimuscle Great question. The erc20 payments work slightly different from the ethereum/bitcoin implementations. Typically getPayport will just derive an address at the index you ask for based on the bip32/44 standard. However a plain ethereum address is not particularly useful for receiving incoming token deposits because that address wouldn't have any ETH to move the funds out. This library was designed with enterprises in mind so being able to generate a unique address that could be given to a customer and swept in one step without needing to top it up with ether was crucial.
The solution to this was to implement a token deposit smart contract that must be deployed in advance by the address at payport 0. The smart contract allows you to generate unique ethereum addresses using the create2 feature of the EVM. When you send or sweep tokens from this address the smart contract deploys an temporary minimal proxy contract of itself, sends the tokens to the recipient, and self destructs itself to get the gas refund. So effectively it facilitates erc20 token transfers while only needing to maintain a balance in a single owner address (payport #0). It's quite gas efficient too, the overhead is only around 20-30k on top of a typical erc20 transfer. Which is much cheaper than the alternatives.
This contract can be deployed using the createServiceTransaction
method to deploy the contract. Then you'd set that contract address as the masterAddress
in the payments config.
For example:
const baseConfig = {
hdKey: 'xprv....',
networkType: 'testnet',
fullNode: 'ropsten.infura.io/v3/yourkeyhere',
}
/*
* One time deployment of smart contract necessary to facilitate receiving future erc20 deposits
*/
const ethPayments = new HdEthereumPayments(baseConfig)
const { address: primaryAddress } = await ethPayments.getPayport(0)
// Add some ether to primaryAddress to facilitate contract deployment. It controls the master contract and pays fees for all erc20 transfers out of payports at index >=1
const deployMasterUnsignedTx = await ethPayments.createServiceTransaction()
const deployMasterSignedTx = await ethPayments.signTransaction(deployMasterUnsignedTx)
const { id: deployMasterTxHash } = await ethPayments.broadcastTransaction(deployMasterSignedTx)
// You'll want to regularly look up the hash until it confirms
const deployTxInfo = await ethPayments.getTransactionInfo(deployMasterTxHash)
if (deployTxInfo.confirmations > 10) {
// master contract deployment confirmed
const { contractAddress: masterAddress } = txInfo.data
// Save this master address to use for erc20 payments config. It can be used for all tokens, so you only need to do this once per primary address
}
/*
* Sweep the tokens from deposit address (payport #1) to recipient address 0xrecipient.
* You **don't** need to have an ether balance at the deposit address, only your primary address (payport #0)
*/
const usdcPayments = new HdErc20Payments({
...baseConfig,
masterAddress, // from earlier deployment
symbol: 'USDC',
name: 'USD Coin',
decimals: 6,
tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
})
const depositIndex = 1
const { address: depositAddress } = await usdcPayments.getPayport(depositIndex)
const unsignedTx = await usdcPayments.createSweepTransaction(depositIndex, '0xrecipient')
const signedTx = await usdcPayments.signTransaction(unsignedTx)
const { id: txHash } = await usdcPayments.broadcastTransaction(signedTx)
// On etherscan you'll see the transaction "from" your primaryAddress and "to" your masterAddress
// The result will be moving all tokens from depositAddress to 0xrecipient
from coinlib.
Hello again,
Great explanation, now I understand better. When I checked the code with the tests, some doubts jumped.
There was a moment that I imagined that masterAddress was used in something like what you are telling me.
Following my instincts, I tried to implement something. I managed to obtain the balance of USDC from an address to which I sent test tokens, but when I tried to create a transaction I always had problems with the transaction gas
from coinlib.
Can I deploy the contract only once and use the masterAddress for all tokens or do I need to deploy the contract every time I need to do a transaction instead?
from coinlib.
Is it not necessary to define at some point the ABI of the USDC token?
from coinlib.
I'm getting a invalid sender exception from broadcastTransation
from coinlib.
Can I deploy the contract only once and use the masterAddress for all tokens or do I need to deploy the contract every time I need to do a transaction instead?
Only once per primary address. The same contract works for all tokens. The ABI is already built into this library.
I can't do much to help you without your code
from coinlib.
Can I deploy the contract only once and use the masterAddress for all tokens or do I need to deploy the contract every time I need to do a transaction instead?
Only once per primary address. The same contract works for all tokens. The ABI is already built into this library.
I can't do much to help you without your code
Ok, this the code
try {
const baseConfig = {
hdKey:
"xprv...",
networkType: NetworkType.Testnet,
gasStation: GAS_STATION_URL,
fullNode: ROPSTEN_NETWORK_URL
};
/*
* One time deployment of smart contract necessary to facilitate receiving future erc20 deposits
*/
const ethPayments = new HdEthereumPayments(baseConfig);
const { address: primaryAddress } = await ethPayments.getPayport(0);
// Add some ether to primaryAddress to facilitate contract deployment. It controls the master contract and pays fees for all erc20 transfers out of payports at index >=1
const deployMasterUnsignedTx = await ethPayments.createServiceTransaction(
undefined,
{ gas: "4700000" }
);
const deployMasterSignedTx = await ethPayments.signTransaction(
deployMasterUnsignedTx
);
const { id: deployMasterTxHash } = await ethPayments.broadcastTransaction(
deployMasterSignedTx
);
// You'll want to regularly look up the hash until it confirms
const deployTxInfo = await ethPayments.getTransactionInfo(
deployMasterTxHash
);
if (deployTxInfo.confirmations > 10) {
// master contract deployment confirmed
const { contractAddress: masterAddress } = txInfo.data;
// Save this master address to use for erc20 payments config. It can be used for all tokens, so you only need to do this once per primary address
}
const usdcPayments = new HdErc20Payments({
...baseConfig,
masterAddress, // from earlier deployment
symbol: "USDC",
name: "USD Coin",
decimals: 6,
tokenAddress: USDC_CONTRACT_ADDRESS
});
const depositIndex = 1;
const { address: depositAddress } = await usdcPayments.getPayport(
depositIndex
);
const unsignedTx = await usdcPayments.createSweepTransaction(
depositIndex,
"0x9fFfB5002A7CE674766661a1731757D04D1100E6"
);
const signedTx = await usdcPayments.signTransaction(unsignedTx);
const { id: txHash } = await usdcPayments.broadcastTransaction(signedTx);
} catch (error) {
console.log(error);
}
it's the same code except the arguments in createServiceTransaction
from coinlib.
@kalimuscle The code I provided wasn't complete. You'll still need to add a loop around the getTransactionInfo until it's actually confirmed then you can use the master address from it
from coinlib.
Hi I add a loop using setInterval for check getTransactionInfo
const main = async () => {
try {
const baseConfig = {
hdKey:
"xprv....",
networkType: NetworkType.Testnet,
//gasStation: GAS_STATION_URL,
fullNode: LOCAL_NETWORK_URL,
tokenAddress: "",
masterAddress: "",
depositKeyIndex: 0
};
/*
* One time deployment of smart contract necessary to facilitate receiving future erc20 deposits
*/
const ethPayments = new HdEthereumPayments(baseConfig);
const { address } = await ethPayments.getPayport(0);
// Add some ether to primaryAddress to facilitate contract deployment. It controls the master contract and pays fees for all erc20 transfers out of payports at index >=1
const deployMasterUnsignedTx = await ethPayments.createServiceTransaction(
undefined,
{
gas: "4700000"
}
);
const deployMasterSignedTx = await ethPayments.signTransaction(
deployMasterUnsignedTx
);
const { id } = await ethPayments.broadcastTransaction(deployMasterSignedTx);
console.log("id hash from broadcast:", id);
loop(ethPayments, id);
//while (true) {}
} catch (error) {
console.log(error);
}
};
const loop = async (ethPayments, deployMasterTxHash) => {
console.log("inside loop");
const functionInterval = setInterval(async () => {
let contractAddress;
console.log("Verifying transaction ...");
// You'll want to regularly look up the hash until it confirms
const deployTxInfo = await ethPayments.getTransactionInfo(
deployMasterTxHash
);
if (deployTxInfo.isConfirmed) {
console.log("Transaction is confirmed");
// master contract deployment confirmed
contractAddress = deployTxInfo.data.contractAddress;
console.log("contract address:", contractAddress);
// Save this master address to use for erc20 payments config. It can be used for all tokens, so you only need to do this once per primary address
const usdcPayments = new HdErc20Payments({
...baseConfig,
masterAddress: contractAddress, // from earlier deployment
symbol: "USDC",
name: "USD Coin",
decimals: 6,
tokenAddress: USDC_CONTRACT_ADDRESS
});
const depositIndex = 1;
const { address } = await usdcPayments.getPayport(depositIndex);
console.log(address);
const unsignedTx = await usdcPayments.createSweepTransaction(
"0xD4E45ae7DC0701f411410187b1E0269D61d4cF27",
"0xdd7069791FAd5c380338352E47468C194cba5651"
);
const signedTx = await usdcPayments.signTransaction(unsignedTx);
const { id } = await usdcPayments.broadcastTransaction(signedTx);
// On etherscan you'll see the transaction "from" your primaryAddress and "to" your masterAddress
// The result will be moving all tokens from depositAddress to 0xrecipient
clearInterval(functionInterval);
} else {
console.log("Transaction not confirmed");
}
}, 5000);
};
main();
from coinlib.
Other observation, in this code I am using ganache-cli, broadcastTrnasaction work and I can check the transaction. However, if I use ropsten network I am getting "invalid sender" exception from broadcast transaction
from coinlib.
@kalimuscle it might be caused by the token address as this is network specific:
- tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' - is for mainnet
- tokenAddress: '0x07865c6e87b9f70255377e024ace6630c1eaa37f' - is for ropsten
ref https://developers.circle.com/docs/usdc-on-testnet
from coinlib.
Hi @dzdidi , tokenAddress is fine, the problem occurs when I run the broadcastTransaction at the beginning
from coinlib.
@kalimuscle
sorry but it is hard to say what exactly is happening without any concrete information about setup, invocation, execution. Output and or exact steps to recreate.
The only help I can do is to refer you to this test which uses local instance of ganache-code
there you can adjust test parameters to your preferences.
from coinlib.
This is the exception :
[ethereum-payments] Ethereum broadcast tx unsuccessful 0x4316e1718f52c6792323b19329ac5055df04346dd55b7955946a7165aae63438: Returned error: invalid sender
Error: Ethereum broadcast tx unsuccessful: 0x4316e1718f52c6792323b19329ac5055df04346dd55b7955946a7165aae63438 Returned error: invalid sender
from coinlib.
Searching in Internet I found that this error can be with chainId
from coinlib.
Hi guys, I solved the problem in Ropsten, in the config I am using networkType: NetworkType.Testnet instead of
network: "testnet".
Thanks for your help
from coinlib.
@kalimuscle Ah, yes sorry my example config was incorrect. Glad you were able to figure it out
from coinlib.
Related Issues (13)
- Send from all addresses in a single transaction HOT 2
- Remove @types/bip32
- Include Doge and Tether HOT 4
- TRC20 transactions HOT 2
- TypeError: coinPayments.forAsset is not a function HOT 3
- The generated wallet address is different from that of other wallet programs HOT 1
- build docker file
- bugs
- update depecreate version HOT 1
- Error: Failed to load plugin '@typescript-eslint' declared in '.eslintrc.js': Cannot find module 'eslint/use-at-your-own-risk'
- Any sreenshot of the actual state ?
- How can i generate a simple LTC wallet
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from coinlib.