Git Product home page Git Product logo

Comments (23)

dylanseago avatar dylanseago commented on September 28, 2024

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.

kalimuscle avatar kalimuscle commented on September 28, 2024

thanks

from coinlib.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

dylanseago avatar dylanseago commented on September 28, 2024

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.

kalimuscle avatar kalimuscle commented on September 28, 2024

Thanks a lot. Thank you for helping this humble beginner of the crypto world

from coinlib.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

dylanseago avatar dylanseago commented on September 28, 2024

@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.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

kalimuscle avatar kalimuscle commented on September 28, 2024

Is it not necessary to define at some point the ABI of the USDC token?

from coinlib.

kalimuscle avatar kalimuscle commented on September 28, 2024

I'm getting a invalid sender exception from broadcastTransation

from coinlib.

dylanseago avatar dylanseago commented on September 28, 2024

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.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

dylanseago avatar dylanseago commented on September 28, 2024

@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.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

dzdidi avatar dzdidi commented on September 28, 2024

@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.

kalimuscle avatar kalimuscle commented on September 28, 2024

Hi @dzdidi , tokenAddress is fine, the problem occurs when I run the broadcastTransaction at the beginning

from coinlib.

dzdidi avatar dzdidi commented on September 28, 2024

@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.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

kalimuscle avatar kalimuscle commented on September 28, 2024

Searching in Internet I found that this error can be with chainId

from coinlib.

kalimuscle avatar kalimuscle commented on September 28, 2024

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.

dylanseago avatar dylanseago commented on September 28, 2024

@kalimuscle Ah, yes sorry my example config was incorrect. Glad you were able to figure it out

from coinlib.

Related Issues (13)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.