From 8e3cb571837ca0db637c1334906e9b1d4f0d4112 Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Sun, 11 Aug 2024 02:19:29 +0800 Subject: [PATCH 1/3] refactor: support non-blocking verfion for calling contracts --- chainio/clients/avsregistry/writer.go | 18 ++++++---- chainio/clients/elcontracts/writer.go | 33 +++++++++++------ chainio/txmgr/geometric/geometric.go | 6 +++- .../txmgr/geometric/geometric_example_test.go | 2 +- chainio/txmgr/geometric/geometric_test.go | 20 +++++------ chainio/txmgr/simple.go | 36 ++++++++++++++----- chainio/txmgr/txmgr.go | 2 +- services/bls_aggregation/blsagg_test.go | 9 ++++- signerv2/kms_signer_test.go | 2 +- 9 files changed, 87 insertions(+), 41 deletions(-) diff --git a/chainio/clients/avsregistry/writer.go b/chainio/clients/avsregistry/writer.go index 9609d4b4..df56d83b 100644 --- a/chainio/clients/avsregistry/writer.go +++ b/chainio/clients/avsregistry/writer.go @@ -201,6 +201,7 @@ func (w *ChainWriter) RegisterOperatorInQuorumWithAVSRegistryCoordinator( blsKeyPair *bls.KeyPair, quorumNumbers types.QuorumNums, socket string, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { operatorAddr := crypto.PubkeyToAddress(operatorEcdsaPrivateKey.PublicKey) w.logger.Info( @@ -271,7 +272,7 @@ func (w *ChainWriter) RegisterOperatorInQuorumWithAVSRegistryCoordinator( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -302,6 +303,7 @@ func (w *ChainWriter) RegisterOperator( blsKeyPair *bls.KeyPair, quorumNumbers types.QuorumNums, socket string, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { operatorAddr := crypto.PubkeyToAddress(operatorEcdsaPrivateKey.PublicKey) w.logger.Info( @@ -390,7 +392,7 @@ func (w *ChainWriter) RegisterOperator( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -417,6 +419,7 @@ func (w *ChainWriter) UpdateStakesOfEntireOperatorSetForQuorums( ctx context.Context, operatorsPerQuorum [][]gethcommon.Address, quorumNumbers types.QuorumNums, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { w.logger.Info("updating stakes for entire operator set", "quorumNumbers", quorumNumbers) noSendTxOpts, err := w.txMgr.GetNoSendTxOpts() @@ -431,7 +434,7 @@ func (w *ChainWriter) UpdateStakesOfEntireOperatorSetForQuorums( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -449,6 +452,7 @@ func (w *ChainWriter) UpdateStakesOfEntireOperatorSetForQuorums( func (w *ChainWriter) UpdateStakesOfOperatorSubsetForAllQuorums( ctx context.Context, operators []gethcommon.Address, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { w.logger.Info("updating stakes of operator subset for all quorums", "operators", operators) noSendTxOpts, err := w.txMgr.GetNoSendTxOpts() @@ -459,7 +463,7 @@ func (w *ChainWriter) UpdateStakesOfOperatorSubsetForAllQuorums( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -477,6 +481,7 @@ func (w *ChainWriter) DeregisterOperator( ctx context.Context, quorumNumbers types.QuorumNums, pubkey regcoord.BN254G1Point, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { w.logger.Info("deregistering operator with the AVS's registry coordinator") noSendTxOpts, err := w.txMgr.GetNoSendTxOpts() @@ -487,7 +492,7 @@ func (w *ChainWriter) DeregisterOperator( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -502,6 +507,7 @@ func (w *ChainWriter) DeregisterOperator( func (w *ChainWriter) UpdateSocket( ctx context.Context, socket types.Socket, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { noSendTxOpts, err := w.txMgr.GetNoSendTxOpts() if err != nil { @@ -511,7 +517,7 @@ func (w *ChainWriter) UpdateSocket( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send UpdateSocket tx with err: " + err.Error()) } diff --git a/chainio/clients/elcontracts/writer.go b/chainio/clients/elcontracts/writer.go index 9658be62..c921c331 100644 --- a/chainio/clients/elcontracts/writer.go +++ b/chainio/clients/elcontracts/writer.go @@ -155,7 +155,11 @@ func NewWriterFromConfig( ), nil } -func (w *ChainWriter) RegisterAsOperator(ctx context.Context, operator types.Operator) (*gethtypes.Receipt, error) { +func (w *ChainWriter) RegisterAsOperator( + ctx context.Context, + operator types.Operator, + waitForReceipt bool, +) (*gethtypes.Receipt, error) { if w.delegationManager == nil { return nil, errors.New("DelegationManager contract not provided") } @@ -177,7 +181,7 @@ func (w *ChainWriter) RegisterAsOperator(ctx context.Context, operator types.Ope if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -189,6 +193,7 @@ func (w *ChainWriter) RegisterAsOperator(ctx context.Context, operator types.Ope func (w *ChainWriter) UpdateOperatorDetails( ctx context.Context, operator types.Operator, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { if w.delegationManager == nil { return nil, errors.New("DelegationManager contract not provided") @@ -212,7 +217,7 @@ func (w *ChainWriter) UpdateOperatorDetails( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -227,7 +232,8 @@ func (w *ChainWriter) UpdateOperatorDetails( return receipt, nil } -func (w *ChainWriter) UpdateMetadataURI(ctx context.Context, uri string) (*gethtypes.Receipt, error) { +func (w *ChainWriter) UpdateMetadataURI(ctx context.Context, uri string, waitForReceipt bool, +) (*gethtypes.Receipt, error) { if w.delegationManager == nil { return nil, errors.New("DelegationManager contract not provided") } @@ -241,7 +247,7 @@ func (w *ChainWriter) UpdateMetadataURI(ctx context.Context, uri string) (*getht if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -258,6 +264,7 @@ func (w *ChainWriter) DepositERC20IntoStrategy( ctx context.Context, strategyAddr gethcommon.Address, amount *big.Int, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { if w.strategyManager == nil { return nil, errors.New("StrategyManager contract not provided") @@ -280,7 +287,7 @@ func (w *ChainWriter) DepositERC20IntoStrategy( if err != nil { return nil, errors.Join(errors.New("failed to approve token transfer"), err) } - _, err = w.txMgr.Send(ctx, tx) + _, err = w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -289,7 +296,7 @@ func (w *ChainWriter) DepositERC20IntoStrategy( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, errors.New("failed to send tx with err: " + err.Error()) } @@ -301,6 +308,7 @@ func (w *ChainWriter) DepositERC20IntoStrategy( func (w *ChainWriter) SetClaimerFor( ctx context.Context, claimer gethcommon.Address, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { if w.rewardsCoordinator == nil { return nil, errors.New("RewardsCoordinator contract not provided") @@ -315,7 +323,7 @@ func (w *ChainWriter) SetClaimerFor( if err != nil { return nil, err } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, utils.WrapError("failed to send tx", err) } @@ -327,6 +335,7 @@ func (w *ChainWriter) ProcessClaim( ctx context.Context, claim rewardscoordinator.IRewardsCoordinatorRewardsMerkleClaim, earnerAddress gethcommon.Address, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { if w.rewardsCoordinator == nil { return nil, errors.New("RewardsCoordinator contract not provided") @@ -341,7 +350,7 @@ func (w *ChainWriter) ProcessClaim( if err != nil { return nil, utils.WrapError("failed to create ProcessClaim tx", err) } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, utils.WrapError("failed to send tx", err) } @@ -355,6 +364,7 @@ func (w *ChainWriter) ForceDeregisterFromOperatorSets( avs gethcommon.Address, operatorSetIds []uint32, operatorSignature avsdirectory.ISignatureUtilsSignatureWithSaltAndExpiry, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { if w.avsDirectory == nil { return nil, errors.New("AVSDirectory contract not provided") @@ -377,7 +387,7 @@ func (w *ChainWriter) ForceDeregisterFromOperatorSets( return nil, utils.WrapError("failed to create ForceDeregisterFromOperatorSets tx", err) } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, utils.WrapError("failed to send tx", err) } @@ -390,6 +400,7 @@ func (w *ChainWriter) SetOperatorCommissionBips( operatorSet rewardscoordinator.IAVSDirectoryOperatorSet, rewardType uint8, commissionBips uint16, + waitForReceipt bool, ) (*gethtypes.Receipt, error) { if w.rewardsCoordinator == nil { return nil, errors.New("RewardsCoordinator contract not provided") @@ -405,7 +416,7 @@ func (w *ChainWriter) SetOperatorCommissionBips( return nil, utils.WrapError("failed to create SetOperatorCommissionBips tx", err) } - receipt, err := w.txMgr.Send(ctx, tx) + receipt, err := w.txMgr.Send(ctx, tx, waitForReceipt) if err != nil { return nil, utils.WrapError("failed to send tx", err) } diff --git a/chainio/txmgr/geometric/geometric.go b/chainio/txmgr/geometric/geometric.go index 896c80b0..722d3dfe 100644 --- a/chainio/txmgr/geometric/geometric.go +++ b/chainio/txmgr/geometric/geometric.go @@ -169,7 +169,11 @@ func newTxnRequest(tx *types.Transaction) *txnRequest { // but it does not do nonce management, so the tx argument must have the correct nonce already set. // // Send is blocking and safe to call concurrently, so sending multiple txs in parallel is safe. -func (t *GeometricTxManager) Send(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) { +func (t *GeometricTxManager) Send( + ctx context.Context, + tx *types.Transaction, + waitForReceipt bool, +) (*types.Receipt, error) { return t.processTransaction(ctx, newTxnRequest(tx)) } diff --git a/chainio/txmgr/geometric/geometric_example_test.go b/chainio/txmgr/geometric/geometric_example_test.go index c7f0e75a..d77740a9 100644 --- a/chainio/txmgr/geometric/geometric_example_test.go +++ b/chainio/txmgr/geometric/geometric_example_test.go @@ -43,7 +43,7 @@ func ExampleGeometricTxManager() { client, txmgr := createTxMgr(anvilUrl, ecdsaPrivateKey) tx := createTx(client, address) - _, err = txmgr.Send(context.TODO(), tx) + _, err = txmgr.Send(context.TODO(), tx, true) if err != nil { panic(err) } diff --git a/chainio/txmgr/geometric/geometric_test.go b/chainio/txmgr/geometric/geometric_test.go index 250e79fe..01c62bc0 100644 --- a/chainio/txmgr/geometric/geometric_test.go +++ b/chainio/txmgr/geometric/geometric_test.go @@ -76,7 +76,7 @@ func TestGeometricTxManager(t *testing.T) { unsignedTx := newUnsignedEthTransferTx(0, nil) ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx) + txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx, true) require.NoError(t, err) h.validateTxReceipt(t, txReceipt) @@ -90,7 +90,7 @@ func TestGeometricTxManager(t *testing.T) { unsignedTx := newUnsignedEthTransferTx(0, nil) ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx) + txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx, true) require.NoError(t, err) h.validateTxReceipt(t, txReceipt) @@ -102,7 +102,7 @@ func TestGeometricTxManager(t *testing.T) { unsignedTx := newUnsignedEthTransferTx(0, big.NewInt(1)) ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx) + txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx, true) // ethBackend returns an error if the tx's gasFeeCap is less than the baseFeePerGas // this test makes sure that even setting a gasFeeCap less than the baseFeePerGas in the tx (1 above) still // works, @@ -128,7 +128,7 @@ func TestGeometricTxManager(t *testing.T) { g.Go(func() error { ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - txReceipt, err := h.txmgr.Send(ctxWithTimeout, tx) + txReceipt, err := h.txmgr.Send(ctxWithTimeout, tx, true) if err == nil { txReceipts[nonce] = txReceipt } @@ -157,7 +157,7 @@ func TestGeometricTxManager(t *testing.T) { g.Go(func() error { ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - txReceipt, err := h.txmgr.Send(ctxWithTimeout, tx) + txReceipt, err := h.txmgr.Send(ctxWithTimeout, tx, true) if err == nil { txReceipts[nonce] = txReceipt } @@ -179,7 +179,7 @@ func TestGeometricTxManager(t *testing.T) { tx := newUnsignedEthTransferTx(nonce, nil) ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - _, err := h.txmgr.Send(ctxWithTimeout, tx) + _, err := h.txmgr.Send(ctxWithTimeout, tx, true) require.NoError(t, err) } @@ -191,7 +191,7 @@ func TestGeometricTxManager(t *testing.T) { tx := newUnsignedEthTransferTx(uint64(100), nil) ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() - _, err := h.txmgr.Send(ctxWithTimeout, tx) + _, err := h.txmgr.Send(ctxWithTimeout, tx, true) require.Equal(t, context.DeadlineExceeded, err) }) @@ -202,11 +202,11 @@ func TestGeometricTxManager(t *testing.T) { unsignedTx := newUnsignedEthTransferTx(0, nil) ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - _, err := h.txmgr.Send(ctxWithTimeout, unsignedTx) + _, err := h.txmgr.Send(ctxWithTimeout, unsignedTx, true) require.NoError(t, err) unsignedTx = newUnsignedEthTransferTx(0, nil) - _, err = h.txmgr.Send(ctxWithTimeout, unsignedTx) + _, err = h.txmgr.Send(ctxWithTimeout, unsignedTx, true) require.Error(t, err) }) } @@ -409,7 +409,7 @@ func TestGeometricTxManagerIntegration(t *testing.T) { unsignedTx := newUnsignedEthTransferTx(0, nil) ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx) + txReceipt, err := h.txmgr.Send(ctxWithTimeout, unsignedTx, true) require.NoError(t, err) h.validateTxReceipt(t, txReceipt) diff --git a/chainio/txmgr/simple.go b/chainio/txmgr/simple.go index ff4479b8..942ff76e 100644 --- a/chainio/txmgr/simple.go +++ b/chainio/txmgr/simple.go @@ -67,7 +67,30 @@ func (m *SimpleTxManager) WithGasLimitMultiplier(multiplier float64) *SimpleTxMa // If you pass in a signed transaction it will ignore the signature // and resign the transaction after adding the nonce and gas limit. // To check out the whole flow on how this works, check out the README.md in this folder -func (m *SimpleTxManager) Send(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) { +func (m *SimpleTxManager) Send( + ctx context.Context, + tx *types.Transaction, + waitForReceipt bool, +) (*types.Receipt, error) { + + r, err := m.send(ctx, tx) + if err != nil { + return nil, errors.Join(errors.New("send: failed to estimate gas and nonce"), err) + } + if !waitForReceipt { + return r, nil + } + + receipt, err := m.waitForReceipt(ctx, r.TxHash.Hex()) + if err != nil { + log.Info("Transaction receipt not found", "err", err) + return nil, err + } + + return receipt, nil +} + +func (m *SimpleTxManager) send(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) { // Estimate gas and nonce // can't print tx hash in logs because the tx changes below when we complete and sign it // so the txHash is meaningless at this point @@ -89,14 +112,9 @@ func (m *SimpleTxManager) Send(ctx context.Context, tx *types.Transaction) (*typ if err != nil { return nil, errors.Join(errors.New("send: failed to estimate gas and nonce"), err) } - - receipt, err := m.waitForReceipt(ctx, txID) - if err != nil { - log.Info("Transaction receipt not found", "err", err) - return nil, err - } - - return receipt, nil + return &types.Receipt{ + TxHash: common.HexToHash(txID), + }, nil } func NoopSigner(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { diff --git a/chainio/txmgr/txmgr.go b/chainio/txmgr/txmgr.go index da5f369d..c489c7eb 100644 --- a/chainio/txmgr/txmgr.go +++ b/chainio/txmgr/txmgr.go @@ -11,7 +11,7 @@ type TxManager interface { // Send is used to sign and send a transaction to an evm chain // It takes an unsigned transaction and then signs it before sending // It might also take care of nonce management and gas estimation, depending on the implementation - Send(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) + Send(ctx context.Context, tx *types.Transaction, waitForReceipt bool) (*types.Receipt, error) // GetNoSendTxOpts generates a TransactOpts with // - NoSend=true: b/c we want to manage the sending ourselves diff --git a/services/bls_aggregation/blsagg_test.go b/services/bls_aggregation/blsagg_test.go index cfdc79ae..40fa992f 100644 --- a/services/bls_aggregation/blsagg_test.go +++ b/services/bls_aggregation/blsagg_test.go @@ -1161,7 +1161,14 @@ func TestIntegrationBlsAgg(t *testing.T) { // register operator quorumNumbers := types.QuorumNums{0} - _, err = avsWriter.RegisterOperator(context.Background(), ecdsaPrivKey, blsKeyPair, quorumNumbers, "socket") + _, err = avsWriter.RegisterOperator( + context.Background(), + ecdsaPrivKey, + blsKeyPair, + quorumNumbers, + "socket", + true, + ) require.NoError(t, err) // create the task related parameters: RBN, quorumThresholdPercentages, taskIndex and taskResponse diff --git a/signerv2/kms_signer_test.go b/signerv2/kms_signer_test.go index b08d6990..b66587f9 100644 --- a/signerv2/kms_signer_test.go +++ b/signerv2/kms_signer_test.go @@ -116,7 +116,7 @@ func TestSendTransaction(t *testing.T) { Nonce: 0, To: &zeroAddr, Value: big.NewInt(1_000_000_000_000_000_000), - })) + }), true) assert.Nil(t, err) assert.NotNil(t, receipt) balance, err := ethClient.BalanceAt(context.Background(), keyAddr, nil) From 64a3e084c6079a31e5b9ea4ad0c64bea43ebc885 Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Tue, 13 Aug 2024 22:11:52 +0800 Subject: [PATCH 2/3] chore: add comment for dummy txn receipt --- chainio/txmgr/simple.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chainio/txmgr/simple.go b/chainio/txmgr/simple.go index 942ff76e..fb714e20 100644 --- a/chainio/txmgr/simple.go +++ b/chainio/txmgr/simple.go @@ -67,6 +67,8 @@ func (m *SimpleTxManager) WithGasLimitMultiplier(multiplier float64) *SimpleTxMa // If you pass in a signed transaction it will ignore the signature // and resign the transaction after adding the nonce and gas limit. // To check out the whole flow on how this works, check out the README.md in this folder +// One should be aware of that if waitForReceipt is set to false, the function returns a dummy transaction +// receipt with a transaction hash. func (m *SimpleTxManager) Send( ctx context.Context, tx *types.Transaction, From 40229a563a33f85bff91d6eaa2d78e04848ef053 Mon Sep 17 00:00:00 2001 From: xiaohuo Date: Tue, 13 Aug 2024 22:35:25 +0800 Subject: [PATCH 3/3] chore: fix comment --- chainio/txmgr/simple.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/chainio/txmgr/simple.go b/chainio/txmgr/simple.go index fb714e20..6c732ad2 100644 --- a/chainio/txmgr/simple.go +++ b/chainio/txmgr/simple.go @@ -67,8 +67,7 @@ func (m *SimpleTxManager) WithGasLimitMultiplier(multiplier float64) *SimpleTxMa // If you pass in a signed transaction it will ignore the signature // and resign the transaction after adding the nonce and gas limit. // To check out the whole flow on how this works, check out the README.md in this folder -// One should be aware of that if waitForReceipt is set to false, the function returns a dummy transaction -// receipt with a transaction hash. +// If waitForReceipt is set to false, the function returns a dummy transaction receipt containing a transaction hash. func (m *SimpleTxManager) Send( ctx context.Context, tx *types.Transaction,