-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
2,592 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package txm | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
evmtypes "github.com/ethereum/go-ethereum/core/types" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
|
||
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets" | ||
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas" | ||
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/txm/types" | ||
) | ||
|
||
type Keystore interface { | ||
SignTx(ctx context.Context, fromAddress common.Address, tx *evmtypes.Transaction, chainID *big.Int) (*evmtypes.Transaction, error) | ||
} | ||
|
||
type attemptBuilder struct { | ||
chainID *big.Int | ||
priceMax *assets.Wei // TODO: PriceMax per key level | ||
estimator gas.EvmFeeEstimator | ||
keystore Keystore | ||
} | ||
|
||
func NewAttemptBuilder(chainID *big.Int, priceMax *assets.Wei, estimator gas.EvmFeeEstimator, keystore Keystore) *attemptBuilder { | ||
return &attemptBuilder{ | ||
chainID: chainID, | ||
estimator: estimator, | ||
keystore: keystore, | ||
} | ||
} | ||
|
||
func (a *attemptBuilder) NewAttempt(ctx context.Context, lggr logger.Logger, tx *types.Transaction, dynamic bool) (*types.Attempt, error) { | ||
fee, estimatedGasLimit, err := a.estimator.GetFee(ctx, tx.Data, tx.SpecifiedGasLimit, a.priceMax, &tx.FromAddress, &tx.ToAddress) | ||
if err != nil { | ||
return nil, err | ||
} | ||
txType := evmtypes.LegacyTxType | ||
if dynamic { | ||
txType = evmtypes.DynamicFeeTxType | ||
} | ||
return a.newCustomAttempt(ctx, tx, fee, estimatedGasLimit, byte(txType), lggr) | ||
} | ||
|
||
func (a *attemptBuilder) NewBumpAttempt(ctx context.Context, lggr logger.Logger, tx *types.Transaction, previousAttempt types.Attempt) (*types.Attempt, error) { | ||
bumpedFee, bumpedFeeLimit, err := a.estimator.BumpFee(ctx, previousAttempt.Fee, tx.SpecifiedGasLimit, a.priceMax, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return a.newCustomAttempt(ctx, tx, bumpedFee, bumpedFeeLimit, previousAttempt.Type, lggr) | ||
} | ||
|
||
func (a *attemptBuilder) newCustomAttempt( | ||
ctx context.Context, | ||
tx *types.Transaction, | ||
fee gas.EvmFee, | ||
estimatedGasLimit uint64, | ||
txType byte, | ||
lggr logger.Logger, | ||
) (attempt *types.Attempt, err error) { | ||
switch txType { | ||
case 0x0: | ||
if fee.GasPrice == nil { | ||
err = fmt.Errorf("attemptID: %v of txID: %v, is a type 0 transaction but estimator did not return legacy fee", tx.ID, attempt.ID) | ||
logger.Sugared(lggr).AssumptionViolation(err.Error()) | ||
return | ||
} | ||
return a.newLegacyAttempt(ctx, tx, fee.GasPrice, estimatedGasLimit) | ||
case 0x2: | ||
if !fee.ValidDynamic() { | ||
err = fmt.Errorf("attemptID %v of txID: %v, is a type 2 transaction but estimator did not return dynamic fee", tx.ID, attempt.ID) | ||
logger.Sugared(lggr).AssumptionViolation(err.Error()) | ||
return | ||
} | ||
return a.newDynamicFeeAttempt(ctx, tx, fee.DynamicFee, estimatedGasLimit) | ||
default: | ||
return nil, fmt.Errorf("cannot build attempt, unrecognized transaction type: %v", txType) | ||
} | ||
} | ||
|
||
func (a *attemptBuilder) newLegacyAttempt(ctx context.Context, tx *types.Transaction, gasPrice *assets.Wei, estimatedGasLimit uint64) (*types.Attempt, error) { | ||
var data []byte | ||
if !tx.IsPurgeable { | ||
data = tx.Data | ||
} | ||
legacyTx := evmtypes.LegacyTx{ | ||
Nonce: tx.Nonce, | ||
To: &tx.ToAddress, | ||
Value: tx.Value, | ||
Gas: estimatedGasLimit, | ||
GasPrice: gasPrice.ToInt(), | ||
Data: data, | ||
} | ||
|
||
signedTx, err := a.keystore.SignTx(ctx, tx.FromAddress, evmtypes.NewTx(&legacyTx), a.chainID) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to sign attempt for txID: %v, err: %w", tx.ID, err) | ||
} | ||
|
||
attempt := &types.Attempt{ | ||
TxID: tx.ID, | ||
Fee: gas.EvmFee{GasPrice: gasPrice}, | ||
Hash: signedTx.Hash(), | ||
GasLimit: estimatedGasLimit, | ||
SignedTransaction: signedTx, | ||
} | ||
|
||
return attempt, nil | ||
} | ||
|
||
func (a *attemptBuilder) newDynamicFeeAttempt(ctx context.Context, tx *types.Transaction, dynamicFee gas.DynamicFee, estimatedGasLimit uint64) (*types.Attempt, error) { | ||
var data []byte | ||
if !tx.IsPurgeable { | ||
data = tx.Data | ||
} | ||
dynamicTx := evmtypes.DynamicFeeTx{ | ||
Nonce: tx.Nonce, | ||
To: &tx.ToAddress, | ||
Value: tx.Value, | ||
Gas: estimatedGasLimit, | ||
GasFeeCap: dynamicFee.GasFeeCap.ToInt(), | ||
GasTipCap: dynamicFee.GasTipCap.ToInt(), | ||
Data: data, | ||
} | ||
|
||
signedTx, err := a.keystore.SignTx(ctx, tx.FromAddress, evmtypes.NewTx(&dynamicTx), a.chainID) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to sign attempt for txID: %v, err: %w", tx.ID, err) | ||
} | ||
|
||
attempt := &types.Attempt{ | ||
TxID: tx.ID, | ||
Fee: gas.EvmFee{DynamicFee: gas.DynamicFee{GasFeeCap: dynamicFee.GasFeeCap, GasTipCap: dynamicFee.GasTipCap}}, | ||
Hash: signedTx.Hash(), | ||
GasLimit: estimatedGasLimit, | ||
SignedTransaction: signedTx, | ||
} | ||
|
||
return attempt, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package txm | ||
|
||
import ( | ||
"context" | ||
"crypto/ecdsa" | ||
"math/big" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
) | ||
|
||
type DummyKeystore struct { | ||
privateKey *ecdsa.PrivateKey | ||
} | ||
|
||
func NewKeystore(privateKeyString string) *DummyKeystore { | ||
return &DummyKeystore{} | ||
} | ||
|
||
func (k *DummyKeystore) Add(privateKeyString string) error { | ||
privateKey, err := crypto.HexToECDSA(privateKeyString) | ||
if err != nil { | ||
return err | ||
} | ||
k.privateKey = privateKey | ||
return nil | ||
} | ||
|
||
func (k *DummyKeystore) SignTx(_ context.Context, fromAddress common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { | ||
return types.SignTx(tx, types.NewEIP155Signer(chainID), k.privateKey) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.