Skip to content

Commit dc1fc78

Browse files
committed
move contract deployment hook to dedicated file
1 parent 0ac622b commit dc1fc78

File tree

2 files changed

+85
-169
lines changed

2 files changed

+85
-169
lines changed

seth/client.go

Lines changed: 1 addition & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,11 +1002,6 @@ func (cl *ContractLoader[T]) LoadContract(name string, address common.Address, a
10021002
return wrapperInitFn(address, cl.Client.Client)
10031003
}
10041004

1005-
type ContractDeploymentHooks struct {
1006-
Pre func(auth *bind.TransactOpts, name string, abi abi.ABI, bytecode []byte, params ...interface{}) error
1007-
Post func(client *Client, tx *types.Transaction) error
1008-
}
1009-
10101005
func (m *Client) DeployContractWithHooks(hooks ContractDeploymentHooks, auth *bind.TransactOpts, name string, abi abi.ABI, bytecode []byte, params ...interface{}) (DeploymentData, error) {
10111006
L.Info().
10121007
Msgf("Started deploying %s contract", name)
@@ -1068,173 +1063,10 @@ func (m *Client) DeployContractWithHooks(hooks ContractDeploymentHooks, auth *bi
10681063
// to the contract map, so that we can use that, when tracing transactions. It is suggested to use name identical to the name of the contract Solidity file.
10691064
func (m *Client) DeployContract(auth *bind.TransactOpts, name string, abi abi.ABI, bytecode []byte, params ...interface{}) (DeploymentData, error) {
10701065
hooks := ContractDeploymentHooks{
1071-
Post: func(client *Client, tx *types.Transaction) error {
1072-
err := retry.Do(
1073-
func() error {
1074-
ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration())
1075-
_, err := bind.WaitDeployed(ctx, m.Client, tx)
1076-
cancel()
1077-
1078-
// let's make sure that deployment transaction was successful, before retrying
1079-
if err != nil && !errors.Is(err, context.DeadlineExceeded) {
1080-
ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration())
1081-
receipt, mineErr := bind.WaitMined(ctx, m.Client, tx)
1082-
if mineErr != nil {
1083-
cancel()
1084-
return mineErr
1085-
}
1086-
cancel()
1087-
1088-
if receipt.Status == 0 {
1089-
return errors.New("deployment transaction was reverted")
1090-
}
1091-
}
1092-
1093-
return err
1094-
}, retry.OnRetry(func(i uint, retryErr error) {
1095-
switch {
1096-
case errors.Is(retryErr, context.DeadlineExceeded):
1097-
replacementTx, replacementErr := prepareReplacementTransaction(m, tx)
1098-
if replacementErr != nil {
1099-
L.Debug().Str("Current error", retryErr.Error()).Str("Replacement error", replacementErr.Error()).Uint("Attempt", i+1).Msg("Failed to prepare replacement transaction for contract deployment. Retrying with the original one")
1100-
return
1101-
}
1102-
tx = replacementTx
1103-
default:
1104-
// do nothing, just wait again until it's mined
1105-
}
1106-
L.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i+1).Msg("Waiting for contract to be deployed")
1107-
}),
1108-
retry.DelayType(retry.FixedDelay),
1109-
// if gas bump retries are set to 0, we still want to retry 10 times, because what we will be retrying will be other errors (no code at address, etc.)
1110-
// downside is that if retries are enabled and their number is low other retry errors will be retried only that number of times
1111-
// (we could have custom logic for different retry count per error, but that seemed like an overkill, so it wasn't implemented)
1112-
retry.Attempts(func() uint {
1113-
if m.Cfg.GasBumpRetries() != 0 {
1114-
return m.Cfg.GasBumpRetries()
1115-
}
1116-
return 10
1117-
}()),
1118-
retry.RetryIf(func(err error) bool {
1119-
return strings.Contains(strings.ToLower(err.Error()), "no contract code at given address") ||
1120-
strings.Contains(strings.ToLower(err.Error()), "no contract code after deployment") ||
1121-
(m.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded))
1122-
}),
1123-
)
1124-
1125-
if err != nil {
1126-
// pass this specific error, so that Decode knows that it's not the actual revert reason
1127-
_, _ = m.Decode(tx, errors.New(ErrContractDeploymentFailed))
1128-
1129-
return wrapErrInMessageWithASuggestion(m.rewriteDeploymentError(err))
1130-
}
1131-
1132-
return nil
1133-
},
1066+
Post: RetryingPostDeployHook,
11341067
}
11351068

