Skip to content

Commit

Permalink
erc20: Add testnet usdc.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeGruffins committed Dec 1, 2022
1 parent 75bb91d commit f683573
Show file tree
Hide file tree
Showing 21 changed files with 607 additions and 218 deletions.
21 changes: 16 additions & 5 deletions client/asset/eth/contractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type contractor interface {
// case will always be zero.
value(context.Context, *types.Transaction) (incoming, outgoing uint64, err error)
isRefundable(secretHash [32]byte) (bool, error)
voidUnusedNonce()
}

// tokenContractor interacts with an ERC20 token contract and a token swap
Expand Down Expand Up @@ -113,7 +114,7 @@ func newV0Contractor(net dex.Network, acctAddr common.Address, cb bind.ContractB
}

// initiate sends the initiations to the swap contract's initiate function.
func (c *contractorV0) initiate(txOpts *bind.TransactOpts, contracts []*asset.Contract) (*types.Transaction, error) {
func (c *contractorV0) initiate(txOpts *bind.TransactOpts, contracts []*asset.Contract) (tx *types.Transaction, err error) {
inits := make([]swapv0.ETHSwapInitiation, 0, len(contracts))
secrets := make(map[[32]byte]bool, len(contracts))

Expand Down Expand Up @@ -146,9 +147,10 @@ func (c *contractorV0) initiate(txOpts *bind.TransactOpts, contracts []*asset.Co
}

// redeem sends the redemptions to the swap contracts redeem method.
func (c *contractorV0) redeem(txOpts *bind.TransactOpts, redemptions []*asset.Redemption) (*types.Transaction, error) {
func (c *contractorV0) redeem(txOpts *bind.TransactOpts, redemptions []*asset.Redemption) (tx *types.Transaction, err error) {
redemps := make([]swapv0.ETHSwapRedemption, 0, len(redemptions))
secretHashes := make(map[[32]byte]bool, len(redemptions))

for _, r := range redemptions {
secretB, secretHashB := r.Secret, r.Spends.SecretHash
if len(secretB) != 32 || len(secretHashB) != 32 {
Expand Down Expand Up @@ -194,7 +196,7 @@ func (c *contractorV0) swap(ctx context.Context, secretHash [32]byte) (*dexeth.S

// refund issues the refund command to the swap contract. Use isRefundable first
// to ensure the refund will be accepted.
func (c *contractorV0) refund(txOpts *bind.TransactOpts, secretHash [32]byte) (*types.Transaction, error) {
func (c *contractorV0) refund(txOpts *bind.TransactOpts, secretHash [32]byte) (tx *types.Transaction, err error) {
return c.contractV0.Refund(txOpts, secretHash)
}

Expand Down Expand Up @@ -318,6 +320,15 @@ func (c *contractorV0) outgoingValue(tx *types.Transaction) (swapped uint64) {
return
}

// voidUnusedNonce allows the next nonce received from a provider to be the same
// as a recent nonce. Use when we fetch a nonce but error before or while
// sending a transaction.
func (c *contractorV0) voidUnusedNonce() {
if mRPC, is := c.cb.(*multiRPCClient); is {
mRPC.voidUnusedNonce()
}
}

// tokenContractorV0 is a contractor that implements the tokenContractor
// methods, providing access to the methods of the token's ERC20 contract.
type tokenContractorV0 struct {
Expand Down Expand Up @@ -395,13 +406,13 @@ func (c *tokenContractorV0) allowance(ctx context.Context) (*big.Int, error) {

// approve sends an approve transaction approving the linked contract to call
// transferFrom for the specified amount.
func (c *tokenContractorV0) approve(txOpts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) {
func (c *tokenContractorV0) approve(txOpts *bind.TransactOpts, amount *big.Int) (tx *types.Transaction, err error) {
return c.tokenContract.Approve(txOpts, c.contractAddr, amount)
}

// transfer calls the transfer method of the erc20 token contract. Used for
// sends or withdrawals.
func (c *tokenContractorV0) transfer(txOpts *bind.TransactOpts, addr common.Address, amount *big.Int) (*types.Transaction, error) {
func (c *tokenContractorV0) transfer(txOpts *bind.TransactOpts, addr common.Address, amount *big.Int) (tx *types.Transaction, err error) {
return c.tokenContract.Transfer(txOpts, addr, amount)
}

Expand Down
84 changes: 61 additions & 23 deletions client/asset/eth/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
"decred.org/dcrdex/dex/config"
"decred.org/dcrdex/dex/encode"
"decred.org/dcrdex/dex/keygen"
"decred.org/dcrdex/dex/networks/erc20"
dexeth "decred.org/dcrdex/dex/networks/eth"
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
"github.com/decred/dcrd/hdkeychain/v3"
Expand Down Expand Up @@ -60,7 +59,8 @@ func registerToken(tokenID uint32, desc string, nets ...dex.Network) {
func init() {
asset.Register(BipID, &Driver{})
// Test token
registerToken(testTokenID, "A token wallet for the DEX test token. Used for testing DEX software.", dex.Simnet)
registerToken(simnetTokenID, "A token wallet for the DEX test token. Used for testing DEX software.", dex.Simnet)
registerToken(usdcTokenID, "The USDC Ethereum ERC20 token.", dex.Testnet)
}

const (
Expand All @@ -83,7 +83,8 @@ const (
)

var (
testTokenID, _ = dex.BipSymbolID("dextt.eth")
simnetTokenID, _ = dex.BipSymbolID("dextt.eth")
usdcTokenID, _ = dex.BipSymbolID("usdc.eth")
// blockTicker is the delay between calls to check for new blocks.
blockTicker = time.Second
peerCountTicker = 5 * time.Second
Expand Down Expand Up @@ -1482,7 +1483,7 @@ func (w *assetWallet) approvalGas(newGas *big.Int, ver uint32) (uint64, error) {
if approveEst, err := w.estimateApproveGas(newGas); err != nil {
return 0, fmt.Errorf("error estimating approve gas: %v", err)
} else if approveEst > approveGas {
w.log.Warnf("Approve gas estimate %d is greater than the expected value %d. Using live estimate + 10%.")
w.log.Warnf("Approve gas estimate %d is greater than the expected value %d. Using live estimate + 10%%.", approveEst, approveGas)
return approveEst * 11 / 10, nil
}
return approveGas, nil
Expand Down Expand Up @@ -1756,6 +1757,14 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin
return fail("Swap: initiate error: %w", err)
}

if dexeth.Tokens[w.assetID] == nil ||
dexeth.Tokens[w.assetID].NetTokens[w.net] == nil ||
dexeth.Tokens[w.assetID].NetTokens[w.net].SwapContracts[swaps.Version] == nil {
return fail("unable to find contract address for asset %d", w.assetID)
}

contractAddr := dexeth.Tokens[w.assetID].NetTokens[w.net].SwapContracts[swaps.Version].Address.String()

txHash := tx.Hash()
for _, swap := range swaps.Contracts {
var secretHash [dexeth.SecretHashSize]byte
Expand All @@ -1766,7 +1775,7 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin
txHash: txHash,
secretHash: secretHash,
ver: swaps.Version,
contractAddr: erc20.ContractAddresses[swaps.Version][w.net].String(),
contractAddr: contractAddr,
})
}

Expand Down Expand Up @@ -2039,11 +2048,13 @@ func (w *assetWallet) approveToken(amount *big.Int, maxFeeRate uint64, contractV

return tx, w.withTokenContractor(w.assetID, contractVer, func(c tokenContractor) error {
tx, err = c.approve(txOpts, amount)
if err == nil {
w.log.Infof("Approval sent for %s at token address %s, nonce = %s",
dex.BipIDSymbol(w.assetID), c.tokenAddress(), txOpts.Nonce)
if err != nil {
c.voidUnusedNonce()
return err
}
return err
w.log.Infof("Approval sent for %s at token address %s, nonce = %s",
dex.BipIDSymbol(w.assetID), c.tokenAddress(), txOpts.Nonce)
return nil
})
}

Expand Down Expand Up @@ -3387,7 +3398,7 @@ func (w *assetWallet) confirmRedemption(coinID dex.Bytes, redemption *asset.Rede

monitoredTx, err := w.getLatestMonitoredTx(txHash)
if err != nil {
w.log.Error("getLatestMonitoredTx error: %v", err)
w.log.Errorf("getLatestMonitoredTx error: %v", err)
return w.confirmRedemptionWithoutMonitoredTx(txHash, redemption, feeWallet)
}
// This mutex is locked inside of getLatestMonitoredTx.
Expand Down Expand Up @@ -3571,7 +3582,14 @@ func (w *ETHWallet) sendToAddr(addr common.Address, amt uint64, maxFeeRate *big.
if err != nil {
return nil, err
}
return w.node.sendTransaction(w.ctx, txOpts, addr, nil)
tx, err = w.node.sendTransaction(w.ctx, txOpts, addr, nil)
if err != nil {
if mRPC, is := w.node.(*multiRPCClient); is {
mRPC.voidUnusedNonce()
}
return nil, err
}
return tx, nil
}

// sendToAddr sends funds to the address.
Expand All @@ -3588,7 +3606,11 @@ func (w *TokenWallet) sendToAddr(addr common.Address, amt uint64, maxFeeRate *bi
return err
}
tx, err = c.transfer(txOpts, addr, w.evmify(amt))
return err
if err != nil {
c.voidUnusedNonce()
return err
}
return nil
})
}

Expand All @@ -3612,10 +3634,17 @@ func (w *assetWallet) initiate(ctx context.Context, assetID uint32, contracts []
}
w.nonceSendMtx.Lock()
defer w.nonceSendMtx.Unlock()
txOpts, _ := w.node.txOpts(ctx, val, gasLimit, dexeth.GweiToWei(maxFeeRate))
txOpts, err := w.node.txOpts(ctx, val, gasLimit, dexeth.GweiToWei(maxFeeRate))
if err != nil {
return nil, err
}
return tx, w.withContractor(contractVer, func(c contractor) error {
tx, err = c.initiate(txOpts, contracts)
return err
if err != nil {
c.voidUnusedNonce()
return err
}
return nil
})
}

Expand Down Expand Up @@ -3740,7 +3769,11 @@ func (w *assetWallet) redeem(ctx context.Context, assetID uint32, redemptions []

return tx, w.withContractor(contractVer, func(c contractor) error {
tx, err = c.redeem(txOpts, redemptions)
return err
if err != nil {
c.voidUnusedNonce()
return err
}
return nil
})
}

Expand All @@ -3756,12 +3789,16 @@ func (w *assetWallet) refund(secretHash [32]byte, maxFeeRate uint64, contractVer
defer w.nonceSendMtx.Unlock()
txOpts, err := w.node.txOpts(w.ctx, 0, gas.Refund, dexeth.GweiToWei(maxFeeRate))
if err != nil {
return nil, fmt.Errorf("addSignerToOpts error: %w", err)
return nil, err
}

return tx, w.withContractor(contractVer, func(c contractor) error {
tx, err = c.refund(txOpts, secretHash)
return err
if err != nil {
c.voidUnusedNonce()
return err
}
return nil
})
}

Expand Down Expand Up @@ -3799,7 +3836,8 @@ func checkTxStatus(receipt *types.Receipt, gasLimit uint64) error {
// factor of 2. The account should already have a trading balance of at least
// maxSwaps gwei (token or eth), and sufficient eth balance to cover the
// requisite tx fees.
func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps int, g *dexeth.Gases, waitForMined func()) error {
func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps int, g *dexeth.Gases,
toAddress common.Address, waitForMined func(), waitForReceipt func(ethFetcher, *types.Transaction) (*types.Receipt, error)) error {
tokenContractor, isToken := c.(tokenContractor)

stats := struct {
Expand Down Expand Up @@ -3872,7 +3910,7 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
if err != nil {
return fmt.Errorf("error constructing signed tx opts for transfer: %v", err)
}
transferTx, err = tokenContractor.transfer(txOpts, cl.address(), big.NewInt(1))
transferTx, err = tokenContractor.transfer(txOpts, toAddress, big.NewInt(1))
if err != nil {
return fmt.Errorf("error estimating transfer gas: %v", err)
}
Expand Down Expand Up @@ -3909,7 +3947,7 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
return fmt.Errorf("initiate error for %d swaps: %v", n, err)
}
waitForMined()
receipt, _, err := cl.transactionReceipt(ctx, tx.Hash())
receipt, err := waitForReceipt(cl, tx)
if err != nil {
return err
}
Expand All @@ -3919,15 +3957,15 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
stats.swaps = append(stats.swaps, receipt.GasUsed)

if isToken {
receipt, _, err = cl.transactionReceipt(ctx, approveTx.Hash())
receipt, err = waitForReceipt(cl, approveTx)
if err != nil {
return err
}
if err := checkTxStatus(receipt, txOpts.GasLimit); err != nil {
return err
}
stats.approves = append(stats.approves, receipt.GasUsed)
receipt, _, err = cl.transactionReceipt(ctx, transferTx.Hash())
receipt, err = waitForReceipt(cl, transferTx)
if err != nil {
return err
}
Expand Down Expand Up @@ -3962,7 +4000,7 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
return fmt.Errorf("redeem error for %d swaps: %v", n, err)
}
waitForMined()
receipt, _, err = cl.transactionReceipt(ctx, tx.Hash())
receipt, err = waitForReceipt(cl, tx)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit f683573

Please sign in to comment.