Conversation
…58_2 [CHORE] sync with master v1.58.2
…and indexer v1.16.91
…after_v1_58_3 Feat/sync dev with master after v1.58.3
… indexer v1.17.0-beta
…1_17_0_upgrade [CP-651] update protos for v1.17.0 upgrade
…71. Includes support for the new Chainlink oracle messages.
fix: update copyright year in README.md to 2026
…81. Includes support for the new Chainlink oracle messages.
…treams_oracle [C-655] add chainlink data streams oracle
…e_v1_18_0_beta2 [CHORE] Update Injective Core to v1.18.0-beta2
Release v1.60.0
… release/v1_60_0_to_master
… release/v1_60_0_to_master
📝 WalkthroughWalkthroughThis PR introduces Chainlink Data Streams oracle support, adds Failure event bindings to EVM precompiles, removes Band oracle-related functionality, refactors log decoding from event-based to protobuf, and implements comprehensive derivative/spot order matching and VWAP tracking subsystems for v2 exchanges with fee discount logic and expanded validator configurations. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR updates the SDK to support Injective chain v1.18.0 and Indexer v1.18.3, introducing significant changes including Band oracle deprecation, Chainlink Data Streams support, and various API enhancements.
Changes:
- Deprecated Band oracle support with backward compatibility for historical data
- Added Chainlink Data Streams oracle integration
- Split permissions contract hooks into separate wasm_hook and evm_hook fields
- Enhanced exchange module with post-only mode activation, synthetic trades validation, and force-paused market status
- Updated peggy module with improved validator power calculations and checkpoint key handling
Reviewed changes
Copilot reviewed 52 out of 102 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| proto/injective/permissions/v1beta1/* | Split contract_hook into wasm_hook and evm_hook for better contract type separation |
| proto/injective/oracle/v1beta1/* | Deprecated Band oracle, added Chainlink Data Streams support |
| proto/injective/peggy/v1/* | Added interface implementations for EthereumSigned and EthereumClaim |
| proto/injective/exchange/v2/* | Added post-only mode messages, synthetic execution type, force-paused status |
| proto/injective/evm/v1/tx.proto | Added execution_gas_used field and interface annotations |
| chain/oracle/types/* | Implemented Band oracle deprecation with error handling |
| chain/peggy/types/* | Changed PowerDiff to use LegacyDec, updated GetFakeCheckpointKey signature |
| chain/exchange/types/* | Enhanced validation, added fee discount infrastructure, improved market interfaces |
| client/exchange/exchange.go | Updated GetOracleList with additional filter parameters |
| go.mod/go.sum | Updated cometbft and block-sdk dependencies, removed bandchain-packet |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if t.Margin.IsNil() || t.Margin.IsNegative() { | ||
| return ErrInvalidMargin |
There was a problem hiding this comment.
The validation logic has changed from allowing negative margins to rejecting them. This is a breaking change that could reject previously valid trades. The old comment stated "Margin can be 0 or even negative!" but the new code rejects negative margins. Verify this is intentional and that all callers are aware of this stricter validation.
| GetCumulativePrice( | ||
| ctx sdk.Context, | ||
| oracleType oracletypes.OracleType, | ||
| base, quote string, | ||
| ) (baseCumulative, quoteCumulative *sdkmath.LegacyDec) |
There was a problem hiding this comment.
The GetCumulativePrice interface has changed to return two values (baseCumulative and quoteCumulative) instead of one. This is a breaking API change. Ensure all implementations and callers have been updated accordingly throughout the codebase.
| func GetFakeCheckpointKey(checkpoint, addr []byte) []byte { | ||
| k := make([]byte, 0, len(FakeCheckpointKey)+len(checkpoint)+len(addr)) | ||
| k = append(k, FakeCheckpointKey...) | ||
| k = append(k, checkpoint...) | ||
| k = append(k, addr...) | ||
|
|
||
| return k | ||
| } |
There was a problem hiding this comment.
The GetFakeCheckpointKey function signature has changed from taking only a signature parameter to taking checkpoint and addr parameters. This breaks existing callers. Verify all call sites have been updated to pass both parameters correctly.
| @@ -78,7 +80,7 @@ func (b BridgeValidators) Sort() { | |||
| // if the total on chain voting power increases by 1% due to inflation, we shouldn't have to generate a new validator | |||
| // set, after all the validators retained their relative percentages during inflation and normalized Gravity bridge power | |||
| // shows no difference. | |||
| func (b BridgeValidators) PowerDiff(c BridgeValidators) float64 { | |||
| func (b BridgeValidators) PowerDiff(c BridgeValidators) sdkmath.LegacyDec { | |||
There was a problem hiding this comment.
The PowerDiff function return type has changed from float64 to sdkmath.LegacyDec. This is a breaking API change that affects how power differences are calculated and compared. Ensure all callers handle the new decimal type correctly, especially in comparison operations.
| @@ -49,7 +49,7 @@ type ExchangeClient interface { | |||
| GetDerivativeFundingRates(ctx context.Context, req *derivativeExchangePB.FundingRatesRequest) (*derivativeExchangePB.FundingRatesResponse, error) | |||
| GetPrice(ctx context.Context, baseSymbol string, quoteSymbol string, oracleType string, oracleScaleFactor uint32) (*oraclePB.PriceResponse, error) | |||
| FetchPriceV2(ctx context.Context, filters []*oraclePB.PricePayloadV2) (*oraclePB.PriceV2Response, error) | |||
| GetOracleList(ctx context.Context) (*oraclePB.OracleListResponse, error) | |||
| GetOracleList(ctx context.Context, symbol, oracleType, token string, perPage int32) (*oraclePB.OracleListResponse, error) | |||
There was a problem hiding this comment.
GetOracleList API signature has changed to require additional parameters (symbol, oracleType, token, perPage). While the example was updated, this is a breaking change for all existing callers. Verify all code using this function has been updated to pass the new parameters.
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 Fix all issues with AI agents
In `@chain/evm/types/utils.go`:
- Around line 93-96: The current bounds check only guards the upper bound so a
negative msgIndex will cause a panic when indexing txResponses; update the
validation in the function that accesses txResponses (the block returning
logsFromTxResponse(nil, txResponses[msgIndex], blockNumber)) to check both lower
and upper bounds (e.g., if msgIndex < 0 || msgIndex >= len(txResponses) return
an error) and return a clear error message referencing the invalid msgIndex and
the txResponses slice instead of indexing into it when out of range.
In `@chain/exchange/types/expected_keepers.go`:
- Around line 108-111: The PermissionsKeeper interface is inconsistent: change
SendRestrictionFn to use sdk.Context (not context.Context) and to accept
sdk.Coins (not sdk.Coin) so both methods use sdk.Context and multi-denom
transfers are supported; update the interface signature for SendRestrictionFn,
then update all implementations and call sites of
PermissionsKeeper.SendRestrictionFn as well as any references to its
parameter/return types to match the new sdk.Context and sdk.Coins types (ensure
imports reflect sdk.Coins usage).
In `@chain/exchange/types/v2/codec.go`:
- Around line 122-123: RegisterInterfaces is missing DenomMinNotionalProposal in
the govtypes.Content implementations list, so Any unpacking will fail; update
the registry.RegisterImplementations call in RegisterInterfaces to include
&DenomMinNotionalProposal{} alongside the other proposal types (the same
concrete type already registered in cdc.RegisterConcrete), ensuring
DenomMinNotionalProposal is registered as an implementation of govtypes.Content
for proper InterfaceRegistry/Any unpacking.
In `@chain/exchange/types/v2/derivative.go`:
- Around line 878-882: ComputeSyntheticVwapUnitDelta can panic or divide-by-zero
when p.PerpetualVwapInfo[marketID] is missing, when vwapInfo.MarkPrice is nil,
or when MarkPrice equals zero; guard inside
DerivativeVwapInfo.ComputeSyntheticVwapUnitDelta by checking that vwapInfo
exists, that vwapInfo.MarkPrice is non-nil, and that not
vwapInfo.MarkPrice.IsZero() before doing Price.Sub(...).Quo(...); if any check
fails, return math.LegacyDec{} (zero) or an explicit zero value to keep callers
safe and avoid panics.
- Around line 812-839: The ApplyVwap method can dereference nil when marketType
is unsupported or when vwapData is nil; update ApplyVwap to early-return for
unsupported market types (handle the default case by returning) and guard
against nil vwapData and nil vwapInfo before using them: after selecting
vwapInfo in the switch over types.MarketType (inside ApplyVwap) check if
vwapInfo == nil (and create it only for known market types) and if vwapData ==
nil or vwapData.Quantity.IsZero() then return; ensure any use of
vwapInfo.VwapData (and calling ApplyExecution) is only done when vwapInfo and
vwapData are non-nil.
In `@chain/exchange/types/v2/fee_discounts.go`:
- Around line 102-162: GetFeeDiscountRate and IncrementAccountVolumeContribution
assume FeeDiscountStakingInfo and its maps exist and return pointers into shared
tier structs, risking nil derefs and data races; update FeeDiscountConfig
methods to first nil-check c.FeeDiscountStakingInfo (or bail/return nil) before
accessing AccountFeeTiers/AccountFeeTiersMux, ensure AccountFeeTiersMux
RLock/RUnlock surround all accesses, and instead of returning
&tier.MakerDiscountRate or &tier.TakerDiscountRate return a copied
math.LegacyDec value (or nil) to avoid exposing internal mutable state; also
apply similar defensive nil checks when touching c.AccountVolumeContributions
and c.SubaccountMarketVolumeContributions in IncrementAccountVolumeContribution.
In `@chain/exchange/types/v2/market.go`:
- Around line 375-394: GetMarketBalanceDelta currently clamps negative payout to
zero but then adds tradeFee when isReduceOnly, which can be negative and
reintroduce a negative payout; after the reduce-only adjustment (inside
GetMarketBalanceDelta) re-clamp payout to zero (or explicitly handle negative
tradeFee) so payout cannot become negative again before computing
collateralizationMargin.Sub(payout); refer to the GetMarketBalanceDelta function
and variables payout, tradeFee, isReduceOnly, collateralizationMargin when
applying the fix.
In `@chain/exchange/types/wasm_trade_summary.go`:
- Around line 68-96: The validateTrades method on SyntheticTradeAction currently
dereferences entries of UserTrades and ContractTrades and can panic if any entry
is nil; add nil checks at the start of the loop in validateTrades to verify
userTrade and contractTrade are non-nil and return errors.Wrapf(ErrInvalidTrade,
"nil user trade at index %d") or errors.Wrapf(ErrInvalidTrade, "nil contract
trade at index %d") as appropriate (referencing SyntheticTradeAction,
UserTrades, ContractTrades, userTrade, contractTrade) before accessing their
fields so the function fails gracefully instead of panicking.
In `@chain/exchange/types/wasm_trades.go`:
- Around line 77-79: IsReduceOnly() calls t.Margin.IsZero() without a nil guard;
add a defensive check like Validate() so a nil Margin doesn't panic—use the
existing IsZeroOrNilDec helper (or check t.Margin == nil) inside
SyntheticTrade.IsReduceOnly to return true when Margin is nil or zero, otherwise
return the existing IsZero result; reference SyntheticTrade, IsReduceOnly,
Margin, IsZero, Validate and IsZeroOrNilDec when making the change.
In `@chain/oracle/types/codec.go`:
- Around line 33-38: The MsgRelayChainlinkPrices type is not registered,
breaking Any unpacking and Amino/JSON serialization; add its registrations by
updating RegisterLegacyAminoCodec to call
cdc.RegisterConcrete(&MsgRelayChainlinkPrices{},
"oracle/MsgRelayChainlinkPrices", nil) immediately after the existing
MsgRelayStorkPrices registration, and update RegisterInterfaces to register the
concrete type and its sdk.Msg interface mapping (i.e., register
MsgRelayChainlinkPrices as implementing sdk.Msg) immediately after the
MsgRelayStorkPrices entries so both the legacy codec and the interface registry
include MsgRelayChainlinkPrices.
In `@chain/oracle/types/msgs.go`:
- Around line 316-343: MsgRelayChainlinkPrices.ValidateBasic currently only
checks Sender and allows an empty Reports slice, enabling a no-op tx; update
ValidateBasic in MsgRelayChainlinkPrices to reject empty payloads by returning
an appropriate sdk error when len(msg.Reports) == 0 (use a standard sdk error
like sdkerrors.ErrInvalidRequest or wrap with fmt.Errorf to include context), so
the function validates both Sender (as it already does) and non-empty Reports
before returning nil.
In `@chain/oracle/types/params.go`:
- Around line 55-59: ParamSetPairs currently only registers PythContract so the
new fields defined on Params (ChainlinkVerifierProxyContract,
AcceptUnverifiedChainlinkDataStreamsReports,
ChainlinkDataStreamsVerificationGasLimit) won't be persisted or updateable;
update the ParamSetPairs() method to register all four fields (PythContract,
ChainlinkVerifierProxyContract, AcceptUnverifiedChainlinkDataStreamsReports,
ChainlinkDataStreamsVerificationGasLimit) using their param keys and attach
validators: string validators for the two contract addresses (non-empty or allow
empty if intended), a boolean validator for
AcceptUnverifiedChainlinkDataStreamsReports, and a uint/uint64 validator (e.g.,
non-negative) for ChainlinkDataStreamsVerificationGasLimit; ensure the
ParamKeyTable/param key constants referenced by ParamSetPairs match the keys
used elsewhere (and align with DefaultParams()).
In `@chain/oracle/types/proposal.go`:
- Around line 203-259: GrantStorkPublisherPrivilegeProposal.ProposalType and
RevokeStorkPublisherPrivilegeProposal.ProposalType incorrectly return
ProposalTypeGrantBandOraclePrivilege and both ValidateBasic implementations omit
govtypes.ValidateAbstract; update
GrantStorkPublisherPrivilegeProposal.ProposalType to return
ProposalTypeGrantStorkPublisherPrivilege and
RevokeStorkPublisherPrivilegeProposal.ProposalType to return
ProposalTypeRevokeStorkPublisherPrivilege, and restore abstract validation by
calling govtypes.ValidateAbstract(p) (or returning its error) at the start of
each ValidateBasic in addition to the existing publisher address checks (refer
to methods GetTitle/GetDescription/ProposalRoute/ProposalType/ValidateBasic on
GrantStorkPublisherPrivilegeProposal and RevokeStorkPublisherPrivilegeProposal).
🧹 Nitpick comments (2)
chain/exchange/types/v2/spot.go (2)
13-35: Avoid silent fallback for unknownOrderFillType.Defaulting to
TransientSellOrderbookFillscan mask invalid input; returningnil(or explicitly panicking) makes failures visible.🔧 Suggested adjustment
case TransientLimitSell: return r.TransientSellOrderbookFills } - return r.TransientSellOrderbookFills + return nil
222-252: Consider guarding against nil VWAP inputs.If callers ever pass a
nilnewVwapData, this will panic. A small guard keeps this helper defensive.🔧 Suggested adjustment
func (p *SpotVwapInfo) ApplyVwap(marketID common.Hash, newVwapData *SpotVwapData) { + if newVwapData == nil { + return + } var existingVwapData *SpotVwapData
| type PermissionsKeeper interface { | ||
| IsEnforcedRestrictionsDenom(ctx sdk.Context, denom string) bool | ||
| SendRestrictionFn(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amount sdk.Coin) (newToAddr sdk.AccAddress, err error) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "PermissionsKeeper" -A 3 -B 3Repository: InjectiveLabs/sdk-go
Length of output: 665
🏁 Script executed:
rg -n "SendRestrictionFn" -A 3 -B 3Repository: InjectiveLabs/sdk-go
Length of output: 519
🏁 Script executed:
rg -n "IsEnforcedRestrictionsDenom" -A 3 -B 3Repository: InjectiveLabs/sdk-go
Length of output: 566
🏁 Script executed:
rg -n "SendRestrictions\|SendRestriction" -A 2 -B 2Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
rg -n "PermissionsKeeper" --type goRepository: InjectiveLabs/sdk-go
Length of output: 143
🏁 Script executed:
rg -n "restriction" -i --type goRepository: InjectiveLabs/sdk-go
Length of output: 8041
🏁 Script executed:
fd "expected_keepers.go" --type fRepository: InjectiveLabs/sdk-go
Length of output: 104
🏁 Script executed:
# Check what module this is in and look for related code
cat chain/exchange/types/expected_keepers.go | head -50Repository: InjectiveLabs/sdk-go
Length of output: 1933
🏁 Script executed:
rg -n "PermissionsKeeper" --type go -B 5 -A 5Repository: InjectiveLabs/sdk-go
Length of output: 876
🏁 Script executed:
# Check exchange module keeper to see how keepers are used
fd "keeper.go" chain/exchange --type fRepository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Look at exchange module structure
ls -la chain/exchange/Repository: InjectiveLabs/sdk-go
Length of output: 229
🏁 Script executed:
# Search for exchange keeper implementations
fd "keeper" chain/exchange --type fRepository: InjectiveLabs/sdk-go
Length of output: 104
🏁 Script executed:
# Look for imports of expected_keepers
rg "expected_keepers" --type goRepository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check if permissions module has keeper that might implement this
ls -la chain/permissions/keeper/ 2>/dev/null || echo "No keeper dir"Repository: InjectiveLabs/sdk-go
Length of output: 77
🏁 Script executed:
# Search for any actual keeper.go or module.go in exchange
find chain/exchange -name "*.go" -type f | head -20Repository: InjectiveLabs/sdk-go
Length of output: 781
🏁 Script executed:
# Final check: look at the full expected_keepers file to understand context
cat chain/exchange/types/expected_keepers.goRepository: InjectiveLabs/sdk-go
Length of output: 4857
Standardize context types and use sdk.Coins for multi-denom support in PermissionsKeeper.
The interface has an inconsistent context signature—IsEnforcedRestrictionsDenom uses sdk.Context while SendRestrictionFn uses context.Context. Since SendRestrictionFn appears to be a stateful operation (redirecting recipient addresses), both methods should use sdk.Context for consistency with other state-modifying keepers in this file. Additionally, SendRestrictionFn accepts only a single sdk.Coin instead of sdk.Coins, which will prevent proper multi-denom transfer handling if the interface is used for send-restriction hooks.
🤖 Prompt for AI Agents
In `@chain/exchange/types/expected_keepers.go` around lines 108 - 111, The
PermissionsKeeper interface is inconsistent: change SendRestrictionFn to use
sdk.Context (not context.Context) and to accept sdk.Coins (not sdk.Coin) so both
methods use sdk.Context and multi-denom transfers are supported; update the
interface signature for SendRestrictionFn, then update all implementations and
call sites of PermissionsKeeper.SendRestrictionFn as well as any references to
its parameter/return types to match the new sdk.Context and sdk.Coins types
(ensure imports reflect sdk.Coins usage).
| cdc.RegisterConcrete(&MsgReclaimLockedFunds{}, "exchange/v2/MsgReclaimLockedFunds", nil) | ||
| cdc.RegisterConcrete(&DenomMinNotionalProposal{}, "exchange/v2/DenomMinNotionalProposal", nil) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate the proposal definition and all registrations
rg -n "DenomMinNotionalProposal" -g '*.go'Repository: InjectiveLabs/sdk-go
Length of output: 8272
🏁 Script executed:
#!/bin/bash
# Inspect the codec.go file for RegisterInterfaces and codec registrations
head -200 chain/exchange/types/v2/codec.go | tail -100Repository: InjectiveLabs/sdk-go
Length of output: 5353
🏁 Script executed:
#!/bin/bash
# Search for RegisterInterfaces function specifically
rg -n "RegisterInterfaces" -A 20 chain/exchange/types/v2/codec.goRepository: InjectiveLabs/sdk-go
Length of output: 868
🏁 Script executed:
#!/bin/bash
# Search for govtypes.Content registrations in the v2 codec file
rg -n "govtypes\.Content" chain/exchange/types/v2/codec.go -B 2 -A 10Repository: InjectiveLabs/sdk-go
Length of output: 569
🏁 Script executed:
#!/bin/bash
# Get the full RegisterInterfaces function from v2/codec.go
sed -n '149,250p' chain/exchange/types/v2/codec.goRepository: InjectiveLabs/sdk-go
Length of output: 3256
Add DenomMinNotionalProposal to the govtypes.Content registry in RegisterInterfaces.
The type implements govtypes.Content (confirmed in chain/exchange/types/proposal.go:1619) and is registered in Legacy Amino, but is missing from the InterfaceRegistry. When packed into Any, it will fail to unpack without the registry entry.
Current govtypes.Content registrations (lines 205-222)
registry.RegisterImplementations(
(*govtypes.Content)(nil),
&ExchangeEnableProposal{},
&BatchExchangeModificationProposal{},
&SpotMarketParamUpdateProposal{},
&SpotMarketLaunchProposal{},
&PerpetualMarketLaunchProposal{},
&ExpiryFuturesMarketLaunchProposal{},
&DerivativeMarketParamUpdateProposal{},
&MarketForcedSettlementProposal{},
&UpdateAuctionExchangeTransferDenomDecimalsProposal{},
&TradingRewardCampaignLaunchProposal{},
&TradingRewardCampaignUpdateProposal{},
&TradingRewardPendingPointsUpdateProposal{},
&FeeDiscountProposal{},
&BatchCommunityPoolSpendProposal{},
&BinaryOptionsMarketParamUpdateProposal{},
&BinaryOptionsMarketLaunchProposal{},
&AtomicMarketOrderFeeMultiplierScheduleProposal{},
)
🤖 Prompt for AI Agents
In `@chain/exchange/types/v2/codec.go` around lines 122 - 123, RegisterInterfaces
is missing DenomMinNotionalProposal in the govtypes.Content implementations
list, so Any unpacking will fail; update the registry.RegisterImplementations
call in RegisterInterfaces to include &DenomMinNotionalProposal{} alongside the
other proposal types (the same concrete type already registered in
cdc.RegisterConcrete), ensuring DenomMinNotionalProposal is registered as an
implementation of govtypes.Content for proper InterfaceRegistry/Any unpacking.
| func (p *DerivativeVwapInfo) ApplyVwap(marketID common.Hash, markPrice *math.LegacyDec, vwapData *VwapData, marketType types.MarketType) { | ||
| var vwapInfo *VwapInfo | ||
|
|
||
| switch marketType { | ||
| case types.MarketType_Perpetual: | ||
| vwapInfo = p.PerpetualVwapInfo[marketID] | ||
| if vwapInfo == nil { | ||
| vwapInfo = NewVwapInfo(markPrice) | ||
| p.PerpetualVwapInfo[marketID] = vwapInfo | ||
| } | ||
| case types.MarketType_Expiry: | ||
| vwapInfo = p.ExpiryVwapInfo[marketID] | ||
| if vwapInfo == nil { | ||
| vwapInfo = NewVwapInfo(markPrice) | ||
| p.ExpiryVwapInfo[marketID] = vwapInfo | ||
| } | ||
| case types.MarketType_BinaryOption: | ||
| vwapInfo = p.BinaryOptionsVwapInfo[marketID] | ||
| if vwapInfo == nil { | ||
| vwapInfo = NewVwapInfo(markPrice) | ||
| p.BinaryOptionsVwapInfo[marketID] = vwapInfo | ||
| } | ||
| default: | ||
| } | ||
|
|
||
| if !vwapData.Quantity.IsZero() { | ||
| vwapInfo.VwapData = vwapInfo.VwapData.ApplyExecution(vwapData.Price, vwapData.Quantity) | ||
| } |
There was a problem hiding this comment.
Guard ApplyVwap against unsupported market types / nil data.
If marketType falls through the default case, vwapInfo stays nil and the method panics. A short early return also protects against nil VWAP data.
🛠️ Proposed fix
switch marketType {
case types.MarketType_Perpetual:
// ...
case types.MarketType_Expiry:
// ...
case types.MarketType_BinaryOption:
// ...
default:
- }
+ return
+ }
+
+ if vwapData == nil {
+ return
+ }
if !vwapData.Quantity.IsZero() {
vwapInfo.VwapData = vwapInfo.VwapData.ApplyExecution(vwapData.Price, vwapData.Quantity)
}🤖 Prompt for AI Agents
In `@chain/exchange/types/v2/derivative.go` around lines 812 - 839, The ApplyVwap
method can dereference nil when marketType is unsupported or when vwapData is
nil; update ApplyVwap to early-return for unsupported market types (handle the
default case by returning) and guard against nil vwapData and nil vwapInfo
before using them: after selecting vwapInfo in the switch over types.MarketType
(inside ApplyVwap) check if vwapInfo == nil (and create it only for known market
types) and if vwapData == nil or vwapData.Quantity.IsZero() then return; ensure
any use of vwapInfo.VwapData (and calling ApplyExecution) is only done when
vwapInfo and vwapData are non-nil.
| // ComputeSyntheticVwapUnitDelta returns (price - markPrice) / markPrice | ||
| func (p *DerivativeVwapInfo) ComputeSyntheticVwapUnitDelta(marketID common.Hash) math.LegacyDec { | ||
| vwapInfo := p.PerpetualVwapInfo[marketID] | ||
| return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice) | ||
| } |
There was a problem hiding this comment.
Handle missing/zero mark price in ComputeSyntheticVwapUnitDelta.
A nil vwapInfo, nil MarkPrice, or zero mark price will panic or divide by zero. Consider a defensive guard (returning zero or an explicit error) to keep callers safe.
🛠️ Proposed fix
func (p *DerivativeVwapInfo) ComputeSyntheticVwapUnitDelta(marketID common.Hash) math.LegacyDec {
vwapInfo := p.PerpetualVwapInfo[marketID]
+ if vwapInfo == nil || vwapInfo.MarkPrice == nil || vwapInfo.MarkPrice.IsZero() {
+ return math.LegacyZeroDec()
+ }
return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // ComputeSyntheticVwapUnitDelta returns (price - markPrice) / markPrice | |
| func (p *DerivativeVwapInfo) ComputeSyntheticVwapUnitDelta(marketID common.Hash) math.LegacyDec { | |
| vwapInfo := p.PerpetualVwapInfo[marketID] | |
| return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice) | |
| } | |
| // ComputeSyntheticVwapUnitDelta returns (price - markPrice) / markPrice | |
| func (p *DerivativeVwapInfo) ComputeSyntheticVwapUnitDelta(marketID common.Hash) math.LegacyDec { | |
| vwapInfo := p.PerpetualVwapInfo[marketID] | |
| if vwapInfo == nil || vwapInfo.MarkPrice == nil || vwapInfo.MarkPrice.IsZero() { | |
| return math.LegacyZeroDec() | |
| } | |
| return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice) | |
| } |
🤖 Prompt for AI Agents
In `@chain/exchange/types/v2/derivative.go` around lines 878 - 882,
ComputeSyntheticVwapUnitDelta can panic or divide-by-zero when
p.PerpetualVwapInfo[marketID] is missing, when vwapInfo.MarkPrice is nil, or
when MarkPrice equals zero; guard inside
DerivativeVwapInfo.ComputeSyntheticVwapUnitDelta by checking that vwapInfo
exists, that vwapInfo.MarkPrice is non-nil, and that not
vwapInfo.MarkPrice.IsZero() before doing Price.Sub(...).Quo(...); if any check
fails, return math.LegacyDec{} (zero) or an explicit zero value to keep callers
safe and avoid panics.
| func (t *SyntheticTrade) IsReduceOnly() bool { | ||
| return t.Margin.IsZero() | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the SyntheticTrade struct definition
rg -n "type SyntheticTrade" chain/exchange/types/wasm_trades.go -A 20Repository: InjectiveLabs/sdk-go
Length of output: 1275
🏁 Script executed:
# Check the Margin field type and any initialization
rg -n "Margin" chain/exchange/types/wasm_trades.go -B 2 -A 2Repository: InjectiveLabs/sdk-go
Length of output: 411
🏁 Script executed:
# Find all IsReduceOnly() call sites across the codebase
rg -rn "IsReduceOnly\(" --type goRepository: InjectiveLabs/sdk-go
Length of output: 1421
🏁 Script executed:
# Check the Validate() method in the same file
rg -n "func.*Validate\(" chain/exchange/types/wasm_trades.go -A 10Repository: InjectiveLabs/sdk-go
Length of output: 375
🏁 Script executed:
# Look for the type of Margin and check if IsZero() is defined on it
rg -n "LegacyDec" chain/exchange/types/wasm_trades.goRepository: InjectiveLabs/sdk-go
Length of output: 269
🏁 Script executed:
# Better search for IsReduceOnly usage
rg -rn "IsReduceOnly" --type go chain/Repository: InjectiveLabs/sdk-go
Length of output: 2048
🏁 Script executed:
# Check if there are tests or usage examples
fd -e go -x grep -l "IsReduceOnly" {} \;Repository: InjectiveLabs/sdk-go
Length of output: 861
🏁 Script executed:
# Look for any wrapping or defensive patterns in wasm_trades.go
cat -n chain/exchange/types/wasm_trades.goRepository: InjectiveLabs/sdk-go
Length of output: 2431
🏁 Script executed:
# Search for LegacyDec definition and IsZero implementation in cosmos imports
rg -rn "func.*IsZero\(\)" --type go -A 3 | head -50Repository: InjectiveLabs/sdk-go
Length of output: 628
🏁 Script executed:
# Find all usages of IsZero in the codebase to see the defensive pattern
rg -rn "\.IsZero\(\)" --type go -B 2 | head -80Repository: InjectiveLabs/sdk-go
Length of output: 4127
🏁 Script executed:
# Look for cosmos-sdk LegacyDec documentation or implementation hints
rg -rn "IsNil\(\) \|\| .*IsZero\(\)" --type goRepository: InjectiveLabs/sdk-go
Length of output: 863
Add nil guard to IsReduceOnly() to match defensive pattern used in Validate(). The Margin field is checked for nil before other operations in Validate() (line 70), but IsReduceOnly() calls IsZero() without a nil check. This inconsistency is risky if the method is called before validation. The codebase uses a consistent defensive pattern—see volume_record.go and the IsZeroOrNilDec() helper in v2/proposal.go.
Suggested fix
func (t *SyntheticTrade) IsReduceOnly() bool {
- return t.Margin.IsZero()
+ if t.Margin.IsNil() {
+ return false
+ }
+ return t.Margin.IsZero()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func (t *SyntheticTrade) IsReduceOnly() bool { | |
| return t.Margin.IsZero() | |
| } | |
| func (t *SyntheticTrade) IsReduceOnly() bool { | |
| if t.Margin.IsNil() { | |
| return false | |
| } | |
| return t.Margin.IsZero() | |
| } |
🤖 Prompt for AI Agents
In `@chain/exchange/types/wasm_trades.go` around lines 77 - 79, IsReduceOnly()
calls t.Margin.IsZero() without a nil guard; add a defensive check like
Validate() so a nil Margin doesn't panic—use the existing IsZeroOrNilDec helper
(or check t.Margin == nil) inside SyntheticTrade.IsReduceOnly to return true
when Margin is nil or zero, otherwise return the existing IsZero result;
reference SyntheticTrade, IsReduceOnly, Margin, IsZero, Validate and
IsZeroOrNilDec when making the change.
| // Deprecated: Band oracle proposal types kept for backward compatibility | ||
| cdc.RegisterConcrete(&GrantBandOraclePrivilegeProposal{}, "oracle/GrantBandOraclePrivilegeProposal", nil) //nolint:staticcheck // deprecated | ||
| cdc.RegisterConcrete(&RevokeBandOraclePrivilegeProposal{}, "oracle/RevokeBandOraclePrivilegeProposal", nil) //nolint:staticcheck // deprecated | ||
| cdc.RegisterConcrete(&AuthorizeBandOracleRequestProposal{}, "oracle/AuthorizeBandOracleRequestProposal", nil) //nolint:staticcheck // deprecated | ||
| cdc.RegisterConcrete(&UpdateBandOracleRequestProposal{}, "oracle/UpdateBandOracleRequestProposal", nil) //nolint:staticcheck // deprecated | ||
| cdc.RegisterConcrete(&EnableBandIBCProposal{}, "oracle/EnableBandIBCProposal", nil) //nolint:staticcheck // deprecated |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find and examine the codec.go file
fd codec.go chain/oracle/types/Repository: InjectiveLabs/sdk-go
Length of output: 91
🏁 Script executed:
# Check the full content of the codec.go file
wc -l chain/oracle/types/codec.goRepository: InjectiveLabs/sdk-go
Length of output: 94
🏁 Script executed:
# Read the full codec.go file to see all registrations
cat -n chain/oracle/types/codec.goRepository: InjectiveLabs/sdk-go
Length of output: 5233
🏁 Script executed:
# Search for MsgRelayChainlinkPrices in the codebase
rg "MsgRelayChainlinkPrices" -g '*.go' -A 3 -B 3Repository: InjectiveLabs/sdk-go
Length of output: 16866
🏁 Script executed:
# Search for other relay Msg types to understand the pattern
rg "MsgRelay.*Prices" -g '*.go' | head -30Repository: InjectiveLabs/sdk-go
Length of output: 2616
Register MsgRelayChainlinkPrices in both codec registries.
The new MsgRelayChainlinkPrices message is defined and implements sdk.Msg but is missing from both the legacy Amino codec and the interface registry. This breaks Any unpacking and JSON/Amino serialization.
Add registrations:
- In
RegisterLegacyAminoCodec: afterMsgRelayStorkPrices(line 22) - In
RegisterInterfaces: afterMsgRelayStorkPrices(line 47)
Suggested fix
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgRelayPriceFeedPrice{}, "oracle/MsgRelayPriceFeedPrice", nil)
cdc.RegisterConcrete(&MsgRelayCoinbaseMessages{}, "oracle/MsgRelayCoinbaseMessages", nil)
cdc.RegisterConcrete(&MsgRelayProviderPrices{}, "oracle/MsgRelayProviderPrices", nil)
cdc.RegisterConcrete(&MsgRelayPythPrices{}, "oracle/MsgRelayPythPrices", nil)
cdc.RegisterConcrete(&MsgRelayStorkPrices{}, "oracle/MsgRelayStorkPrices", nil)
+ cdc.RegisterConcrete(&MsgRelayChainlinkPrices{}, "oracle/MsgRelayChainlinkPrices", nil)
cdc.RegisterConcrete(&MsgUpdateParams{}, "oracle/MsgUpdateParams", nil) func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgRelayPriceFeedPrice{},
&MsgRelayCoinbaseMessages{},
&MsgRelayProviderPrices{},
&MsgRelayPythPrices{},
&MsgRelayStorkPrices{},
+ &MsgRelayChainlinkPrices{},
&MsgUpdateParams{},
)🤖 Prompt for AI Agents
In `@chain/oracle/types/codec.go` around lines 33 - 38, The
MsgRelayChainlinkPrices type is not registered, breaking Any unpacking and
Amino/JSON serialization; add its registrations by updating
RegisterLegacyAminoCodec to call
cdc.RegisterConcrete(&MsgRelayChainlinkPrices{},
"oracle/MsgRelayChainlinkPrices", nil) immediately after the existing
MsgRelayStorkPrices registration, and update RegisterInterfaces to register the
concrete type and its sdk.Msg interface mapping (i.e., register
MsgRelayChainlinkPrices as implementing sdk.Msg) immediately after the
MsgRelayStorkPrices entries so both the legacy codec and the interface registry
include MsgRelayChainlinkPrices.
| // Route implements the sdk.Msg interface. It should return the name of the module | ||
| func (MsgRelayChainlinkPrices) Route() string { return RouterKey } | ||
|
|
||
| // Type implements the sdk.Msg interface. It should return the action. | ||
| func (MsgRelayChainlinkPrices) Type() string { return TypeMsgRelayChainlinkPrices } | ||
|
|
||
| // ValidateBasic implements the sdk.Msg interface for MsgRelayChainlinkPrices. | ||
| func (msg MsgRelayChainlinkPrices) ValidateBasic() error { | ||
| if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // GetSignBytes implements the sdk.Msg interface. It encodes the message for signing | ||
| func (msg *MsgRelayChainlinkPrices) GetSignBytes() []byte { | ||
| return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) | ||
| } | ||
|
|
||
| // GetSigners implements the sdk.Msg interface. It defines whose signature is required | ||
| func (msg MsgRelayChainlinkPrices) GetSigners() []sdk.AccAddress { | ||
| sender, err := sdk.AccAddressFromBech32(msg.Sender) | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| return []sdk.AccAddress{sender} | ||
| } |
There was a problem hiding this comment.
Add a basic non-empty reports check.
ValidateBasic currently allows an empty Reports slice, which permits a no-op tx. Consider rejecting empty payloads up-front.
💡 Suggested fix
func (msg MsgRelayChainlinkPrices) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil {
return err
}
+ if len(msg.Reports) == 0 {
+ return ErrBadMessagesCount
+ }
return nil
}🤖 Prompt for AI Agents
In `@chain/oracle/types/msgs.go` around lines 316 - 343,
MsgRelayChainlinkPrices.ValidateBasic currently only checks Sender and allows an
empty Reports slice, enabling a no-op tx; update ValidateBasic in
MsgRelayChainlinkPrices to reject empty payloads by returning an appropriate sdk
error when len(msg.Reports) == 0 (use a standard sdk error like
sdkerrors.ErrInvalidRequest or wrap with fmt.Errorf to include context), so the
function validates both Sender (as it already does) and non-empty Reports before
returning nil.
| PythContract: "", | ||
| ChainlinkVerifierProxyContract: "", | ||
| AcceptUnverifiedChainlinkDataStreamsReports: true, | ||
| ChainlinkDataStreamsVerificationGasLimit: 500_000, | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find the params.go file and examine its structure
find . -name "params.go" -path "*/chain/oracle/types/*" | head -5Repository: InjectiveLabs/sdk-go
Length of output: 94
🏁 Script executed:
# Search for ParamSetPairs, parameter keys, and x/params usage
rg -n "ParamSetPairs|KeyPythContract|KeyChainlink|NewParamSetPair|Subspace" -A 3 -B 1 chain/oracle/types/params.goRepository: InjectiveLabs/sdk-go
Length of output: 443
🏁 Script executed:
# Check the full params.go file to understand structure
wc -l chain/oracle/types/params.goRepository: InjectiveLabs/sdk-go
Length of output: 96
🏁 Script executed:
# Look for imports related to x/params
rg "cosmossdk.io/x/params|paramtypes" chain/oracle/types/params.goRepository: InjectiveLabs/sdk-go
Length of output: 443
🏁 Script executed:
# Check if there are other parameter-related files in the oracle module
find . -path "*/oracle/*" -name "*.go" | xargs grep -l "ParamSetPairs\|paramtypes" | head -10Repository: InjectiveLabs/sdk-go
Length of output: 94
🏁 Script executed:
# Read the full params.go file to see complete implementation
cat -n chain/oracle/types/params.goRepository: InjectiveLabs/sdk-go
Length of output: 4315
🏁 Script executed:
# Check if ParamSetPairs is being used in keeper/module code
rg -n "SetParams|GetParams|ParamSet" chain/oracle/ --include="*.go" | head -20Repository: InjectiveLabs/sdk-go
Length of output: 505
🏁 Script executed:
# Check if ParamSetPairs is being used in keeper/module code
rg -n "SetParams|GetParams|ParamSet" chain/oracle/ -g "*.go" | head -30Repository: InjectiveLabs/sdk-go
Length of output: 829
🏁 Script executed:
# Check if the module is using x/params subspace at all
rg -n "subspace|Subspace|params.Subspace" chain/oracle/ -g "*.go" -A 2 -B 2Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check the module structure and keeper to understand parameter management
fd "keeper" chain/oracle/ | head -5Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check for MsgUpdateParams implementation and how params are handled
rg -n "MsgUpdateParams|UpdateParams" chain/oracle/ -g "*.go" -A 5 | head -40Repository: InjectiveLabs/sdk-go
Length of output: 2645
🏁 Script executed:
# Look for keeper files
find chain/oracle -name "*.go" -type f | grep -E "(keeper|msg_server)" | head -10Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check what's in the oracle directory structure
ls -la chain/oracle/Repository: InjectiveLabs/sdk-go
Length of output: 288
🏁 Script executed:
# Check for msg_server implementation for UpdateParams
find chain/oracle -name "*.go" -type f -exec grep -l "MsgUpdateParams\|UpdateParams" {} \;Repository: InjectiveLabs/sdk-go
Length of output: 146
🏁 Script executed:
# Look for the actual msg_server.go file
find chain/oracle -name "msg_server.go" -o -name "*server*.go"Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Search for how UpdateParams is implemented
rg -n "func.*UpdateParams" chain/oracle/ -g "*.go" -A 10Repository: InjectiveLabs/sdk-go
Length of output: 15235
🏁 Script executed:
# Check if there's a keeper directory
find chain/oracle -type dRepository: InjectiveLabs/sdk-go
Length of output: 248
🏁 Script executed:
# Check if there's actually a keeper somewhere or if this module uses a different pattern
find . -name "keeper.go" -o -name "*keeper*.go" | grep oracleRepository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Look at what's in the bandchain/oracle directory since that's listed
find chain/oracle/bandchain -name "*.go" -type f | head -20Repository: InjectiveLabs/sdk-go
Length of output: 287
🏁 Script executed:
# Check if there's a module.go that defines the module structure
find chain/oracle -name "module.go" -o -name "app_module.go"Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check the structure of chain/oracle to see if there's implementation elsewhere
ls -la chain/oracle/bandchain/oracle/Repository: InjectiveLabs/sdk-go
Length of output: 226
🏁 Script executed:
# Search for "Keeper" definition in oracle module
rg -n "type.*Keeper" chain/oracle/ -g "*.go"Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Look for any .proto files that might show the message definition
find chain/oracle -name "*.proto"Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check if there's a different structure - maybe params are managed at the app level
rg -n "oracle.*Params\|GetOracleParams" --include="*.go" | head -20Repository: InjectiveLabs/sdk-go
Length of output: 505
🏁 Script executed:
# Search the entire repo for how MsgUpdateParams is actually implemented
rg -n "func.*MsgUpdateParams.*UpdateParams" --include="*.go" -A 15 | head -50Repository: InjectiveLabs/sdk-go
Length of output: 505
🏁 Script executed:
# Search across the whole repo for UpdateParams implementations
rg "func.*UpdateParams" -g "*.go" -A 10 | head -60Repository: InjectiveLabs/sdk-go
Length of output: 3503
🏁 Script executed:
# Check if there's a handler in a different location
find . -name "*.go" -type f -exec grep -l "UpdateParams.*Params" {} \; | head -10Repository: InjectiveLabs/sdk-go
Length of output: 1087
🏁 Script executed:
# Look for any grpc server implementation outside chain/oracle
rg "MsgServer.*UpdateParams" -g "*.go" -B 5 -A 10 | head -60Repository: InjectiveLabs/sdk-go
Length of output: 3986
🏁 Script executed:
# Search for actual implementation of UpdateParams (not the unimplemented stub)
rg "func.*UpdateParams.*req.*MsgUpdateParams" -g "*.go" -A 20 | grep -v "Unimplemented" | head -80Repository: InjectiveLabs/sdk-go
Length of output: 4471
🏁 Script executed:
# Check if there's an app/keeper directory or similar
find . -path "*/app/*" -name "*.go" | xargs grep -l "oracle" | head -5Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Look for where params are actually stored/retrieved
rg "keeper.*Params|StoreParams|LoadParams" -g "*.go" | head -20Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check if there's a migration or initialization where Params are stored
rg "DefaultParams\(\)" -g "*.go" -B 2 -A 5 | head -60Repository: InjectiveLabs/sdk-go
Length of output: 3845
🏁 Script executed:
# Look for test files that might show how parameters are tested/used
find chain/oracle -name "*test*.go" -o -name "*_test.go"Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Search the whole repo for how oracle params are actually used
rg "oracle.*params\|GetOracleParams" -g "*.go" -i | head -20Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check if this is a proto-generated module by looking at .proto files anywhere
find . -name "*.proto" | xargs grep -l "oracle" | head -5Repository: InjectiveLabs/sdk-go
Length of output: 302
🏁 Script executed:
# Look for keeper implementations by searching in other modules as reference
rg "type Keeper struct" -g "*.go" -A 5 | head -40Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Look for oracle proto files specifically
find . -name "*.proto" -path "*oracle*"Repository: InjectiveLabs/sdk-go
Length of output: 337
🏁 Script executed:
# Check if there's proto directory for oracle
find proto -name "*oracle*" -o -path "*/oracle/*"Repository: InjectiveLabs/sdk-go
Length of output: 379
🏁 Script executed:
# Search for parameter implementations across modules to understand the pattern
rg "ParamSetPairs.*paramtypes.ParamSetPairs" -g "*.go" -B 5 -A 15 | head -80Repository: InjectiveLabs/sdk-go
Length of output: 6050
🏁 Script executed:
# Check if the oracle module actually has a keeper implementation somewhere or uses a different pattern
find . -path "*/oracle/*" -name "*.go" | xargs ls -laRepository: InjectiveLabs/sdk-go
Length of output: 2231
🏁 Script executed:
# Check the oracle proto files to understand the Params message definition
cat proto/injective/oracle/v1beta1/oracle.proto | grep -A 30 "message Params"Repository: InjectiveLabs/sdk-go
Length of output: 712
🏁 Script executed:
# Check the tx proto file for MsgUpdateParams
cat proto/injective/oracle/v1beta1/tx.proto | grep -A 10 "MsgUpdateParams"Repository: InjectiveLabs/sdk-go
Length of output: 909
🏁 Script executed:
# Look at the genesis proto to understand how params are stored
cat proto/injective/oracle/v1beta1/genesis.proto | head -50Repository: InjectiveLabs/sdk-go
Length of output: 1600
🏁 Script executed:
# Search for where ParamKeyTable is actually used in the codebase
rg "ParamKeyTable" -g "*.go" -B 2 -A 5Repository: InjectiveLabs/sdk-go
Length of output: 1483
🏁 Script executed:
# Check if there's actually a keeper that uses these params
find . -name "*.go" -type f | xargs grep -l "oracle.*keeper\|OracleKeeper" | head -5Repository: InjectiveLabs/sdk-go
Length of output: 106
🏁 Script executed:
# Check how wasmx module uses params - as a reference
rg "wasmx.*Keeper|type Keeper" chain/wasmx/ -g "*.go" -A 10 | head -50Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Final confirmation - check if ParamSetPairs is used in any actual keeper implementation
rg "p\.PythContract\|p\.Chainlink" chain/oracle/ -g "*.go" -B 3 -A 3Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check if there's an app configuration that actually uses oracle params
find . -name "app.go" -o -name "*app*.go" | xargs grep -l "oracle" | head -3Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Look for any references to oracle params being actually loaded/stored
rg "GetParams.*oracle\|oracle.*GetParams\|GetOracleParams" -g "*.go" -B 2 -A 2 | head -40Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check if Subspace is used anywhere with oracle
rg "oracle.*Subspace\|Subspace.*oracle" -g "*.go" -B 2 -A 2Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Let me check if there are any test files that show how params are actually used
find chain/oracle -type f -name "*.go" | xargs grep -l "Params\|params" | head -10Repository: InjectiveLabs/sdk-go
Length of output: 360
🏁 Script executed:
# Search for any actual parameter usage or testing
rg "DefaultParams\|ParamSetPairs" chain/oracle/types/params.go -A 5 -B 2Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Check if there's a separate module implementation in injective-core that's imported
rg "injective-core\|oracle.*module\|ModuleBasics" . -g "*.go" | head -10Repository: InjectiveLabs/sdk-go
Length of output: 46
🏁 Script executed:
# Final check - see if ParamSetPairs is actually being called anywhere
rg "ParamSetPairs\(\)" -g "*.go" -B 5 -A 5 | head -60Repository: InjectiveLabs/sdk-go
Length of output: 3950
New Chainlink params are missing from ParamSetPairs registration.
The new fields (ChainlinkVerifierProxyContract, AcceptUnverifiedChainlinkDataStreamsReports, ChainlinkDataStreamsVerificationGasLimit) are defined in Params and populated in DefaultParams(), but ParamSetPairs() only registers PythContract. Since the module uses x/params subspace (via ParamKeyTable()), these new fields won't be persisted or updateable through the params system.
Add parameter keys and register all four fields in ParamSetPairs() with appropriate validators:
Suggested fix
// Parameter keys
var (
KeyPythContract = []byte("PythContract")
+ KeyChainlinkVerifierProxyContract = []byte("ChainlinkVerifierProxyContract")
+ KeyAcceptUnverifiedChainlinkDataStreamsReports = []byte("AcceptUnverifiedChainlinkDataStreamsReports")
+ KeyChainlinkDataStreamsVerificationGasLimit = []byte("ChainlinkDataStreamsVerificationGasLimit")
)
func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
return paramtypes.ParamSetPairs{
paramtypes.NewParamSetPair(KeyPythContract, &p.PythContract, validatePythContract),
+ paramtypes.NewParamSetPair(KeyChainlinkVerifierProxyContract, &p.ChainlinkVerifierProxyContract, ValidateChainlinkVerifierProxyContract),
+ paramtypes.NewParamSetPair(KeyAcceptUnverifiedChainlinkDataStreamsReports, &p.AcceptUnverifiedChainlinkDataStreamsReports, validateAcceptUnverifiedChainlinkDataStreamsReports),
+ paramtypes.NewParamSetPair(KeyChainlinkDataStreamsVerificationGasLimit, &p.ChainlinkDataStreamsVerificationGasLimit, validateChainlinkDataStreamsVerificationGasLimit),
}
}
+
+func validateAcceptUnverifiedChainlinkDataStreamsReports(i interface{}) error {
+ if _, ok := i.(bool); !ok {
+ return fmt.Errorf("invalid parameter type: %T", i)
+ }
+ return nil
+}
+
+func validateChainlinkDataStreamsVerificationGasLimit(i interface{}) error {
+ if _, ok := i.(uint64); !ok {
+ return fmt.Errorf("invalid parameter type: %T", i)
+ }
+ return nil
+}🤖 Prompt for AI Agents
In `@chain/oracle/types/params.go` around lines 55 - 59, ParamSetPairs currently
only registers PythContract so the new fields defined on Params
(ChainlinkVerifierProxyContract, AcceptUnverifiedChainlinkDataStreamsReports,
ChainlinkDataStreamsVerificationGasLimit) won't be persisted or updateable;
update the ParamSetPairs() method to register all four fields (PythContract,
ChainlinkVerifierProxyContract, AcceptUnverifiedChainlinkDataStreamsReports,
ChainlinkDataStreamsVerificationGasLimit) using their param keys and attach
validators: string validators for the two contract addresses (non-empty or allow
empty if intended), a boolean validator for
AcceptUnverifiedChainlinkDataStreamsReports, and a uint/uint64 validator (e.g.,
non-negative) for ChainlinkDataStreamsVerificationGasLimit; ensure the
ParamKeyTable/param key constants referenced by ParamSetPairs match the keys
used elsewhere (and align with DefaultParams()).
| // GetTitle returns the title of this proposal. | ||
| func (p *AuthorizeBandOracleRequestProposal) GetTitle() string { | ||
| func (p *GrantStorkPublisherPrivilegeProposal) GetTitle() string { | ||
| return p.Title | ||
| } | ||
|
|
||
| // GetDescription returns the description of this proposal. | ||
| func (p *AuthorizeBandOracleRequestProposal) GetDescription() string { | ||
| func (p *GrantStorkPublisherPrivilegeProposal) GetDescription() string { | ||
| return p.Description | ||
| } | ||
|
|
||
| // ProposalRoute returns router key of this proposal. | ||
| func (p *AuthorizeBandOracleRequestProposal) ProposalRoute() string { return RouterKey } | ||
| func (p *GrantStorkPublisherPrivilegeProposal) ProposalRoute() string { return RouterKey } | ||
|
|
||
| // ProposalType returns proposal type of this proposal. | ||
| func (p *AuthorizeBandOracleRequestProposal) ProposalType() string { | ||
| return ProposalTypeRevokePriceFeederOraclePrivilege | ||
| func (p *GrantStorkPublisherPrivilegeProposal) ProposalType() string { | ||
| return ProposalTypeGrantBandOraclePrivilege | ||
| } | ||
|
|
||
| // ValidateBasic returns ValidateBasic result of this proposal. | ||
| func (p *AuthorizeBandOracleRequestProposal) ValidateBasic() error { | ||
| if p.Request.OracleScriptId <= 0 { | ||
| return errors.Wrapf(ErrInvalidBandIBCRequest, "AuthorizeBandOracleRequestProposal: Oracle script id (%d) must be positive.", p.Request.OracleScriptId) | ||
| } | ||
|
|
||
| if len(p.Request.Symbols) == 0 { | ||
| return errors.Wrap(ErrBadSymbolsCount, "AuthorizeBandOracleRequestProposal") | ||
| } | ||
|
|
||
| callData, err := bandobi.Encode(bandprice.SymbolInput{ | ||
| Symbols: p.Request.Symbols, | ||
| MinimumSourceCount: uint8(p.Request.MinCount), | ||
| }) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if len(callData) > bandoracle.MaxDataSize { | ||
| return bandoracle.WrapMaxError(bandoracle.ErrTooLargeCalldata, len(callData), bandoracle.MaxDataSize) | ||
| } | ||
|
|
||
| if p.Request.MinCount <= 0 { | ||
| return errors.Wrapf(bandoracle.ErrInvalidMinCount, "AuthorizeBandOracleRequestProposal: Minimum validator count (%d) must be positive.", p.Request.MinCount) | ||
| } | ||
|
|
||
| if p.Request.AskCount <= 0 { | ||
| return errors.Wrapf(bandoracle.ErrInvalidAskCount, "AuthorizeBandOracleRequestProposal: Request validator count (%d) must be positive.", p.Request.AskCount) | ||
| } | ||
|
|
||
| if p.Request.AskCount < p.Request.MinCount { | ||
| return errors.Wrapf(bandoracle.ErrInvalidAskCount, "AuthorizeBandOracleRequestProposal: Request validator count (%d) must not be less than sufficient validator count (%d).", p.Request.AskCount, p.Request.MinCount) | ||
| } | ||
|
|
||
| if !p.Request.FeeLimit.IsValid() { | ||
| return errors.Wrapf(sdkerrors.ErrInvalidCoins, "AuthorizeBandOracleRequestProposal: Invalid Fee Limit (%s)", p.Request.GetFeeLimit().String()) | ||
| } | ||
|
|
||
| if p.Request.PrepareGas <= 0 { | ||
| return errors.Wrapf(bandoracle.ErrInvalidOwasmGas, "AuthorizeBandOracleRequestProposal: Invalid Prepare Gas (%d)", p.Request.GetPrepareGas()) | ||
| } | ||
|
|
||
| if p.Request.ExecuteGas <= 0 { | ||
| return errors.Wrapf(bandoracle.ErrInvalidOwasmGas, "AuthorizeBandOracleRequestProposal: Invalid Execute Gas (%d)", p.Request.ExecuteGas) | ||
| } | ||
|
|
||
| if p.Request.PrepareGas+p.Request.ExecuteGas > bandoracle.MaximumOwasmGas { | ||
| return errors.Wrapf(bandoracle.ErrInvalidOwasmGas, "AuthorizeBandOracleRequestProposal: Sum of prepare gas and execute gas (%d) exceeds Maximum gas (%d)", (p.Request.PrepareGas + p.Request.ExecuteGas), bandoracle.MaximumOwasmGas) | ||
| func (p *GrantStorkPublisherPrivilegeProposal) ValidateBasic() error { | ||
| for _, publisher := range p.StorkPublishers { | ||
| if !common.IsHexAddress(publisher) { | ||
| return fmt.Errorf("invalid publisher address: %s", publisher) | ||
| } | ||
| } | ||
|
|
||
| return govtypes.ValidateAbstract(p) | ||
| return nil | ||
| } | ||
|
|
||
| // GetTitle returns the title of this proposal. | ||
| func (p *EnableBandIBCProposal) GetTitle() string { | ||
| func (p *RevokeStorkPublisherPrivilegeProposal) GetTitle() string { | ||
| return p.Title | ||
| } | ||
|
|
||
| // GetDescription returns the description of this proposal. | ||
| func (p *EnableBandIBCProposal) GetDescription() string { | ||
| func (p *RevokeStorkPublisherPrivilegeProposal) GetDescription() string { | ||
| return p.Description | ||
| } | ||
|
|
||
| // ProposalRoute returns router key of this proposal. | ||
| func (p *EnableBandIBCProposal) ProposalRoute() string { return RouterKey } | ||
| func (p *RevokeStorkPublisherPrivilegeProposal) ProposalRoute() string { return RouterKey } | ||
|
|
||
| // ProposalType returns proposal type of this proposal. | ||
| func (p *EnableBandIBCProposal) ProposalType() string { | ||
| return ProposalEnableBandIBC | ||
| func (p *RevokeStorkPublisherPrivilegeProposal) ProposalType() string { | ||
| return ProposalTypeGrantBandOraclePrivilege | ||
| } | ||
|
|
||
| // ValidateBasic returns ValidateBasic result of this proposal. | ||
| func (p *EnableBandIBCProposal) ValidateBasic() error { | ||
|
|
||
| if p.BandIbcParams.IbcRequestInterval == 0 { | ||
| return ErrBadRequestInterval | ||
| } | ||
|
|
||
| if p.BandIbcParams.IbcSourceChannel == "" { | ||
| return errors.Wrap(ErrInvalidBandIBCRequest, "AuthorizeBandOracleRequestProposal: IBC Source Chanel must not be empty.") | ||
| } | ||
| if p.BandIbcParams.IbcVersion == "" { | ||
| return errors.Wrap(bandoracle.ErrInvalidVersion, "AuthorizeBandOracleRequestProposal: IBC Version must not be empty.") | ||
| func (p *RevokeStorkPublisherPrivilegeProposal) ValidateBasic() error { | ||
| for _, publisher := range p.StorkPublishers { | ||
| if !common.IsHexAddress(publisher) { | ||
| return fmt.Errorf("invalid publisher address: %s", publisher) | ||
| } | ||
| } | ||
|
|
||
| return govtypes.ValidateAbstract(p) | ||
| return nil | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, run the provided inspection script
rg -n "GrantStorkPublisherPrivilegeProposal|RevokeStorkPublisherPrivilegeProposal|ValidateAbstract" chain/oracle/types/proposal.go -C 2Repository: InjectiveLabs/sdk-go
Length of output: 2538
🏁 Script executed:
#!/bin/bash
# Search for the proposal type constants definitions
rg -n "ProposalType.*=|const.*ProposalType" chain/oracle/types/proposal.go | head -30Repository: InjectiveLabs/sdk-go
Length of output: 861
🏁 Script executed:
#!/bin/bash
# Check if the expected constants exist
rg -n "ProposalTypeGrantStorkPublisherPrivilege|ProposalTypeRevokeStorkPublisherPrivilege|ProposalTypeGrantBandOraclePrivilege" chain/oracle/types/Repository: InjectiveLabs/sdk-go
Length of output: 1014
🏁 Script executed:
#!/bin/bash
# Check how other proposal types implement ValidateBasic with ValidateAbstract
rg -B 5 -A 2 "ValidateAbstract\(p\)" chain/oracle/types/proposal.goRepository: InjectiveLabs/sdk-go
Length of output: 464
Fix Stork proposal type constants and restore ValidateAbstract validation.
Both GrantStorkPublisherPrivilegeProposal and RevokeStorkPublisherPrivilegeProposal return ProposalTypeGrantBandOraclePrivilege instead of their respective types, and both skip govtypes.ValidateAbstract() in ValidateBasic(), allowing invalid proposal metadata to pass validation. The correct constants (ProposalTypeGrantStorkPublisherPrivilege and ProposalTypeRevokeStorkPublisherPrivilege) exist and are already registered.
Suggested fix
func (p *GrantStorkPublisherPrivilegeProposal) ProposalType() string {
- return ProposalTypeGrantBandOraclePrivilege
+ return ProposalTypeGrantStorkPublisherPrivilege
}
func (p *GrantStorkPublisherPrivilegeProposal) ValidateBasic() error {
for _, publisher := range p.StorkPublishers {
if !common.IsHexAddress(publisher) {
return fmt.Errorf("invalid publisher address: %s", publisher)
}
}
- return nil
+ return govtypes.ValidateAbstract(p)
}
func (p *RevokeStorkPublisherPrivilegeProposal) ProposalType() string {
- return ProposalTypeGrantBandOraclePrivilege
+ return ProposalTypeRevokeStorkPublisherPrivilege
}
func (p *RevokeStorkPublisherPrivilegeProposal) ValidateBasic() error {
for _, publisher := range p.StorkPublishers {
if !common.IsHexAddress(publisher) {
return fmt.Errorf("invalid publisher address: %s", publisher)
}
}
- return nil
+ return govtypes.ValidateAbstract(p)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // GetTitle returns the title of this proposal. | |
| func (p *AuthorizeBandOracleRequestProposal) GetTitle() string { | |
| func (p *GrantStorkPublisherPrivilegeProposal) GetTitle() string { | |
| return p.Title | |
| } | |
| // GetDescription returns the description of this proposal. | |
| func (p *AuthorizeBandOracleRequestProposal) GetDescription() string { | |
| func (p *GrantStorkPublisherPrivilegeProposal) GetDescription() string { | |
| return p.Description | |
| } | |
| // ProposalRoute returns router key of this proposal. | |
| func (p *AuthorizeBandOracleRequestProposal) ProposalRoute() string { return RouterKey } | |
| func (p *GrantStorkPublisherPrivilegeProposal) ProposalRoute() string { return RouterKey } | |
| // ProposalType returns proposal type of this proposal. | |
| func (p *AuthorizeBandOracleRequestProposal) ProposalType() string { | |
| return ProposalTypeRevokePriceFeederOraclePrivilege | |
| func (p *GrantStorkPublisherPrivilegeProposal) ProposalType() string { | |
| return ProposalTypeGrantBandOraclePrivilege | |
| } | |
| // ValidateBasic returns ValidateBasic result of this proposal. | |
| func (p *AuthorizeBandOracleRequestProposal) ValidateBasic() error { | |
| if p.Request.OracleScriptId <= 0 { | |
| return errors.Wrapf(ErrInvalidBandIBCRequest, "AuthorizeBandOracleRequestProposal: Oracle script id (%d) must be positive.", p.Request.OracleScriptId) | |
| } | |
| if len(p.Request.Symbols) == 0 { | |
| return errors.Wrap(ErrBadSymbolsCount, "AuthorizeBandOracleRequestProposal") | |
| } | |
| callData, err := bandobi.Encode(bandprice.SymbolInput{ | |
| Symbols: p.Request.Symbols, | |
| MinimumSourceCount: uint8(p.Request.MinCount), | |
| }) | |
| if err != nil { | |
| return err | |
| } | |
| if len(callData) > bandoracle.MaxDataSize { | |
| return bandoracle.WrapMaxError(bandoracle.ErrTooLargeCalldata, len(callData), bandoracle.MaxDataSize) | |
| } | |
| if p.Request.MinCount <= 0 { | |
| return errors.Wrapf(bandoracle.ErrInvalidMinCount, "AuthorizeBandOracleRequestProposal: Minimum validator count (%d) must be positive.", p.Request.MinCount) | |
| } | |
| if p.Request.AskCount <= 0 { | |
| return errors.Wrapf(bandoracle.ErrInvalidAskCount, "AuthorizeBandOracleRequestProposal: Request validator count (%d) must be positive.", p.Request.AskCount) | |
| } | |
| if p.Request.AskCount < p.Request.MinCount { | |
| return errors.Wrapf(bandoracle.ErrInvalidAskCount, "AuthorizeBandOracleRequestProposal: Request validator count (%d) must not be less than sufficient validator count (%d).", p.Request.AskCount, p.Request.MinCount) | |
| } | |
| if !p.Request.FeeLimit.IsValid() { | |
| return errors.Wrapf(sdkerrors.ErrInvalidCoins, "AuthorizeBandOracleRequestProposal: Invalid Fee Limit (%s)", p.Request.GetFeeLimit().String()) | |
| } | |
| if p.Request.PrepareGas <= 0 { | |
| return errors.Wrapf(bandoracle.ErrInvalidOwasmGas, "AuthorizeBandOracleRequestProposal: Invalid Prepare Gas (%d)", p.Request.GetPrepareGas()) | |
| } | |
| if p.Request.ExecuteGas <= 0 { | |
| return errors.Wrapf(bandoracle.ErrInvalidOwasmGas, "AuthorizeBandOracleRequestProposal: Invalid Execute Gas (%d)", p.Request.ExecuteGas) | |
| } | |
| if p.Request.PrepareGas+p.Request.ExecuteGas > bandoracle.MaximumOwasmGas { | |
| return errors.Wrapf(bandoracle.ErrInvalidOwasmGas, "AuthorizeBandOracleRequestProposal: Sum of prepare gas and execute gas (%d) exceeds Maximum gas (%d)", (p.Request.PrepareGas + p.Request.ExecuteGas), bandoracle.MaximumOwasmGas) | |
| func (p *GrantStorkPublisherPrivilegeProposal) ValidateBasic() error { | |
| for _, publisher := range p.StorkPublishers { | |
| if !common.IsHexAddress(publisher) { | |
| return fmt.Errorf("invalid publisher address: %s", publisher) | |
| } | |
| } | |
| return govtypes.ValidateAbstract(p) | |
| return nil | |
| } | |
| // GetTitle returns the title of this proposal. | |
| func (p *EnableBandIBCProposal) GetTitle() string { | |
| func (p *RevokeStorkPublisherPrivilegeProposal) GetTitle() string { | |
| return p.Title | |
| } | |
| // GetDescription returns the description of this proposal. | |
| func (p *EnableBandIBCProposal) GetDescription() string { | |
| func (p *RevokeStorkPublisherPrivilegeProposal) GetDescription() string { | |
| return p.Description | |
| } | |
| // ProposalRoute returns router key of this proposal. | |
| func (p *EnableBandIBCProposal) ProposalRoute() string { return RouterKey } | |
| func (p *RevokeStorkPublisherPrivilegeProposal) ProposalRoute() string { return RouterKey } | |
| // ProposalType returns proposal type of this proposal. | |
| func (p *EnableBandIBCProposal) ProposalType() string { | |
| return ProposalEnableBandIBC | |
| func (p *RevokeStorkPublisherPrivilegeProposal) ProposalType() string { | |
| return ProposalTypeGrantBandOraclePrivilege | |
| } | |
| // ValidateBasic returns ValidateBasic result of this proposal. | |
| func (p *EnableBandIBCProposal) ValidateBasic() error { | |
| if p.BandIbcParams.IbcRequestInterval == 0 { | |
| return ErrBadRequestInterval | |
| } | |
| if p.BandIbcParams.IbcSourceChannel == "" { | |
| return errors.Wrap(ErrInvalidBandIBCRequest, "AuthorizeBandOracleRequestProposal: IBC Source Chanel must not be empty.") | |
| } | |
| if p.BandIbcParams.IbcVersion == "" { | |
| return errors.Wrap(bandoracle.ErrInvalidVersion, "AuthorizeBandOracleRequestProposal: IBC Version must not be empty.") | |
| func (p *RevokeStorkPublisherPrivilegeProposal) ValidateBasic() error { | |
| for _, publisher := range p.StorkPublishers { | |
| if !common.IsHexAddress(publisher) { | |
| return fmt.Errorf("invalid publisher address: %s", publisher) | |
| } | |
| } | |
| return govtypes.ValidateAbstract(p) | |
| return nil | |
| } | |
| // GetTitle returns the title of this proposal. | |
| func (p *GrantStorkPublisherPrivilegeProposal) GetTitle() string { | |
| return p.Title | |
| } | |
| // GetDescription returns the description of this proposal. | |
| func (p *GrantStorkPublisherPrivilegeProposal) GetDescription() string { | |
| return p.Description | |
| } | |
| // ProposalRoute returns router key of this proposal. | |
| func (p *GrantStorkPublisherPrivilegeProposal) ProposalRoute() string { return RouterKey } | |
| // ProposalType returns proposal type of this proposal. | |
| func (p *GrantStorkPublisherPrivilegeProposal) ProposalType() string { | |
| return ProposalTypeGrantStorkPublisherPrivilege | |
| } | |
| // ValidateBasic returns ValidateBasic result of this proposal. | |
| func (p *GrantStorkPublisherPrivilegeProposal) ValidateBasic() error { | |
| for _, publisher := range p.StorkPublishers { | |
| if !common.IsHexAddress(publisher) { | |
| return fmt.Errorf("invalid publisher address: %s", publisher) | |
| } | |
| } | |
| return govtypes.ValidateAbstract(p) | |
| } | |
| // GetTitle returns the title of this proposal. | |
| func (p *RevokeStorkPublisherPrivilegeProposal) GetTitle() string { | |
| return p.Title | |
| } | |
| // GetDescription returns the description of this proposal. | |
| func (p *RevokeStorkPublisherPrivilegeProposal) GetDescription() string { | |
| return p.Description | |
| } | |
| // ProposalRoute returns router key of this proposal. | |
| func (p *RevokeStorkPublisherPrivilegeProposal) ProposalRoute() string { return RouterKey } | |
| // ProposalType returns proposal type of this proposal. | |
| func (p *RevokeStorkPublisherPrivilegeProposal) ProposalType() string { | |
| return ProposalTypeRevokeStorkPublisherPrivilege | |
| } | |
| // ValidateBasic returns ValidateBasic result of this proposal. | |
| func (p *RevokeStorkPublisherPrivilegeProposal) ValidateBasic() error { | |
| for _, publisher := range p.StorkPublishers { | |
| if !common.IsHexAddress(publisher) { | |
| return fmt.Errorf("invalid publisher address: %s", publisher) | |
| } | |
| } | |
| return govtypes.ValidateAbstract(p) | |
| } |
🤖 Prompt for AI Agents
In `@chain/oracle/types/proposal.go` around lines 203 - 259,
GrantStorkPublisherPrivilegeProposal.ProposalType and
RevokeStorkPublisherPrivilegeProposal.ProposalType incorrectly return
ProposalTypeGrantBandOraclePrivilege and both ValidateBasic implementations omit
govtypes.ValidateAbstract; update
GrantStorkPublisherPrivilegeProposal.ProposalType to return
ProposalTypeGrantStorkPublisherPrivilege and
RevokeStorkPublisherPrivilegeProposal.ProposalType to return
ProposalTypeRevokeStorkPublisherPrivilege, and restore abstract validation by
calling govtypes.ValidateAbstract(p) (or returning its error) at the start of
each ValidateBasic in addition to the existing publisher address checks (refer
to methods GetTitle/GetDescription/ProposalRoute/ProposalType/ValidateBasic on
GrantStorkPublisherPrivilegeProposal and RevokeStorkPublisherPrivilegeProposal).
Updated proto dependencies to Injective chain v1.18.0 and Indexer v1.18.3
Summary by CodeRabbit
Release Notes
New Features
Enhancements
Deprecations
Updates