11361069
return m.DeployContractWithHooks(hooks, auth, name, abi, bytecode, params...)
1137-
1138-
// L.Info().
1139-
// Msgf("Started deploying %s contract", name)
1140-
1141-
// if auth.Context != nil {
1142-
// if err, ok := auth.Context.Value(ContextErrorKey{}).(error); ok {
1143-
// return DeploymentData{}, errors.Wrapf(err, "aborted contract deployment for %s, because context passed in transaction options had an error set", name)
1144-
// }
1145-
// }
1146-
1147-
// address, tx, contract, err := bind.DeployContract(auth, abi, bytecode, m.Client, params...)
1148-
// if err != nil {
1149-
// return DeploymentData{}, wrapErrInMessageWithASuggestion(err)
1150-
// }
1151-
1152-
// L.Info().
1153-
// Str("Address", address.Hex()).
1154-
// Str("TXHash", tx.Hash().Hex()).
1155-
// Msgf("Waiting for %s contract deployment to finish", name)
1156-
1157-
// m.ContractAddressToNameMap.AddContract(address.Hex(), name)
1158-
1159-
// if _, ok := m.ContractStore.GetABI(name); !ok {
1160-
// m.ContractStore.AddABI(name, abi)
1161-
// }
1162-
1163-
// // retry is needed both for gas bumping and for waiting for deployment to finish (sometimes there's no code at address the first time we check)
1164-
// if err := retry.Do(
1165-
// func() error {
1166-
// ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration())
1167-
// _, err := bind.WaitDeployed(ctx, m.Client, tx)
1168-
// cancel()
1169-
1170-
// // let's make sure that deployment transaction was successful, before retrying
1171-
// if err != nil && !errors.Is(err, context.DeadlineExceeded) {
1172-
// ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration())
1173-
// receipt, mineErr := bind.WaitMined(ctx, m.Client, tx)
1174-
// if mineErr != nil {
1175-
// cancel()
1176-
// return mineErr
1177-
// }
1178-
// cancel()
1179-
1180-
// if receipt.Status == 0 {
1181-
// return errors.New("deployment transaction was reverted")
1182-
// }
1183-
// }
1184-
1185-
// return err
1186-
// }, retry.OnRetry(func(i uint, retryErr error) {
1187-
// switch {
1188-
// case errors.Is(retryErr, context.DeadlineExceeded):
1189-
// replacementTx, replacementErr := prepareReplacementTransaction(m, tx)
1190-
// if replacementErr != nil {
1191-
// L.Debug().Str("Current error", retryErr.Error()).Str("Replacement error", replacementErr.Error()).Uint("Attempt", i+1).Msg("Failed to prepare replacement transaction for contract deployment. Retrying with the original one")
1192-
// return
1193-
// }
1194-
// tx = replacementTx
1195-
// default:
1196-
// // do nothing, just wait again until it's mined
1197-
// }
1198-
// L.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i+1).Msg("Waiting for contract to be deployed")
1199-
// }),
1200-
// retry.DelayType(retry.FixedDelay),
1201-
// // if gas bump retries are set to 0, we still want to retry 10 times, because what we will be retrying will be other errors (no code at address, etc.)
1202-
// // downside is that if retries are enabled and their number is low other retry errors will be retried only that number of times
1203-
// // (we could have custom logic for different retry count per error, but that seemed like an overkill, so it wasn't implemented)
1204-
// retry.Attempts(func() uint {
1205-
// if m.Cfg.GasBumpRetries() != 0 {
1206-
// return m.Cfg.GasBumpRetries()
1207-
// }
1208-
// return 10
1209-
// }()),
1210-
// retry.RetryIf(func(err error) bool {
1211-
// return strings.Contains(strings.ToLower(err.Error()), "no contract code at given address") ||
1212-
// strings.Contains(strings.ToLower(err.Error()), "no contract code after deployment") ||
1213-
// (m.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded))
1214-
// }),
1215-
// ); err != nil {
1216-
// // pass this specific error, so that Decode knows that it's not the actual revert reason
1217-
// _, _ = m.Decode(tx, errors.New(ErrContractDeploymentFailed))
1218-
1219-
// return DeploymentData{}, wrapErrInMessageWithASuggestion(m.rewriteDeploymentError(err))
1220-
// }
1221-
1222-
// L.Info().
1223-
// Str("Address", address.Hex()).
1224-
// Str("TXHash", tx.Hash().Hex()).
1225-
// Msgf("Deployed %s contract", name)
1226-
1227-
// if !m.Cfg.ShouldSaveDeployedContractMap() {
1228-
// return DeploymentData{Address: address, Transaction: tx, BoundContract: contract}, nil
1229-
// }
1230-
1231-
// if err := SaveDeployedContract(m.Cfg.ContractMapFile, name, address.Hex()); err != nil {
1232-
// L.Warn().
1233-
// Err(err).
1234-
// Msg("Failed to save deployed contract address to file")
1235-
// }
1236-
1237-
// return DeploymentData{Address: address, Transaction: tx, BoundContract: contract}, nil
12381070
}
12391071

