Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(relayer): Out of gas #13778

Merged
merged 3 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/protocol/contracts/bridge/BridgeErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ abstract contract BridgeErrors {
error B_NULL_APP_ADDR();
error B_OWNER_IS_NULL();
error B_SIGNAL_NOT_RECEIVED();
error B_STATUS_MISMTACH();
error B_STATUS_MISMATCH();
error B_WRONG_CHAIN_ID();
error B_WRONG_TO_ADDRESS();
error B_ZERO_SIGNAL();
Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/bridge/libs/LibBridgeProcess.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ library LibBridgeProcess {

error B_FORBIDDEN();
error B_SIGNAL_NOT_RECEIVED();
error B_STATUS_MISMTACH();
error B_STATUS_MISMATCH();
error B_WRONG_CHAIN_ID();

/**
Expand Down Expand Up @@ -62,7 +62,7 @@ library LibBridgeProcess {
// LibBridgeRetry.sol.
bytes32 msgHash = message.hashMessage();
if (LibBridgeStatus.getMessageStatus(msgHash) != LibBridgeStatus.MessageStatus.NEW) {
revert B_STATUS_MISMTACH();
revert B_STATUS_MISMATCH();
}
// Message must have been "received" on the destChain (current chain)
address srcBridge = resolver.resolve(message.srcChainId, "bridge", false);
Expand Down
2 changes: 2 additions & 0 deletions packages/relayer/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.netrc
.env
.l1l2.env
.l2l3.env
.test.env
main
coverage.txt
Expand Down
2 changes: 1 addition & 1 deletion packages/relayer/contracts/bridge/Bridge.go

Large diffs are not rendered by default.

71 changes: 55 additions & 16 deletions packages/relayer/message/process_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,47 @@ func (p *Processor) sendProcessMessageCall(
return nil, errors.New("p.getLatestNonce")
}

gas, cost, err := p.estimateGas(ctx, event.Message, proof)
if err != nil || gas == 0 {
if err := p.hardcodeGasLimit(ctx, auth, event); err != nil {
return nil, errors.Wrap(err, "p.hardcodeGasLimit")
eventType, canonicalToken, _, err := relayer.DecodeMessageSentData(event)
if err != nil {
return nil, errors.Wrap(err, "relayer.DecodeMessageSentData")
}

var gas uint64

var cost *big.Int

var needsContractDeployment bool = false
// node is unable to estimate gas correctly for contract deployments, we need to check if the token
// is deployed, and always hardcode in this case. we need to check this before calling
// estimategas, as the node will soemtimes return a gas estimate for a contract deployment, however,
// it is incorrect and the tx will revert.
if eventType == relayer.EventTypeSendERC20 && event.Message.DestChainId.Cmp(canonicalToken.ChainId) != 0 {
// determine whether the canonical token is bridged or not on this chain
bridgedAddress, err := p.destTokenVault.CanonicalToBridged(nil, canonicalToken.ChainId, canonicalToken.Addr)
if err != nil {
return nil, errors.Wrap(err, "p.destTokenVault.IsBridgedToken")
}

if bridgedAddress == relayer.ZeroAddress {
// needs large gas limit because it has to deploy an ERC20 contract on destination
// chain. deploying ERC20 can be 2 mil by itself. we want to skip estimating gas entirely
// in this scenario.
needsContractDeployment = true
}
}

if bool(p.profitableOnly) {
profitable, err := p.isProfitable(ctx, event.Message, cost)
if err != nil || !profitable {
return nil, relayer.ErrUnprofitable
if needsContractDeployment {
auth.GasLimit = 3000000
} else {
// otherwise we can estimate gas
gas, cost, err = p.estimateGas(ctx, event.Message, proof)
// and if gas estimation failed, we just try to hardcore a value no matter what type of event,
// or whether the contract is deployed.
if err != nil || gas == 0 {
cost, err = p.hardcodeGasLimit(ctx, auth, event, eventType, canonicalToken)
if err != nil {
return nil, errors.Wrap(err, "p.hardcodeGasLimit")
}
}
}

Expand All @@ -177,6 +207,13 @@ func (p *Processor) sendProcessMessageCall(

auth.GasPrice = gasPrice

if bool(p.profitableOnly) {
profitable, err := p.isProfitable(ctx, event.Message, cost)
if err != nil || !profitable {
return nil, relayer.ErrUnprofitable
}
}

// process the message on the destination bridge.
tx, err := p.destBridge.ProcessMessage(auth, event.Message, proof)
if err != nil {
Expand All @@ -198,20 +235,17 @@ func (p *Processor) hardcodeGasLimit(
ctx context.Context,
auth *bind.TransactOpts,
event *bridge.BridgeMessageSent,
) error {
eventType, canonicalToken, _, err := relayer.DecodeMessageSentData(event)
if err != nil {
return errors.Wrap(err, "relayer.DecodeMessageSentData")
}

eventType relayer.EventType,
canonicalToken *relayer.CanonicalToken,
) (*big.Int, error) {
if eventType == relayer.EventTypeSendETH {
// eth bridges take much less gas, from 250k to 450k.
auth.GasLimit = 500000
} else {
// determine whether the canonical token is bridged or not on this chain
bridgedAddress, err := p.destTokenVault.CanonicalToBridged(nil, canonicalToken.ChainId, canonicalToken.Addr)
if err != nil {
return errors.Wrap(err, "p.destTokenVault.IsBridgedToken")
return nil, errors.Wrap(err, "p.destTokenVault.IsBridgedToken")
}

if bridgedAddress == relayer.ZeroAddress {
Expand All @@ -225,7 +259,12 @@ func (p *Processor) hardcodeGasLimit(
}
}

return nil
gasPrice, err := p.destEthClient.SuggestGasPrice(ctx)
if err != nil {
return nil, errors.Wrap(err, "p.destEthClient.SuggestGasPrice")
}

return new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(auth.GasLimit)), nil
}

func (p *Processor) setLatestNonce(nonce uint64) {
Expand Down
10 changes: 5 additions & 5 deletions packages/relayer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func WaitConfirmations(ctx context.Context, confirmer confirmer, confirmations u
}
}

func DecodeMessageSentData(event *bridge.BridgeMessageSent) (EventType, CanonicalToken, *big.Int, error) {
func DecodeMessageSentData(event *bridge.BridgeMessageSent) (EventType, *CanonicalToken, *big.Int, error) {
eventType := EventTypeSendETH

var canonicalToken CanonicalToken
Expand All @@ -130,18 +130,18 @@ func DecodeMessageSentData(event *bridge.BridgeMessageSent) (EventType, Canonica

tokenVaultABI, err := tokenVaultMD.GetAbi()
if err != nil {
return eventType, CanonicalToken{}, big.NewInt(0), errors.Wrap(err, "tokenVaultMD.GetAbi()")
return eventType, nil, big.NewInt(0), errors.Wrap(err, "tokenVaultMD.GetAbi()")
}

method, err := tokenVaultABI.MethodById(event.Message.Data[:4])
if err != nil {
return eventType, CanonicalToken{}, big.NewInt(0), errors.Wrap(err, "tokenVaultABI.MethodById")
return eventType, nil, big.NewInt(0), errors.Wrap(err, "tokenVaultABI.MethodById")
}

inputsMap := make(map[string]interface{})

if err := method.Inputs.UnpackIntoMap(inputsMap, event.Message.Data[4:]); err != nil {
return eventType, CanonicalToken{}, big.NewInt(0), errors.Wrap(err, "method.Inputs.UnpackIntoMap")
return eventType, nil, big.NewInt(0), errors.Wrap(err, "method.Inputs.UnpackIntoMap")
}

if method.Name == "receiveERC20" {
Expand All @@ -162,7 +162,7 @@ func DecodeMessageSentData(event *bridge.BridgeMessageSent) (EventType, Canonica
amount = event.Message.DepositValue
}

return eventType, canonicalToken, amount, nil
return eventType, &canonicalToken, amount, nil
}

type CanonicalToken struct {
Expand Down
6 changes: 3 additions & 3 deletions packages/relayer/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func Test_DecodeMessageSentData(t *testing.T) {
name string
event *bridge.BridgeMessageSent
wantEventType EventType
wantCanonicalToken CanonicalToken
wantCanonicalToken *CanonicalToken
wantAmount *big.Int
wantError error
}{
Expand All @@ -164,7 +164,7 @@ func Test_DecodeMessageSentData(t *testing.T) {
},
},
EventTypeSendERC20,
CanonicalToken{
&CanonicalToken{
ChainId: big.NewInt(31336),
Addr: common.HexToAddress("0xe4337137828c93D0046212ebDa8a82a24356b67B"),
Decimals: uint8(18),
Expand All @@ -184,7 +184,7 @@ func Test_DecodeMessageSentData(t *testing.T) {
},
},
EventTypeSendETH,
CanonicalToken{},
&CanonicalToken{},
big.NewInt(1),
nil,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ error B_OWNER_IS_NULL()
error B_SIGNAL_NOT_RECEIVED()
```

### B_STATUS_MISMTACH
### B_STATUS_MISMATCH

```solidity
error B_STATUS_MISMTACH()
error B_STATUS_MISMATCH()
```

### B_WRONG_CHAIN_ID
Expand Down