Skip to content

Commit d5ea00d

Browse files
committed
erc20: Add testnet usdc.
1 parent 4685064 commit d5ea00d

21 files changed

Lines changed: 607 additions & 218 deletions

File tree

client/asset/eth/contractor.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type contractor interface {
4444
// case will always be zero.
4545
value(context.Context, *types.Transaction) (incoming, outgoing uint64, err error)
4646
isRefundable(secretHash [32]byte) (bool, error)
47+
voidUnusedNonce()
4748
}
4849

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

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

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

148149
// redeem sends the redemptions to the swap contracts redeem method.
149-
func (c *contractorV0) redeem(txOpts *bind.TransactOpts, redemptions []*asset.Redemption) (*types.Transaction, error) {
150+
func (c *contractorV0) redeem(txOpts *bind.TransactOpts, redemptions []*asset.Redemption) (tx *types.Transaction, err error) {
150151
redemps := make([]swapv0.ETHSwapRedemption, 0, len(redemptions))
151152
secretHashes := make(map[[32]byte]bool, len(redemptions))
153+
152154
for _, r := range redemptions {
153155
secretB, secretHashB := r.Secret, r.Spends.SecretHash
154156
if len(secretB) != 32 || len(secretHashB) != 32 {
@@ -194,7 +196,7 @@ func (c *contractorV0) swap(ctx context.Context, secretHash [32]byte) (*dexeth.S
194196

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

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

323+
// voidUnusedNonce allows the next nonce received from a provider to be the same
324+
// as a recent nonce. Use when we fetch a nonce but error before or while
325+
// sending a transaction.
326+
func (c *contractorV0) voidUnusedNonce() {
327+
if mRPC, is := c.cb.(*multiRPCClient); is {
328+
mRPC.voidUnusedNonce()
329+
}
330+
}
331+
321332
// tokenContractorV0 is a contractor that implements the tokenContractor
322333
// methods, providing access to the methods of the token's ERC20 contract.
323334
type tokenContractorV0 struct {
@@ -395,13 +406,13 @@ func (c *tokenContractorV0) allowance(ctx context.Context) (*big.Int, error) {
395406

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

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

client/asset/eth/eth.go

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030
"decred.org/dcrdex/dex/config"
3131
"decred.org/dcrdex/dex/encode"
3232
"decred.org/dcrdex/dex/keygen"
33-
"decred.org/dcrdex/dex/networks/erc20"
3433
dexeth "decred.org/dcrdex/dex/networks/eth"
3534
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
3635
"github.com/decred/dcrd/hdkeychain/v3"
@@ -60,7 +59,8 @@ func registerToken(tokenID uint32, desc string, nets ...dex.Network) {
6059
func init() {
6160
asset.Register(BipID, &Driver{})
6261
// Test token
63-
registerToken(testTokenID, "A token wallet for the DEX test token. Used for testing DEX software.", dex.Simnet)
62+
registerToken(simnetTokenID, "A token wallet for the DEX test token. Used for testing DEX software.", dex.Simnet)
63+
registerToken(usdcTokenID, "The USDC Ethereum ERC20 token.", dex.Testnet)
6464
}
6565

6666
const (
@@ -83,7 +83,8 @@ const (
8383
)
8484

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

1760+
if dexeth.Tokens[w.assetID] == nil ||
1761+
dexeth.Tokens[w.assetID].NetTokens[w.net] == nil ||
1762+
dexeth.Tokens[w.assetID].NetTokens[w.net].SwapContracts[swaps.Version] == nil {
1763+
return fail("unable to find contract address for asset %d", w.assetID)
1764+
}
1765+
1766+
contractAddr := dexeth.Tokens[w.assetID].NetTokens[w.net].SwapContracts[swaps.Version].Address.String()
1767+
17591768
txHash := tx.Hash()
17601769
for _, swap := range swaps.Contracts {
17611770
var secretHash [dexeth.SecretHashSize]byte
@@ -1766,7 +1775,7 @@ func (w *TokenWallet) Swap(swaps *asset.Swaps) ([]asset.Receipt, asset.Coin, uin
17661775
txHash: txHash,
17671776
secretHash: secretHash,
17681777
ver: swaps.Version,
1769-
contractAddr: erc20.ContractAddresses[swaps.Version][w.net].String(),
1778+
contractAddr: contractAddr,
17701779
})
17711780
}
17721781

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

20402049
return tx, w.withTokenContractor(w.assetID, contractVer, func(c tokenContractor) error {
20412050
tx, err = c.approve(txOpts, amount)
2042-
if err == nil {
2043-
w.log.Infof("Approval sent for %s at token address %s, nonce = %s",
2044-
dex.BipIDSymbol(w.assetID), c.tokenAddress(), txOpts.Nonce)
2051+
if err != nil {
2052+
c.voidUnusedNonce()
2053+
return err
20452054
}
2046-
return err
2055+
w.log.Infof("Approval sent for %s at token address %s, nonce = %s",
2056+
dex.BipIDSymbol(w.assetID), c.tokenAddress(), txOpts.Nonce)
2057+
return nil
20472058
})
20482059
}
20492060

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

33883399
monitoredTx, err := w.getLatestMonitoredTx(txHash)
33893400
if err != nil {
3390-
w.log.Error("getLatestMonitoredTx error: %v", err)
3401+
w.log.Errorf("getLatestMonitoredTx error: %v", err)
33913402
return w.confirmRedemptionWithoutMonitoredTx(txHash, redemption, feeWallet)
33923403
}
33933404
// This mutex is locked inside of getLatestMonitoredTx.
@@ -3571,7 +3582,14 @@ func (w *ETHWallet) sendToAddr(addr common.Address, amt uint64, maxFeeRate *big.
35713582
if err != nil {
35723583
return nil, err
35733584
}
3574-
return w.node.sendTransaction(w.ctx, txOpts, addr, nil)
3585+
tx, err = w.node.sendTransaction(w.ctx, txOpts, addr, nil)
3586+
if err != nil {
3587+
if mRPC, is := w.node.(*multiRPCClient); is {
3588+
mRPC.voidUnusedNonce()
3589+
}
3590+
return nil, err
3591+
}
3592+
return tx, nil
35753593
}
35763594

35773595
// sendToAddr sends funds to the address.
@@ -3588,7 +3606,11 @@ func (w *TokenWallet) sendToAddr(addr common.Address, amt uint64, maxFeeRate *bi
35883606
return err
35893607
}
35903608
tx, err = c.transfer(txOpts, addr, w.evmify(amt))
3591-
return err
3609+
if err != nil {
3610+
c.voidUnusedNonce()
3611+
return err
3612+
}
3613+
return nil
35923614
})
35933615
}
35943616

@@ -3612,10 +3634,17 @@ func (w *assetWallet) initiate(ctx context.Context, assetID uint32, contracts []
36123634
}
36133635
w.nonceSendMtx.Lock()
36143636
defer w.nonceSendMtx.Unlock()
3615-
txOpts, _ := w.node.txOpts(ctx, val, gasLimit, dexeth.GweiToWei(maxFeeRate))
3637+
txOpts, err := w.node.txOpts(ctx, val, gasLimit, dexeth.GweiToWei(maxFeeRate))
3638+
if err != nil {
3639+
return nil, err
3640+
}
36163641
return tx, w.withContractor(contractVer, func(c contractor) error {
36173642
tx, err = c.initiate(txOpts, contracts)
3618-
return err
3643+
if err != nil {
3644+
c.voidUnusedNonce()
3645+
return err
3646+
}
3647+
return nil
36193648
})
36203649
}
36213650

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

37413770
return tx, w.withContractor(contractVer, func(c contractor) error {
37423771
tx, err = c.redeem(txOpts, redemptions)
3743-
return err
3772+
if err != nil {
3773+
c.voidUnusedNonce()
3774+
return err
3775+
}
3776+
return nil
37443777
})
37453778
}
37463779

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

37623795
return tx, w.withContractor(contractVer, func(c contractor) error {
37633796
tx, err = c.refund(txOpts, secretHash)
3764-
return err
3797+
if err != nil {
3798+
c.voidUnusedNonce()
3799+
return err
3800+
}
3801+
return nil
37653802
})
37663803
}
37673804

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

38053843
stats := struct {
@@ -3872,7 +3910,7 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
38723910
if err != nil {
38733911
return fmt.Errorf("error constructing signed tx opts for transfer: %v", err)
38743912
}
3875-
transferTx, err = tokenContractor.transfer(txOpts, cl.address(), big.NewInt(1))
3913+
transferTx, err = tokenContractor.transfer(txOpts, toAddress, big.NewInt(1))
38763914
if err != nil {
38773915
return fmt.Errorf("error estimating transfer gas: %v", err)
38783916
}
@@ -3909,7 +3947,7 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
39093947
return fmt.Errorf("initiate error for %d swaps: %v", n, err)
39103948
}
39113949
waitForMined()
3912-
receipt, _, err := cl.transactionReceipt(ctx, tx.Hash())
3950+
receipt, err := waitForReceipt(cl, tx)
39133951
if err != nil {
39143952
return err
39153953
}
@@ -3919,15 +3957,15 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
39193957
stats.swaps = append(stats.swaps, receipt.GasUsed)
39203958

39213959
if isToken {
3922-
receipt, _, err = cl.transactionReceipt(ctx, approveTx.Hash())
3960+
receipt, err = waitForReceipt(cl, approveTx)
39233961
if err != nil {
39243962
return err
39253963
}
39263964
if err := checkTxStatus(receipt, txOpts.GasLimit); err != nil {
39273965
return err
39283966
}
39293967
stats.approves = append(stats.approves, receipt.GasUsed)
3930-
receipt, _, err = cl.transactionReceipt(ctx, transferTx.Hash())
3968+
receipt, err = waitForReceipt(cl, transferTx)
39313969
if err != nil {
39323970
return err
39333971
}
@@ -3962,7 +4000,7 @@ func GetGasEstimates(ctx context.Context, cl ethFetcher, c contractor, maxSwaps
39624000
return fmt.Errorf("redeem error for %d swaps: %v", n, err)
39634001
}
39644002
waitForMined()
3965-
receipt, _, err = cl.transactionReceipt(ctx, tx.Hash())
4003+
receipt, err = waitForReceipt(cl, tx)
39664004
if err != nil {
39674005
return err
39684006
}

0 commit comments

Comments
 (0)