12401072
// rewriteDeploymentError makes some known errors more human friendly

seth/contracts.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package seth
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
"github.com/avast/retry-go"
8+
"github.com/ethereum/go-ethereum/accounts/abi"
9+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
10+
"github.com/ethereum/go-ethereum/core/types"
11+
"github.com/pkg/errors"
12+
)
13+
14+
type PreDeployHook func(auth *bind.TransactOpts, name string, abi abi.ABI, bytecode []byte, params ...interface{}) error
15+
type PostDeployHook func(client *Client, tx *types.Transaction) error
16+
17+
type ContractDeploymentHooks struct {
18+
Pre PreDeployHook
19+
Post PostDeployHook
20+
}
21+
22+
var RetryingPostDeployHook = func(client *Client, tx *types.Transaction) error {
23+
err := retry.Do(
24+
func() error {
25+
ctx, cancel := context.WithTimeout(context.Background(), client.Cfg.Network.TxnTimeout.Duration())
26+
_, err := bind.WaitDeployed(ctx, client.Client, tx)
27+
cancel()
28+
29+
// let's make sure that deployment transaction was successful, before retrying
30+
if err != nil && !errors.Is(err, context.DeadlineExceeded) {
31+
ctx, cancel := context.WithTimeout(context.Background(), client.Cfg.Network.TxnTimeout.Duration())
32+
receipt, mineErr := bind.WaitMined(ctx, client.Client, tx)
33+
if mineErr != nil {
34+
cancel()
35+
return mineErr
36+
}
37+
cancel()
38+
39+
if receipt.Status == 0 {
40+
return errors.New("deployment transaction was reverted")
41+
}
42+
}
43+
44+
return err
45+
}, retry.OnRetry(func(i uint, retryErr error) {
46+
switch {
47+
case errors.Is(retryErr, context.DeadlineExceeded):
48+
replacementTx, replacementErr := prepareReplacementTransaction(client, tx)
49+
if replacementErr != nil {
50+
L.Debug().Str("Current error", retryErr.Error()).Str("Replacement error", replacementErr.Error()).Uint("Attempt", i+1).Msg("Failed to prepare replacement transaction for contract deployment. Retrying with the original one")
51+
return
52+
}
53+
tx = replacementTx
54+
default:
55+
// do nothing, just wait again until it's mined
56+
}
57+
L.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i+1).Msg("Waiting for contract to be deployed")
58+
}),
59+
retry.DelayType(retry.FixedDelay),
60+
// if gas bump retries are set to 0, we still want to retry 10 times, because what we will be retrying will be other errors (no code at address, etc.)
61+
// downside is that if retries are enabled and their number is low other retry errors will be retried only that number of times
62+
// (we could have custom logic for different retry count per error, but that seemed like an overkill, so it wasn't implemented)
63+
retry.Attempts(func() uint {
64+
if client.Cfg.GasBumpRetries() != 0 {
65+
return client.Cfg.GasBumpRetries()
66+
}
67+
return 10
68+
}()),
69+
retry.RetryIf(func(err error) bool {
70+
return strings.Contains(strings.ToLower(err.Error()), "no contract code at given address") ||
71+
strings.Contains(strings.ToLower(err.Error()), "no contract code after deployment") ||
72+
(client.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded))
73+
}),
74+
)
75+
76+
if err != nil {
77+
// pass this specific error, so that Decode knows that it's not the actual revert reason
78+
_, _ = client.Decode(tx, errors.New(ErrContractDeploymentFailed))
79+
80+
return wrapErrInMessageWithASuggestion(client.rewriteDeploymentError(err))
81+
}
82+
83+
return nil
84+
}

0 commit comments

Comments
 (0)