diff --git a/app/ante/cosmos/authz.go b/app/ante/cosmos/authz.go index 808c3e900..b15b64fdd 100644 --- a/app/ante/cosmos/authz.go +++ b/app/ante/cosmos/authz.go @@ -3,7 +3,6 @@ package cosmos import ( "fmt" - errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/authz" @@ -28,7 +27,7 @@ func NewAuthzLimiterDecorator(disabledMsgTypes ...string) AuthzLimiterDecorator func (ald AuthzLimiterDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { if err := ald.checkDisabledMsgs(tx.GetMsgs(), false, 1); err != nil { - return ctx, errorsmod.Wrapf(errortypes.ErrUnauthorized, err.Error()) + return ctx, errortypes.ErrUnauthorized.Wrap(err.Error()) } return next(ctx, tx, simulate) } diff --git a/precompiles/avs/abi.json b/precompiles/avs/abi.json index d7cd79762..a41261e6b 100644 --- a/precompiles/avs/abi.json +++ b/precompiles/avs/abi.json @@ -415,6 +415,49 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "avsAddr", + "type": "address" + } + ], + "name": "getAVSUSDValue", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "avsAddr", + "type": "address" + }, + { + "internalType": "string", + "name": "operatorAddr", + "type": "string" + } + ], + "name": "getOperatorOptedUSDValue", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/precompiles/avs/avs.go b/precompiles/avs/avs.go index 052732a4b..5024752c0 100644 --- a/precompiles/avs/avs.go +++ b/precompiles/avs/avs.go @@ -87,24 +87,68 @@ func (p Precompile) Run(evm *vm.EVM, contract *vm.Contract, readOnly bool) (bz [ switch method.Name { case MethodRegisterAVS: bz, err = p.RegisterAVS(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodDeregisterAVS: bz, err = p.DeregisterAVS(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodUpdateAVS: bz, err = p.UpdateAVS(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodRegisterOperatorToAVS: bz, err = p.BindOperatorToAVS(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodDeregisterOperatorFromAVS: bz, err = p.UnbindOperatorToAVS(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodCreateAVSTask: bz, err = p.CreateAVSTask(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodRegisterBLSPublicKey: bz, err = p.RegisterBLSPublicKey(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodGetOptinOperators: bz, err = p.GetOptedInOperatorAccAddrs(ctx, contract, method, args) case MethodSubmitProof: bz, err = p.SubmitProof(ctx, evm.Origin, contract, stateDB, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(false) + } case MethodGetRegisteredPubkey: bz, err = p.GetRegisteredPubkey(ctx, contract, method, args) + case MethodGetAVSUSDValue: + bz, err = p.GetAVSUSDValue(ctx, contract, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(common.Big0) + } + case MethodGetOperatorOptedUSDValue: + bz, err = p.GetOperatorOptedUSDValue(ctx, contract, method, args) + if err != nil { + ctx.Logger().Error("internal error when calling avs precompile", "module", "avs precompile", "method", method.Name, "err", err) + bz, err = method.Outputs.Pack(common.Big0) + } } if err != nil { @@ -129,7 +173,7 @@ func (Precompile) IsTransaction(methodID string) bool { case MethodRegisterAVS, MethodDeregisterAVS, MethodUpdateAVS, MethodRegisterOperatorToAVS, MethodDeregisterOperatorFromAVS, MethodCreateAVSTask, MethodRegisterBLSPublicKey, MethodSubmitProof: return true - case MethodGetRegisteredPubkey, MethodGetOptinOperators: + case MethodGetRegisteredPubkey, MethodGetOptinOperators, MethodGetAVSUSDValue, MethodGetOperatorOptedUSDValue: return false default: return false diff --git a/precompiles/avs/avs.sol b/precompiles/avs/avs.sol index ef5df67c3..548e28406 100644 --- a/precompiles/avs/avs.sol +++ b/precompiles/avs/avs.sol @@ -141,6 +141,22 @@ interface IAVSManager { /// @param avsAddress avs address function getOptInOperators(address avsAddress) external returns (string[] calldata operators); + /// @dev getAVSUSDValue is a function to retrieve the USD share of specified Avs. + /// @param avsAddr The address of the avs + /// @return amount The total USD share of specified operator and Avs. + function getAVSUSDValue( + address avsAddr + ) external view returns (uint256 amount); + + /// @dev getOperatorOptedUSDValue is a function to retrieve the USD share of specified operator and Avs. + /// @param avsAddr The address of the avs + /// @param operatorAddr The address of the operator + /// @return amount The total USD share of specified operator and Avs. + function getOperatorOptedUSDValue( + address avsAddr, + string memory operatorAddr + ) external view returns (uint256 amount); + /// @dev RegisterBLSPublicKey Emitted when `operator` registers with the public keys `pubKey`. /// @param operator the address of the delegator /// @param pubKey the address of the validator diff --git a/precompiles/avs/avs_test.go b/precompiles/avs/avs_test.go index 03a81399e..1171d328e 100644 --- a/precompiles/avs/avs_test.go +++ b/precompiles/avs/avs_test.go @@ -18,7 +18,7 @@ import ( evmtypes "github.com/evmos/evmos/v14/x/evm/types" ) -func (s *AVSManagerPrecompileSuite) TestIsTransaction() { +func (suite *AVSManagerPrecompileSuite) TestIsTransaction() { testCases := []struct { name string method string @@ -26,57 +26,57 @@ func (s *AVSManagerPrecompileSuite) TestIsTransaction() { }{ { avs.MethodRegisterAVS, - s.precompile.Methods[avs.MethodRegisterAVS].Name, + suite.precompile.Methods[avs.MethodRegisterAVS].Name, true, }, { avs.MethodDeregisterAVS, - s.precompile.Methods[avs.MethodDeregisterAVS].Name, + suite.precompile.Methods[avs.MethodDeregisterAVS].Name, true, }, { avs.MethodUpdateAVS, - s.precompile.Methods[avs.MethodUpdateAVS].Name, + suite.precompile.Methods[avs.MethodUpdateAVS].Name, true, }, { avs.MethodRegisterOperatorToAVS, - s.precompile.Methods[avs.MethodRegisterOperatorToAVS].Name, + suite.precompile.Methods[avs.MethodRegisterOperatorToAVS].Name, true, }, { avs.MethodDeregisterOperatorFromAVS, - s.precompile.Methods[avs.MethodDeregisterOperatorFromAVS].Name, + suite.precompile.Methods[avs.MethodDeregisterOperatorFromAVS].Name, true, }, { avs.MethodSubmitProof, - s.precompile.Methods[avs.MethodSubmitProof].Name, + suite.precompile.Methods[avs.MethodSubmitProof].Name, true, }, { avs.MethodCreateAVSTask, - s.precompile.Methods[avs.MethodCreateAVSTask].Name, + suite.precompile.Methods[avs.MethodCreateAVSTask].Name, true, }, { avs.MethodRegisterBLSPublicKey, - s.precompile.Methods[avs.MethodRegisterBLSPublicKey].Name, + suite.precompile.Methods[avs.MethodRegisterBLSPublicKey].Name, true, }, } for _, tc := range testCases { - s.Run(tc.name, func() { - s.Require().Equal(s.precompile.IsTransaction(tc.method), tc.isTx) + suite.Run(tc.name, func() { + suite.Require().Equal(suite.precompile.IsTransaction(tc.method), tc.isTx) }) } } -func (s *AVSManagerPrecompileSuite) TestRegisterAVS() { +func (suite *AVSManagerPrecompileSuite) TestRegisterAVS() { avsName, operatorAddress, slashAddress, rewardAddress := "avsTest", "exo18cggcpvwspnd5c6ny8wrqxpffj5zmhklprtnph", "0xDF907c29719154eb9872f021d21CAE6E5025d7aB", "0xDF907c29719154eb9872f021d21CAE6E5025d7aB" avsOwnerAddress := []string{ - sdk.AccAddress(s.Address.Bytes()).String(), + sdk.AccAddress(suite.Address.Bytes()).String(), sdk.AccAddress(utiltx.GenerateAddress().Bytes()).String(), sdk.AccAddress(utiltx.GenerateAddress().Bytes()).String(), } @@ -93,11 +93,11 @@ func (s *AVSManagerPrecompileSuite) TestRegisterAVS() { EarningsAddr: operatorAddress, }, } - _, err := s.OperatorMsgServer.RegisterOperator(sdk.WrapSDKContext(s.Ctx), registerReq) - s.NoError(err) + _, err := suite.OperatorMsgServer.RegisterOperator(sdk.WrapSDKContext(suite.Ctx), registerReq) + suite.NoError(err) } commonMalleate := func() (common.Address, []byte) { - input, err := s.precompile.Pack( + input, err := suite.precompile.Pack( avs.MethodRegisterAVS, avsName, minStakeAmount, @@ -111,12 +111,12 @@ func (s *AVSManagerPrecompileSuite) TestRegisterAVS() { epochIdentifier, params, ) - s.Require().NoError(err, "failed to pack input") + suite.Require().NoError(err, "failed to pack input") return common.HexToAddress("0x3e108c058e8066DA635321Dc3018294cA82ddEdf"), input } - successRet, err := s.precompile.Methods[avs.MethodRegisterAVS].Outputs.Pack(true) - s.Require().NoError(err) + successRet, err := suite.precompile.Methods[avs.MethodRegisterAVS].Outputs.Pack(true) + suite.Require().NoError(err) testcases := []struct { name string @@ -140,20 +140,20 @@ func (s *AVSManagerPrecompileSuite) TestRegisterAVS() { for _, tc := range testcases { tc := tc - s.Run(tc.name, func() { + suite.Run(tc.name, func() { - baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) // malleate testcase caller, input := tc.malleate() - contract := vm.NewPrecompile(vm.AccountRef(caller), s.precompile, big.NewInt(0), uint64(1e6)) + contract := vm.NewPrecompile(vm.AccountRef(caller), suite.precompile, big.NewInt(0), uint64(1e6)) contract.Input = input contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: s.App.EvmKeeper.ChainID(), + ChainID: suite.App.EvmKeeper.ChainID(), Nonce: 0, To: &contractAddr, Amount: nil, @@ -165,59 +165,59 @@ func (s *AVSManagerPrecompileSuite) TestRegisterAVS() { } msgEthereumTx := evmtypes.NewTx(&txArgs) - msgEthereumTx.From = s.Address.String() - err := msgEthereumTx.Sign(s.EthSigner, s.Signer) - s.Require().NoError(err, "failed to sign Ethereum message") + msgEthereumTx.From = suite.Address.String() + err := msgEthereumTx.Sign(suite.EthSigner, suite.Signer) + suite.Require().NoError(err, "failed to sign Ethereum message") // Instantiate config - proposerAddress := s.Ctx.BlockHeader().ProposerAddress - cfg, err := s.App.EvmKeeper.EVMConfig(s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID()) - s.Require().NoError(err, "failed to instantiate EVM config") + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.App.EvmKeeper.ChainID()) + suite.Require().NoError(err, "failed to instantiate EVM config") - msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) - s.Require().NoError(err, "failed to instantiate Ethereum message") + msg, err := msgEthereumTx.AsMessage(suite.EthSigner, baseFee) + suite.Require().NoError(err, "failed to instantiate Ethereum message") // Instantiate EVM - evm := s.App.EvmKeeper.NewEVM( - s.Ctx, msg, cfg, nil, s.StateDB, + evm := suite.App.EvmKeeper.NewEVM( + suite.Ctx, msg, cfg, nil, suite.StateDB, ) - params := s.App.EvmKeeper.GetParams(s.Ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + precompileMap := suite.App.EvmKeeper.Precompiles(activePrecompiles...) err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - s.Require().NoError(err, "invalid precompiles", activePrecompiles) + suite.Require().NoError(err, "invalid precompiles", activePrecompiles) evm.WithPrecompiles(precompileMap, activePrecompiles) // Run precompiled contract - bz, err := s.precompile.Run(evm, contract, tc.readOnly) + bz, err := suite.precompile.Run(evm, contract, tc.readOnly) // Check results if tc.expPass { - s.Require().NoError(err, "expected no error when running the precompile") - s.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") + suite.Require().NoError(err, "expected no error when running the precompile") + suite.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") } else { - s.Require().Error(err, "expected error to be returned when running the precompile") - s.Require().Nil(bz, "expected returned bytes to be nil") - s.Require().ErrorContains(err, tc.errContains) + suite.Require().Error(err, "expected error to be returned when running the precompile") + suite.Require().Nil(bz, "expected returned bytes to be nil") + suite.Require().ErrorContains(err, tc.errContains) } }) } } -func (s *AVSManagerPrecompileSuite) TestDeregisterAVS() { +func (suite *AVSManagerPrecompileSuite) TestDeregisterAVS() { avsName := "avsTest" commonMalleate := func() (common.Address, []byte) { // prepare the call input for delegation test - input, err := s.precompile.Pack( + input, err := suite.precompile.Pack( avs.MethodDeregisterAVS, avsName, ) - s.Require().NoError(err, "failed to pack input") + suite.Require().NoError(err, "failed to pack input") return common.HexToAddress("0x3e108c058e8066DA635321Dc3018294cA82ddEdf"), input } - successRet, err := s.precompile.Methods[avs.MethodDeregisterAVS].Outputs.Pack(true) - s.Require().NoError(err) + successRet, err := suite.precompile.Methods[avs.MethodDeregisterAVS].Outputs.Pack(true) + suite.Require().NoError(err) testcases := []struct { name string @@ -230,7 +230,7 @@ func (s *AVSManagerPrecompileSuite) TestDeregisterAVS() { { name: "pass for avs-deregister", malleate: func() (common.Address, []byte) { - s.TestRegisterAVS() + suite.TestRegisterAVS() return commonMalleate() }, readOnly: false, @@ -241,19 +241,19 @@ func (s *AVSManagerPrecompileSuite) TestDeregisterAVS() { for _, tc := range testcases { tc := tc - s.Run(tc.name, func() { - baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + suite.Run(tc.name, func() { + baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) // malleate testcase caller, input := tc.malleate() - contract := vm.NewPrecompile(vm.AccountRef(caller), s.precompile, big.NewInt(0), uint64(1e6)) + contract := vm.NewPrecompile(vm.AccountRef(caller), suite.precompile, big.NewInt(0), uint64(1e6)) contract.Input = input contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: s.App.EvmKeeper.ChainID(), + ChainID: suite.App.EvmKeeper.ChainID(), Nonce: 0, To: &contractAddr, Amount: nil, @@ -265,50 +265,50 @@ func (s *AVSManagerPrecompileSuite) TestDeregisterAVS() { } msgEthereumTx := evmtypes.NewTx(&txArgs) - msgEthereumTx.From = s.Address.String() - err := msgEthereumTx.Sign(s.EthSigner, s.Signer) - s.Require().NoError(err, "failed to sign Ethereum message") + msgEthereumTx.From = suite.Address.String() + err := msgEthereumTx.Sign(suite.EthSigner, suite.Signer) + suite.Require().NoError(err, "failed to sign Ethereum message") // Instantiate config - proposerAddress := s.Ctx.BlockHeader().ProposerAddress - cfg, err := s.App.EvmKeeper.EVMConfig(s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID()) - s.Require().NoError(err, "failed to instantiate EVM config") + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.App.EvmKeeper.ChainID()) + suite.Require().NoError(err, "failed to instantiate EVM config") - msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) - s.Require().NoError(err, "failed to instantiate Ethereum message") + msg, err := msgEthereumTx.AsMessage(suite.EthSigner, baseFee) + suite.Require().NoError(err, "failed to instantiate Ethereum message") // Instantiate EVM - evm := s.App.EvmKeeper.NewEVM( - s.Ctx, msg, cfg, nil, s.StateDB, + evm := suite.App.EvmKeeper.NewEVM( + suite.Ctx, msg, cfg, nil, suite.StateDB, ) - params := s.App.EvmKeeper.GetParams(s.Ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + precompileMap := suite.App.EvmKeeper.Precompiles(activePrecompiles...) err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - s.Require().NoError(err, "invalid precompiles", activePrecompiles) + suite.Require().NoError(err, "invalid precompiles", activePrecompiles) evm.WithPrecompiles(precompileMap, activePrecompiles) // Run precompiled contract - bz, err := s.precompile.Run(evm, contract, tc.readOnly) + bz, err := suite.precompile.Run(evm, contract, tc.readOnly) // Check results if tc.expPass { - s.Require().NoError(err, "expected no error when running the precompile") - s.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") + suite.Require().NoError(err, "expected no error when running the precompile") + suite.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") } else { - s.Require().Error(err, "expected error to be returned when running the precompile") - s.Require().Nil(bz, "expected returned bytes to be nil") - s.Require().ErrorContains(err, tc.errContains) + suite.Require().Error(err, "expected error to be returned when running the precompile") + suite.Require().Nil(bz, "expected returned bytes to be nil") + suite.Require().ErrorContains(err, tc.errContains) } }) } } -func (s *AVSManagerPrecompileSuite) TestUpdateAVS() { +func (suite *AVSManagerPrecompileSuite) TestUpdateAVS() { avsName, slashAddress, rewardAddress := "avsTest", "0xDF907c29719154eb9872f021d21CAE6E5025d7aB", "0xDF907c29719154eb9872f021d21CAE6E5025d7aB" avsOwnerAddress := []string{ - sdk.AccAddress(s.Address.Bytes()).String(), + sdk.AccAddress(suite.Address.Bytes()).String(), sdk.AccAddress(utiltx.GenerateAddress().Bytes()).String(), sdk.AccAddress(utiltx.GenerateAddress().Bytes()).String(), } @@ -318,7 +318,7 @@ func (s *AVSManagerPrecompileSuite) TestUpdateAVS() { epochIdentifier := epochstypes.DayEpochID params := []uint64{2, 3, 4, 4} commonMalleate := func() (common.Address, []byte) { - input, err := s.precompile.Pack( + input, err := suite.precompile.Pack( avs.MethodUpdateAVS, avsName, minStakeAmount, @@ -332,12 +332,12 @@ func (s *AVSManagerPrecompileSuite) TestUpdateAVS() { epochIdentifier, params, ) - s.Require().NoError(err, "failed to pack input") + suite.Require().NoError(err, "failed to pack input") return common.HexToAddress("0x3e108c058e8066DA635321Dc3018294cA82ddEdf"), input } - successRet, err := s.precompile.Methods[avs.MethodUpdateAVS].Outputs.Pack(true) - s.Require().NoError(err) + successRet, err := suite.precompile.Methods[avs.MethodUpdateAVS].Outputs.Pack(true) + suite.Require().NoError(err) testcases := []struct { name string @@ -350,7 +350,7 @@ func (s *AVSManagerPrecompileSuite) TestUpdateAVS() { { name: "pass for avs-update", malleate: func() (common.Address, []byte) { - s.TestRegisterAVS() + suite.TestRegisterAVS() return commonMalleate() }, readOnly: false, @@ -361,19 +361,19 @@ func (s *AVSManagerPrecompileSuite) TestUpdateAVS() { for _, tc := range testcases { tc := tc - s.Run(tc.name, func() { - baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + suite.Run(tc.name, func() { + baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) // malleate testcase caller, input := tc.malleate() - contract := vm.NewPrecompile(vm.AccountRef(caller), s.precompile, big.NewInt(0), uint64(1e6)) + contract := vm.NewPrecompile(vm.AccountRef(caller), suite.precompile, big.NewInt(0), uint64(1e6)) contract.Input = input contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: s.App.EvmKeeper.ChainID(), + ChainID: suite.App.EvmKeeper.ChainID(), Nonce: 0, To: &contractAddr, Amount: nil, @@ -385,49 +385,49 @@ func (s *AVSManagerPrecompileSuite) TestUpdateAVS() { } msgEthereumTx := evmtypes.NewTx(&txArgs) - msgEthereumTx.From = s.Address.String() - err := msgEthereumTx.Sign(s.EthSigner, s.Signer) - s.Require().NoError(err, "failed to sign Ethereum message") + msgEthereumTx.From = suite.Address.String() + err := msgEthereumTx.Sign(suite.EthSigner, suite.Signer) + suite.Require().NoError(err, "failed to sign Ethereum message") // Instantiate config - proposerAddress := s.Ctx.BlockHeader().ProposerAddress - cfg, err := s.App.EvmKeeper.EVMConfig(s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID()) - s.Require().NoError(err, "failed to instantiate EVM config") + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.App.EvmKeeper.ChainID()) + suite.Require().NoError(err, "failed to instantiate EVM config") - msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) - s.Require().NoError(err, "failed to instantiate Ethereum message") + msg, err := msgEthereumTx.AsMessage(suite.EthSigner, baseFee) + suite.Require().NoError(err, "failed to instantiate Ethereum message") // Instantiate EVM - evm := s.App.EvmKeeper.NewEVM( - s.Ctx, msg, cfg, nil, s.StateDB, + evm := suite.App.EvmKeeper.NewEVM( + suite.Ctx, msg, cfg, nil, suite.StateDB, ) - params := s.App.EvmKeeper.GetParams(s.Ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + precompileMap := suite.App.EvmKeeper.Precompiles(activePrecompiles...) err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - s.Require().NoError(err, "invalid precompiles", activePrecompiles) + suite.Require().NoError(err, "invalid precompiles", activePrecompiles) evm.WithPrecompiles(precompileMap, activePrecompiles) // Run precompiled contract - bz, err := s.precompile.Run(evm, contract, tc.readOnly) + bz, err := suite.precompile.Run(evm, contract, tc.readOnly) // Check results if tc.expPass { - s.Require().NoError(err, "expected no error when running the precompile") - s.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") + suite.Require().NoError(err, "expected no error when running the precompile") + suite.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") } else { - s.Require().Error(err, "expected error to be returned when running the precompile") - s.Require().Nil(bz, "expected returned bytes to be nil") - s.Require().ErrorContains(err, tc.errContains) + suite.Require().Error(err, "expected error to be returned when running the precompile") + suite.Require().Nil(bz, "expected returned bytes to be nil") + suite.Require().ErrorContains(err, tc.errContains) } }) } } -func (s *AVSManagerPrecompileSuite) TestRegisterOperatorToAVS() { +func (suite *AVSManagerPrecompileSuite) TestRegisterOperatorToAVS() { // from := s.Address - operatorAddress := sdk.AccAddress(s.Address.Bytes()).String() + operatorAddress := sdk.AccAddress(suite.Address.Bytes()).String() registerOperator := func() { registerReq := &operatortypes.RegisterOperatorReq{ @@ -436,18 +436,18 @@ func (s *AVSManagerPrecompileSuite) TestRegisterOperatorToAVS() { EarningsAddr: operatorAddress, }, } - _, err := s.OperatorMsgServer.RegisterOperator(sdk.WrapSDKContext(s.Ctx), registerReq) - s.NoError(err) + _, err := suite.OperatorMsgServer.RegisterOperator(sdk.WrapSDKContext(suite.Ctx), registerReq) + suite.NoError(err) } commonMalleate := func() (common.Address, []byte) { - input, err := s.precompile.Pack( + input, err := suite.precompile.Pack( avs.MethodRegisterOperatorToAVS, ) - s.Require().NoError(err, "failed to pack input") + suite.Require().NoError(err, "failed to pack input") return common.HexToAddress("0x3e108c058e8066DA635321Dc3018294cA82ddEdf"), input } - successRet, err := s.precompile.Methods[avs.MethodRegisterAVS].Outputs.Pack(true) - s.Require().NoError(err) + successRet, err := suite.precompile.Methods[avs.MethodRegisterAVS].Outputs.Pack(true) + suite.Require().NoError(err) testcases := []struct { name string @@ -460,7 +460,7 @@ func (s *AVSManagerPrecompileSuite) TestRegisterOperatorToAVS() { { name: "pass for operator opt-in avs", malleate: func() (common.Address, []byte) { - s.TestRegisterAVS() + suite.TestRegisterAVS() registerOperator() return commonMalleate() }, @@ -472,19 +472,19 @@ func (s *AVSManagerPrecompileSuite) TestRegisterOperatorToAVS() { for _, tc := range testcases { tc := tc - s.Run(tc.name, func() { - baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + suite.Run(tc.name, func() { + baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) // malleate testcase caller, input := tc.malleate() - contract := vm.NewPrecompile(vm.AccountRef(caller), s.precompile, big.NewInt(0), uint64(1e6)) + contract := vm.NewPrecompile(vm.AccountRef(caller), suite.precompile, big.NewInt(0), uint64(1e6)) contract.Input = input contract.CallerAddress = caller contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: s.App.EvmKeeper.ChainID(), + ChainID: suite.App.EvmKeeper.ChainID(), Nonce: 0, To: &contractAddr, Amount: nil, @@ -496,47 +496,47 @@ func (s *AVSManagerPrecompileSuite) TestRegisterOperatorToAVS() { } msgEthereumTx := evmtypes.NewTx(&txArgs) - msgEthereumTx.From = s.Address.String() - err := msgEthereumTx.Sign(s.EthSigner, s.Signer) - s.Require().NoError(err, "failed to sign Ethereum message") + msgEthereumTx.From = suite.Address.String() + err := msgEthereumTx.Sign(suite.EthSigner, suite.Signer) + suite.Require().NoError(err, "failed to sign Ethereum message") // Instantiate config - proposerAddress := s.Ctx.BlockHeader().ProposerAddress - cfg, err := s.App.EvmKeeper.EVMConfig(s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID()) - s.Require().NoError(err, "failed to instantiate EVM config") + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.App.EvmKeeper.ChainID()) + suite.Require().NoError(err, "failed to instantiate EVM config") - msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) - s.Require().NoError(err, "failed to instantiate Ethereum message") + msg, err := msgEthereumTx.AsMessage(suite.EthSigner, baseFee) + suite.Require().NoError(err, "failed to instantiate Ethereum message") // Instantiate EVM - evm := s.App.EvmKeeper.NewEVM( - s.Ctx, msg, cfg, nil, s.StateDB, + evm := suite.App.EvmKeeper.NewEVM( + suite.Ctx, msg, cfg, nil, suite.StateDB, ) - params := s.App.EvmKeeper.GetParams(s.Ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + precompileMap := suite.App.EvmKeeper.Precompiles(activePrecompiles...) err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - s.Require().NoError(err, "invalid precompiles", activePrecompiles) + suite.Require().NoError(err, "invalid precompiles", activePrecompiles) evm.WithPrecompiles(precompileMap, activePrecompiles) // Run precompiled contract - bz, err := s.precompile.Run(evm, contract, tc.readOnly) + bz, err := suite.precompile.Run(evm, contract, tc.readOnly) // Check results if tc.expPass { - s.Require().NoError(err, "expected no error when running the precompile") - s.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") + suite.Require().NoError(err, "expected no error when running the precompile") + suite.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") } else { - s.Require().Error(err, "expected error to be returned when running the precompile") - s.Require().Nil(bz, "expected returned bytes to be nil") - s.Require().ErrorContains(err, tc.errContains) + suite.Require().Error(err, "expected error to be returned when running the precompile") + suite.Require().Nil(bz, "expected returned bytes to be nil") + suite.Require().ErrorContains(err, tc.errContains) } }) } } -func (s *AVSManagerPrecompileSuite) TestDeregisterOperatorFromAVS() { +func (suite *AVSManagerPrecompileSuite) TestDeregisterOperatorFromAVS() { // from := s.Address // operatorAddress, err := util.ProcessAddress(from.String()) @@ -551,14 +551,14 @@ func (s *AVSManagerPrecompileSuite) TestDeregisterOperatorFromAVS() { // s.NoError(err) // } commonMalleate := func() (common.Address, []byte) { - input, err := s.precompile.Pack( + input, err := suite.precompile.Pack( avs.MethodDeregisterOperatorFromAVS, ) - s.Require().NoError(err, "failed to pack input") + suite.Require().NoError(err, "failed to pack input") return common.HexToAddress("0x3e108c058e8066DA635321Dc3018294cA82ddEdf"), input } - successRet, err := s.precompile.Methods[avs.MethodDeregisterOperatorFromAVS].Outputs.Pack(true) - s.Require().NoError(err) + successRet, err := suite.precompile.Methods[avs.MethodDeregisterOperatorFromAVS].Outputs.Pack(true) + suite.Require().NoError(err) testcases := []struct { name string @@ -571,7 +571,7 @@ func (s *AVSManagerPrecompileSuite) TestDeregisterOperatorFromAVS() { { name: "pass for operator opt-out avs", malleate: func() (common.Address, []byte) { - s.TestRegisterOperatorToAVS() + suite.TestRegisterOperatorToAVS() // registerOperator() return commonMalleate() }, @@ -583,19 +583,19 @@ func (s *AVSManagerPrecompileSuite) TestDeregisterOperatorFromAVS() { for _, tc := range testcases { tc := tc - s.Run(tc.name, func() { - baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + suite.Run(tc.name, func() { + baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) // malleate testcase caller, input := tc.malleate() - contract := vm.NewPrecompile(vm.AccountRef(caller), s.precompile, big.NewInt(0), uint64(1e6)) + contract := vm.NewPrecompile(vm.AccountRef(caller), suite.precompile, big.NewInt(0), uint64(1e6)) contract.Input = input contract.CallerAddress = caller contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: s.App.EvmKeeper.ChainID(), + ChainID: suite.App.EvmKeeper.ChainID(), Nonce: 0, To: &contractAddr, Amount: nil, @@ -607,58 +607,58 @@ func (s *AVSManagerPrecompileSuite) TestDeregisterOperatorFromAVS() { } msgEthereumTx := evmtypes.NewTx(&txArgs) - msgEthereumTx.From = s.Address.String() - err := msgEthereumTx.Sign(s.EthSigner, s.Signer) - s.Require().NoError(err, "failed to sign Ethereum message") + msgEthereumTx.From = suite.Address.String() + err := msgEthereumTx.Sign(suite.EthSigner, suite.Signer) + suite.Require().NoError(err, "failed to sign Ethereum message") // Instantiate config - proposerAddress := s.Ctx.BlockHeader().ProposerAddress - cfg, err := s.App.EvmKeeper.EVMConfig(s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID()) - s.Require().NoError(err, "failed to instantiate EVM config") + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.App.EvmKeeper.ChainID()) + suite.Require().NoError(err, "failed to instantiate EVM config") - msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) - s.Require().NoError(err, "failed to instantiate Ethereum message") + msg, err := msgEthereumTx.AsMessage(suite.EthSigner, baseFee) + suite.Require().NoError(err, "failed to instantiate Ethereum message") // Instantiate EVM - evm := s.App.EvmKeeper.NewEVM( - s.Ctx, msg, cfg, nil, s.StateDB, + evm := suite.App.EvmKeeper.NewEVM( + suite.Ctx, msg, cfg, nil, suite.StateDB, ) - params := s.App.EvmKeeper.GetParams(s.Ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + precompileMap := suite.App.EvmKeeper.Precompiles(activePrecompiles...) err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - s.Require().NoError(err, "invalid precompiles", activePrecompiles) + suite.Require().NoError(err, "invalid precompiles", activePrecompiles) evm.WithPrecompiles(precompileMap, activePrecompiles) // Run precompiled contract - bz, err := s.precompile.Run(evm, contract, tc.readOnly) + bz, err := suite.precompile.Run(evm, contract, tc.readOnly) // Check results if tc.expPass { - s.Require().NoError(err, "expected no error when running the precompile") - s.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") + suite.Require().NoError(err, "expected no error when running the precompile") + suite.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") } else { - s.Require().Error(err, "expected error to be returned when running the precompile") - s.Require().Nil(bz, "expected returned bytes to be nil") - s.Require().ErrorContains(err, tc.errContains) + suite.Require().Error(err, "expected error to be returned when running the precompile") + suite.Require().Nil(bz, "expected returned bytes to be nil") + suite.Require().ErrorContains(err, tc.errContains) } }) } } // TestRun tests the precompiles Run method reg avstask. -func (s *AVSManagerPrecompileSuite) TestRunRegTaskinfo() { +func (suite *AVSManagerPrecompileSuite) TestRunRegTaskInfo() { taskAddr := utiltx.GenerateAddress() registerAVS := func() { avsName := "avsTest" avsOwnerAddress := []string{ - sdk.AccAddress(s.Address.Bytes()).String(), + sdk.AccAddress(suite.Address.Bytes()).String(), "exo13h6xg79g82e2g2vhjwg7j4r2z2hlncelwutkjr", "exo13h6xg79g82e2g2vhjwg7j4r2z2hlncelwutkj2", } assetID := []string{"11", "22", "33"} - avs := &types.AVSInfo{ + avsInfo := &types.AVSInfo{ Name: avsName, AvsAddress: utiltx.GenerateAddress().String(), SlashAddr: utiltx.GenerateAddress().String(), @@ -675,11 +675,11 @@ func (s *AVSManagerPrecompileSuite) TestRunRegTaskinfo() { TaskAddr: taskAddr.String(), } - err := s.App.AVSManagerKeeper.SetAVSInfo(s.Ctx, avs) - s.NoError(err) + err := suite.App.AVSManagerKeeper.SetAVSInfo(suite.Ctx, avsInfo) + suite.NoError(err) } commonMalleate := func() (common.Address, []byte) { - input, err := s.precompile.Pack( + input, err := suite.precompile.Pack( avs.MethodCreateAVSTask, "test-avstask", rand.Bytes(3), @@ -688,11 +688,11 @@ func (s *AVSManagerPrecompileSuite) TestRunRegTaskinfo() { uint64(3), uint64(3), ) - s.Require().NoError(err, "failed to pack input") - return s.Address, input + suite.Require().NoError(err, "failed to pack input") + return suite.Address, input } - successRet, err := s.precompile.Methods[avs.MethodCreateAVSTask].Outputs.Pack(true) - s.Require().NoError(err) + successRet, err := suite.precompile.Methods[avs.MethodCreateAVSTask].Outputs.Pack(true) + suite.Require().NoError(err) testcases := []struct { name string malleate func() (common.Address, []byte) @@ -704,7 +704,7 @@ func (s *AVSManagerPrecompileSuite) TestRunRegTaskinfo() { { name: "pass - avstask via pre-compiles", malleate: func() (common.Address, []byte) { - s.Require().NoError(err) + suite.Require().NoError(err) registerAVS() return commonMalleate() }, @@ -715,20 +715,20 @@ func (s *AVSManagerPrecompileSuite) TestRunRegTaskinfo() { } for _, tc := range testcases { tc := tc - s.Run(tc.name, func() { - baseFee := s.App.FeeMarketKeeper.GetBaseFee(s.Ctx) + suite.Run(tc.name, func() { + baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) // malleate testcase caller, input := tc.malleate() - contract := vm.NewPrecompile(vm.AccountRef(caller), s.precompile, big.NewInt(0), uint64(1e6)) + contract := vm.NewPrecompile(vm.AccountRef(caller), suite.precompile, big.NewInt(0), uint64(1e6)) contract.Input = input contract.CallerAddress = taskAddr contractAddr := contract.Address() // Build and sign Ethereum transaction txArgs := evmtypes.EvmTxArgs{ - ChainID: s.App.EvmKeeper.ChainID(), + ChainID: suite.App.EvmKeeper.ChainID(), Nonce: 0, To: &contractAddr, Amount: nil, @@ -740,42 +740,42 @@ func (s *AVSManagerPrecompileSuite) TestRunRegTaskinfo() { } msgEthereumTx := evmtypes.NewTx(&txArgs) - msgEthereumTx.From = s.Address.String() - err := msgEthereumTx.Sign(s.EthSigner, s.Signer) - s.Require().NoError(err, "failed to sign Ethereum message") + msgEthereumTx.From = suite.Address.String() + err := msgEthereumTx.Sign(suite.EthSigner, suite.Signer) + suite.Require().NoError(err, "failed to sign Ethereum message") // Instantiate config - proposerAddress := s.Ctx.BlockHeader().ProposerAddress - cfg, err := s.App.EvmKeeper.EVMConfig(s.Ctx, proposerAddress, s.App.EvmKeeper.ChainID()) - s.Require().NoError(err, "failed to instantiate EVM config") + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, suite.App.EvmKeeper.ChainID()) + suite.Require().NoError(err, "failed to instantiate EVM config") - msg, err := msgEthereumTx.AsMessage(s.EthSigner, baseFee) - s.Require().NoError(err, "failed to instantiate Ethereum message") + msg, err := msgEthereumTx.AsMessage(suite.EthSigner, baseFee) + suite.Require().NoError(err, "failed to instantiate Ethereum message") // Create StateDB - s.StateDB = statedb.New(s.Ctx, s.App.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(s.Ctx.HeaderHash().Bytes()))) + suite.StateDB = statedb.New(suite.Ctx, suite.App.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(suite.Ctx.HeaderHash().Bytes()))) // Instantiate EVM - evm := s.App.EvmKeeper.NewEVM( - s.Ctx, msg, cfg, nil, s.StateDB, + evm := suite.App.EvmKeeper.NewEVM( + suite.Ctx, msg, cfg, nil, suite.StateDB, ) - params := s.App.EvmKeeper.GetParams(s.Ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) activePrecompiles := params.GetActivePrecompilesAddrs() - precompileMap := s.App.EvmKeeper.Precompiles(activePrecompiles...) + precompileMap := suite.App.EvmKeeper.Precompiles(activePrecompiles...) err = vm.ValidatePrecompiles(precompileMap, activePrecompiles) - s.Require().NoError(err, "invalid precompiles", activePrecompiles) + suite.Require().NoError(err, "invalid precompiles", activePrecompiles) evm.WithPrecompiles(precompileMap, activePrecompiles) // Run precompiled contract - bz, err := s.precompile.Run(evm, contract, tc.readOnly) + bz, err := suite.precompile.Run(evm, contract, tc.readOnly) // Check results if tc.expPass { - s.Require().NoError(err, "expected no error when running the precompile") - s.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") + suite.Require().NoError(err, "expected no error when running the precompile") + suite.Require().Equal(tc.returnBytes, bz, "the return doesn't match the expected result") } else { - s.Require().Error(err, "expected error to be returned when running the precompile") - s.Require().Nil(bz, "expected returned bytes to be nil") - s.Require().ErrorContains(err, tc.errContains) + suite.Require().Error(err, "expected error to be returned when running the precompile") + suite.Require().Nil(bz, "expected returned bytes to be nil") + suite.Require().ErrorContains(err, tc.errContains) } }) } diff --git a/precompiles/avs/query.go b/precompiles/avs/query.go index dd8dcd37b..80b7083ed 100644 --- a/precompiles/avs/query.go +++ b/precompiles/avs/query.go @@ -12,11 +12,12 @@ import ( ) const ( - MethodGetRegisteredPubkey = "getRegisteredPubkey" - MethodGetOptinOperators = "getOptInOperators" + MethodGetRegisteredPubkey = "getRegisteredPubkey" + MethodGetOptinOperators = "getOptInOperators" + MethodGetAVSUSDValue = "getAVSUSDValue" + MethodGetOperatorOptedUSDValue = "getOperatorOptedUSDValue" ) -// GetRegisteredPubkey func (p Precompile) GetRegisteredPubkey( ctx sdk.Context, _ *vm.Contract, @@ -24,7 +25,7 @@ func (p Precompile) GetRegisteredPubkey( args []interface{}, ) ([]byte, error) { if len(args) != len(p.ABI.Methods[MethodGetRegisteredPubkey].Inputs) { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 1, len(args)) + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodRegisterAVS].Inputs), len(args)) } // the key is set using the operator's acc address so the same logic should apply here addr, ok := args[0].(string) @@ -38,7 +39,6 @@ func (p Precompile) GetRegisteredPubkey( return method.Outputs.Pack(pubkey) } -// GetOptedInOperatorAccAddrs func (p Precompile) GetOptedInOperatorAccAddrs( ctx sdk.Context, _ *vm.Contract, @@ -46,7 +46,7 @@ func (p Precompile) GetOptedInOperatorAccAddrs( args []interface{}, ) ([]byte, error) { if len(args) != len(p.ABI.Methods[MethodGetOptinOperators].Inputs) { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 1, len(args)) + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodRegisterAVS].Inputs), len(args)) } addr, ok := args[0].(common.Address) @@ -60,3 +60,49 @@ func (p Precompile) GetOptedInOperatorAccAddrs( } return method.Outputs.Pack(list) } + +// GetAVSUSDValue is a function to retrieve the USD share of specified Avs, +func (p Precompile) GetAVSUSDValue( + ctx sdk.Context, + _ *vm.Contract, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) != len(p.ABI.Methods[MethodGetAVSUSDValue].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodRegisterAVS].Inputs), len(args)) + } + addr, ok := args[0].(common.Address) + if !ok { + return nil, fmt.Errorf(exocmn.ErrContractInputParaOrType, 0, "common.Address", addr) + } + amount, err := p.avsKeeper.GetOperatorKeeper().GetAVSUSDValue(ctx, addr.String()) + if err != nil { + return nil, err + } + return method.Outputs.Pack(amount.BigInt()) +} + +// GetOperatorOptedUSDValue is a function to retrieve the USD share of specified operator and Avs, +func (p Precompile) GetOperatorOptedUSDValue( + ctx sdk.Context, + _ *vm.Contract, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) != len(p.ABI.Methods[MethodGetOperatorOptedUSDValue].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodRegisterAVS].Inputs), len(args)) + } + avsAddr, ok := args[0].(common.Address) + if !ok { + return nil, fmt.Errorf(exocmn.ErrContractInputParaOrType, 0, "common.Address", avsAddr) + } + operatorAddr, ok := args[1].(string) + if !ok { + return nil, fmt.Errorf(exocmn.ErrContractInputParaOrType, 1, "string", operatorAddr) + } + amount, err := p.avsKeeper.GetOperatorKeeper().GetOperatorOptedUSDValue(ctx, avsAddr.String(), operatorAddr) + if err != nil { + return nil, err + } + return method.Outputs.Pack(amount.ActiveUSDValue.BigInt()) +} diff --git a/precompiles/avs/query_test.go b/precompiles/avs/query_test.go index 1a4eac055..f6f677258 100644 --- a/precompiles/avs/query_test.go +++ b/precompiles/avs/query_test.go @@ -1,11 +1,16 @@ package avs_test import ( + sdkmath "cosmossdk.io/math" "fmt" avsManagerPrecompile "github.com/ExocoreNetwork/exocore/precompiles/avs" exocmn "github.com/ExocoreNetwork/exocore/precompiles/common" + assetstype "github.com/ExocoreNetwork/exocore/x/assets/types" + operatorKeeper "github.com/ExocoreNetwork/exocore/x/operator/keeper" "github.com/ExocoreNetwork/exocore/x/operator/types" + "github.com/ethereum/go-ethereum/common" "math/big" + "time" "github.com/ethereum/go-ethereum/core/vm" ) @@ -21,68 +26,68 @@ type avsTestCases struct { var baseTestCases = []avsTestCases{ { - "fail - empty input args", - func() []interface{} { + name: "fail - empty input args", + malleate: func() []interface{} { return []interface{}{} }, - func(bz []byte) {}, - 100000, - true, - "invalid number of arguments", + postCheck: func(bz []byte) {}, + gas: 100000, + expErr: true, + errContains: "invalid number of arguments", }, { - "fail - invalid address", - func() []interface{} { + name: "fail - invalid address", + malleate: func() []interface{} { return []interface{}{ "invalid", } }, - func(bz []byte) {}, - 100000, - true, - "invalid bech32 string", + postCheck: func(bz []byte) {}, + gas: 100000, + expErr: true, + errContains: "invalid bech32 string", }, } -func (s *AVSManagerPrecompileSuite) TestGetOptedInOperatorAccAddrs() { - method := s.precompile.Methods[avsManagerPrecompile.MethodGetOptinOperators] - operatorAddress, avsAddr, slashContract := "exo18cggcpvwspnd5c6ny8wrqxpffj5zmhklprtnph", s.Address, "0xDF907c29719154eb9872f021d21CAE6E5025d7aB" +func (suite *AVSManagerPrecompileSuite) TestGetOptedInOperatorAccAddrs() { + method := suite.precompile.Methods[avsManagerPrecompile.MethodGetOptinOperators] + operatorAddress, avsAddr, slashContract := "exo18cggcpvwspnd5c6ny8wrqxpffj5zmhklprtnph", suite.Address, "0xDF907c29719154eb9872f021d21CAE6E5025d7aB" operatorOptIn := func() { optedInfo := &types.OptedInfo{ SlashContract: slashContract, // #nosec G701 - OptedInHeight: uint64(s.Ctx.BlockHeight()), + OptedInHeight: uint64(suite.Ctx.BlockHeight()), OptedOutHeight: types.DefaultOptedOutHeight, } - err := s.App.OperatorKeeper.SetOptedInfo(s.Ctx, operatorAddress, avsAddr.String(), optedInfo) - s.NoError(err) + err := suite.App.OperatorKeeper.SetOptedInfo(suite.Ctx, operatorAddress, avsAddr.String(), optedInfo) + suite.NoError(err) } testCases := []avsTestCases{ { - "fail - invalid avs address", - func() []interface{} { + name: "fail - invalid avs address", + malleate: func() []interface{} { return []interface{}{ "invalid", } }, - func(bz []byte) {}, - 100000, - true, - fmt.Sprintf(exocmn.ErrContractInputParaOrType, 0, "string", "0x0000000000000000000000000000000000000000"), + postCheck: func(bz []byte) {}, + gas: 100000, + expErr: true, + errContains: fmt.Sprintf(exocmn.ErrContractInputParaOrType, 0, "string", "0x0000000000000000000000000000000000000000"), }, { "success - no operators", func() []interface{} { return []interface{}{ - s.Address, + suite.Address, } }, func(bz []byte) { var out []string - err := s.precompile.UnpackIntoInterface(&out, avsManagerPrecompile.MethodGetOptinOperators, bz) - s.Require().NoError(err, "failed to unpack output", err) - s.Require().Equal(0, len(out)) + err := suite.precompile.UnpackIntoInterface(&out, avsManagerPrecompile.MethodGetOptinOperators, bz) + suite.Require().NoError(err, "failed to unpack output", err) + suite.Require().Equal(0, len(out)) }, 100000, false, @@ -93,15 +98,105 @@ func (s *AVSManagerPrecompileSuite) TestGetOptedInOperatorAccAddrs() { func() []interface{} { operatorOptIn() return []interface{}{ - s.Address, + suite.Address, } }, func(bz []byte) { var out []string - err := s.precompile.UnpackIntoInterface(&out, avsManagerPrecompile.MethodGetOptinOperators, bz) + err := suite.precompile.UnpackIntoInterface(&out, avsManagerPrecompile.MethodGetOptinOperators, bz) + suite.Require().NoError(err, "failed to unpack output", err) + suite.Require().Equal(1, len(out)) + suite.Require().Equal(operatorAddress, out[0]) + + }, + 100000, + false, + "", + }, + } + testCases = append(testCases, baseTestCases[0]) + + for _, tc := range testCases { + suite.Run(tc.name, func() { + contract := vm.NewContract(vm.AccountRef(suite.Address), suite.precompile, big.NewInt(0), tc.gas) + + bz, err := suite.precompile.GetOptedInOperatorAccAddrs(suite.Ctx, contract, &method, tc.malleate()) + + if tc.expErr { + suite.Require().Error(err) + suite.Require().Contains(err.Error(), tc.errContains) + } else { + suite.Require().NoError(err) + suite.Require().NotEmpty(bz) + tc.postCheck(bz) + } + }) + } +} + +func (suite *AVSManagerPrecompileSuite) TestAVSUSDValue() { + method := s.precompile.Methods[avsManagerPrecompile.MethodGetAVSUSDValue] + expectedUSDvalue := sdkmath.LegacyNewDec(0) + + setUp := func() { + suite.prepare() + // register the new token + usdcAddr := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") + usdcClientChainAsset := assetstype.AssetInfo{ + Name: "USD coin", + Symbol: "USDC", + Address: usdcAddr.String(), + Decimals: 6, + TotalSupply: sdkmath.NewInt(1e18), + LayerZeroChainID: 101, + MetaInfo: "USDC", + } + err := suite.App.AssetsKeeper.SetStakingAssetInfo( + suite.Ctx, + &assetstype.StakingAssetInfo{ + AssetBasicInfo: &usdcClientChainAsset, + StakingTotalAmount: sdkmath.NewInt(0), + }, + ) + suite.NoError(err) + // register the new AVS + suite.prepareAvs([]string{"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48_0x65", "0xdac17f958d2ee523a2206206994597c13d831ec7_0x65"}) + // opt in + err = suite.App.OperatorKeeper.OptIn(suite.Ctx, suite.operatorAddr, suite.avsAddr) + suite.NoError(err) + usdtPrice, err := suite.App.OperatorKeeper.OracleInterface().GetSpecifiedAssetsPrice(suite.Ctx, suite.assetID) + suite.NoError(err) + usdtValue := operatorKeeper.CalculateUSDValue(suite.delegationAmount, usdtPrice.Value, suite.assetDecimal, usdtPrice.Decimal) + // deposit and delegate another asset to the operator + suite.NoError(err) + suite.prepareDeposit(usdcAddr, sdkmath.NewInt(1e8)) + usdcPrice, err := suite.App.OperatorKeeper.OracleInterface().GetSpecifiedAssetsPrice(suite.Ctx, suite.assetID) + suite.NoError(err) + delegatedAmount := sdkmath.NewIntWithDecimal(8, 7) + suite.prepareDelegation(true, usdcAddr, delegatedAmount) + + // updating the new voting power + usdcValue := operatorKeeper.CalculateUSDValue(suite.delegationAmount, usdcPrice.Value, suite.assetDecimal, usdcPrice.Decimal) + expectedUSDvalue = usdcValue.Add(usdtValue) + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + } + + testCases := []avsTestCases{ + { + "success - existent operators", + func() []interface{} { + setUp() + return []interface{}{ + common.HexToAddress(suite.avsAddr), + } + }, + func(bz []byte) { + var out *big.Int + err := s.precompile.UnpackIntoInterface(&out, avsManagerPrecompile.MethodGetAVSUSDValue, bz) s.Require().NoError(err, "failed to unpack output", err) - s.Require().Equal(1, len(out)) - s.Require().Equal(operatorAddress, out[0]) + s.Require().Equal(expectedUSDvalue.BigInt(), out) }, 100000, @@ -115,7 +210,97 @@ func (s *AVSManagerPrecompileSuite) TestGetOptedInOperatorAccAddrs() { s.Run(tc.name, func() { contract := vm.NewContract(vm.AccountRef(s.Address), s.precompile, big.NewInt(0), tc.gas) - bz, err := s.precompile.GetOptedInOperatorAccAddrs(s.Ctx, contract, &method, tc.malleate()) + bz, err := s.precompile.GetAVSUSDValue(s.Ctx, contract, &method, tc.malleate()) + + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errContains) + } else { + s.Require().NoError(err) + s.Require().NotEmpty(bz) + tc.postCheck(bz) + } + }) + } +} + +func (suite *AVSManagerPrecompileSuite) TestGetOperatorOptedUSDValue() { + method := s.precompile.Methods[avsManagerPrecompile.MethodGetOperatorOptedUSDValue] + expectedUSDvalue := sdkmath.LegacyNewDec(0) + + setUp := func() { + suite.prepare() + // register the new token + usdcAddr := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") + usdcClientChainAsset := assetstype.AssetInfo{ + Name: "USD coin", + Symbol: "USDC", + Address: usdcAddr.String(), + Decimals: 6, + TotalSupply: sdkmath.NewInt(1e18), + LayerZeroChainID: 101, + MetaInfo: "USDC", + } + err := suite.App.AssetsKeeper.SetStakingAssetInfo( + suite.Ctx, + &assetstype.StakingAssetInfo{ + AssetBasicInfo: &usdcClientChainAsset, + StakingTotalAmount: sdkmath.NewInt(0), + }, + ) + suite.NoError(err) + // register the new AVS + suite.prepareAvs([]string{"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48_0x65", "0xdac17f958d2ee523a2206206994597c13d831ec7_0x65"}) + // opt in + err = suite.App.OperatorKeeper.OptIn(suite.Ctx, suite.operatorAddr, suite.avsAddr) + suite.NoError(err) + usdtPrice, err := suite.App.OperatorKeeper.OracleInterface().GetSpecifiedAssetsPrice(suite.Ctx, suite.assetID) + suite.NoError(err) + usdtValue := operatorKeeper.CalculateUSDValue(suite.delegationAmount, usdtPrice.Value, suite.assetDecimal, usdtPrice.Decimal) + // deposit and delegate another asset to the operator + suite.NoError(err) + suite.prepareDeposit(usdcAddr, sdkmath.NewInt(1e8)) + usdcPrice, err := suite.App.OperatorKeeper.OracleInterface().GetSpecifiedAssetsPrice(suite.Ctx, suite.assetID) + suite.NoError(err) + delegatedAmount := sdkmath.NewIntWithDecimal(8, 7) + suite.prepareDelegation(true, usdcAddr, delegatedAmount) + + // updating the new voting power + usdcValue := operatorKeeper.CalculateUSDValue(suite.delegationAmount, usdcPrice.Value, suite.assetDecimal, usdcPrice.Decimal) + expectedUSDvalue = usdcValue.Add(usdtValue) + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + } + + testCases := []avsTestCases{ + { + "success - existent operators", + func() []interface{} { + setUp() + return []interface{}{ + common.HexToAddress(suite.avsAddr), + suite.operatorAddr.String(), + } + }, + func(bz []byte) { + var out *big.Int + err := s.precompile.UnpackIntoInterface(&out, avsManagerPrecompile.MethodGetOperatorOptedUSDValue, bz) + s.Require().NoError(err, "failed to unpack output", err) + s.Require().Equal(expectedUSDvalue.BigInt(), out) + }, + 100000, + false, + "", + }, + } + testCases = append(testCases, baseTestCases[0]) + + for _, tc := range testCases { + s.Run(tc.name, func() { + contract := vm.NewContract(vm.AccountRef(s.Address), s.precompile, big.NewInt(0), tc.gas) + + bz, err := s.precompile.GetOperatorOptedUSDValue(s.Ctx, contract, &method, tc.malleate()) if tc.expErr { s.Require().Error(err) diff --git a/precompiles/avs/setup_test.go b/precompiles/avs/setup_test.go index c47cb2192..ea02896b5 100644 --- a/precompiles/avs/setup_test.go +++ b/precompiles/avs/setup_test.go @@ -1,6 +1,9 @@ package avs_test import ( + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" "testing" "github.com/ExocoreNetwork/exocore/precompiles/avs" @@ -16,6 +19,17 @@ var s *AVSManagerPrecompileSuite type AVSManagerPrecompileSuite struct { testutil.BaseTestSuite precompile *avs.Precompile + // needed by test + operatorAddr sdk.AccAddress + avsAddr string + assetID string + stakerID string + assetAddr common.Address + assetDecimal uint32 + clientChainLzID uint64 + depositAmount sdkmath.Int + delegationAmount sdkmath.Int + updatedAmountForOptIn sdkmath.Int } func TestPrecompileTestSuite(t *testing.T) { @@ -27,9 +41,9 @@ func TestPrecompileTestSuite(t *testing.T) { RunSpecs(t, "AVSManager Precompile Suite") } -func (s *AVSManagerPrecompileSuite) SetupTest() { - s.DoSetupTest() - precompile, err := avs.NewPrecompile(s.App.AVSManagerKeeper, s.App.AuthzKeeper) - s.Require().NoError(err) - s.precompile = precompile +func (suite *AVSManagerPrecompileSuite) SetupTest() { + suite.DoSetupTest() + precompile, err := avs.NewPrecompile(suite.App.AVSManagerKeeper, suite.App.AuthzKeeper) + suite.Require().NoError(err) + suite.precompile = precompile } diff --git a/precompiles/avs/utils_test.go b/precompiles/avs/utils_test.go new file mode 100644 index 000000000..5bbbaceaa --- /dev/null +++ b/precompiles/avs/utils_test.go @@ -0,0 +1,207 @@ +package avs_test + +import ( + "fmt" + "strings" + "time" + + assetskeeper "github.com/ExocoreNetwork/exocore/x/assets/keeper" + avskeeper "github.com/ExocoreNetwork/exocore/x/avs/keeper" + avstypes "github.com/ExocoreNetwork/exocore/x/avs/types" + epochstypes "github.com/ExocoreNetwork/exocore/x/epochs/types" + + sdkmath "cosmossdk.io/math" + assetstypes "github.com/ExocoreNetwork/exocore/x/assets/types" + delegationtype "github.com/ExocoreNetwork/exocore/x/delegation/types" + operatorKeeper "github.com/ExocoreNetwork/exocore/x/operator/keeper" + operatorTypes "github.com/ExocoreNetwork/exocore/x/operator/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" +) + +type StateForCheck struct { + OptedInfo *operatorTypes.OptedInfo + AVSTotalShare sdkmath.LegacyDec + AVSOperatorShare sdkmath.LegacyDec + AssetState *operatorTypes.OptedInAssetState + OperatorShare sdkmath.LegacyDec + StakerShare sdkmath.LegacyDec +} + +func (suite *AVSManagerPrecompileSuite) prepareOperator() { + opAccAddr, err := sdk.AccAddressFromBech32("exo13h6xg79g82e2g2vhjwg7j4r2z2hlncelwutkjr") + suite.operatorAddr = opAccAddr + suite.NoError(err) + // register operator + registerReq := &operatorTypes.RegisterOperatorReq{ + FromAddress: suite.operatorAddr.String(), + Info: &operatorTypes.OperatorInfo{ + EarningsAddr: suite.operatorAddr.String(), + }, + } + _, err = s.OperatorMsgServer.RegisterOperator(s.Ctx, registerReq) + suite.NoError(err) +} + +func (suite *AVSManagerPrecompileSuite) prepareDeposit(assetAddr common.Address, amount sdkmath.Int) { + clientChainLzID := uint64(101) + suite.avsAddr = common.BytesToAddress([]byte("avsTestAddr")).String() + suite.assetAddr = assetAddr + suite.assetDecimal = 6 + suite.clientChainLzID = clientChainLzID + suite.depositAmount = amount + suite.updatedAmountForOptIn = sdkmath.NewInt(20) + suite.stakerID, suite.assetID = assetstypes.GetStakeIDAndAssetID(suite.clientChainLzID, suite.Address[:], suite.assetAddr[:]) + // staking assets + depositParam := &assetskeeper.DepositWithdrawParams{ + ClientChainLzID: suite.clientChainLzID, + Action: assetstypes.Deposit, + StakerAddress: suite.Address[:], + OpAmount: suite.depositAmount, + AssetsAddress: assetAddr[:], + } + err := suite.App.AssetsKeeper.PerformDepositOrWithdraw(suite.Ctx, depositParam) + suite.NoError(err) +} + +func (suite *AVSManagerPrecompileSuite) prepareDelegation(isDelegation bool, assetAddr common.Address, amount sdkmath.Int) { + suite.delegationAmount = amount + param := &delegationtype.DelegationOrUndelegationParams{ + ClientChainID: suite.clientChainLzID, + AssetsAddress: assetAddr[:], + OperatorAddress: suite.operatorAddr, + StakerAddress: suite.Address[:], + OpAmount: amount, + LzNonce: 0, + TxHash: common.HexToHash("0x24c4a315d757249c12a7a1d7b6fb96261d49deee26f06a3e1787d008b445c3ac"), + } + var err error + if isDelegation { + err = suite.App.DelegationKeeper.DelegateTo(suite.Ctx, param) + } else { + err = suite.App.DelegationKeeper.UndelegateFrom(suite.Ctx, param) + } + suite.NoError(err) +} + +func (suite *AVSManagerPrecompileSuite) prepare() { + usdtAddress := common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7") + depositAmount := sdkmath.NewInt(100) + delegationAmount := sdkmath.NewInt(50) + suite.prepareOperator() + suite.prepareDeposit(usdtAddress, depositAmount) + suite.prepareDelegation(true, usdtAddress, delegationAmount) +} + +func (suite *AVSManagerPrecompileSuite) prepareAvs(assetIDs []string) { + err := suite.App.AVSManagerKeeper.AVSInfoUpdate(suite.Ctx, &avstypes.AVSRegisterOrDeregisterParams{ + Action: avskeeper.RegisterAction, + EpochIdentifier: epochstypes.HourEpochID, + AvsAddress: suite.avsAddr, + AssetID: assetIDs, + }) + suite.NoError(err) +} + +func (suite *AVSManagerPrecompileSuite) CheckState(expectedState *StateForCheck) { + // check opted info + optInfo, err := suite.App.OperatorKeeper.GetOptedInfo(suite.Ctx, suite.operatorAddr.String(), suite.avsAddr) + if expectedState.OptedInfo == nil { + suite.True(strings.Contains(err.Error(), operatorTypes.ErrNoKeyInTheStore.Error())) + } else { + suite.NoError(err) + suite.Equal(*expectedState.OptedInfo, *optInfo) + } + // check total USD value for AVS and operator + value, err := suite.App.OperatorKeeper.GetAVSUSDValue(suite.Ctx, suite.avsAddr) + if expectedState.AVSTotalShare.IsNil() { + suite.True(strings.Contains(err.Error(), operatorTypes.ErrNoKeyInTheStore.Error())) + } else { + suite.NoError(err) + suite.Equal(expectedState.AVSTotalShare, value) + } + + optedUSDValues, err := suite.App.OperatorKeeper.GetOperatorOptedUSDValue(suite.Ctx, suite.avsAddr, suite.operatorAddr.String()) + if expectedState.AVSOperatorShare.IsNil() { + fmt.Println("the err is:", err) + suite.True(strings.Contains(err.Error(), operatorTypes.ErrNoKeyInTheStore.Error())) + } else { + suite.NoError(err) + suite.Equal(expectedState.AVSOperatorShare, optedUSDValues.TotalUSDValue) + } +} + +func (suite *AVSManagerPrecompileSuite) TestOptIn() { + suite.prepare() + suite.prepareAvs([]string{"0xdac17f958d2ee523a2206206994597c13d831ec7_0x65"}) + err := suite.App.OperatorKeeper.OptIn(suite.Ctx, suite.operatorAddr, suite.avsAddr) + suite.NoError(err) + // check if the related state is correct + price, err := suite.App.OperatorKeeper.OracleInterface().GetSpecifiedAssetsPrice(suite.Ctx, suite.assetID) + suite.NoError(err) + usdValue := operatorKeeper.CalculateUSDValue(suite.delegationAmount, price.Value, suite.assetDecimal, price.Decimal) + expectedState := &StateForCheck{ + OptedInfo: &operatorTypes.OptedInfo{ + OptedInHeight: uint64(suite.Ctx.BlockHeight()), + OptedOutHeight: operatorTypes.DefaultOptedOutHeight, + }, + AVSTotalShare: usdValue, + AVSOperatorShare: usdValue, + AssetState: &operatorTypes.OptedInAssetState{ + Amount: suite.delegationAmount, + Value: usdValue, + }, + OperatorShare: sdkmath.LegacyDec{}, + StakerShare: usdValue, + } + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + suite.CommitAfter(time.Hour*2 + time.Nanosecond) + suite.CheckState(expectedState) +} + +func (suite *AVSManagerPrecompileSuite) TestOptInList() { + suite.prepare() + suite.prepareAvs([]string{"0xdac17f958d2ee523a2206206994597c13d831ec7_0x65"}) + err := suite.App.OperatorKeeper.OptIn(suite.Ctx, suite.operatorAddr, suite.avsAddr) + suite.NoError(err) + // check if the related state is correct + operatorList, err := suite.App.OperatorKeeper.GetOptedInOperatorListByAVS(suite.Ctx, suite.avsAddr) + suite.NoError(err) + suite.Contains(operatorList, suite.operatorAddr.String()) + + avsList, err := suite.App.OperatorKeeper.GetOptedInAVSForOperator(suite.Ctx, suite.operatorAddr.String()) + suite.NoError(err) + + suite.Contains(avsList, suite.avsAddr) + +} + +func (suite *AVSManagerPrecompileSuite) TestOptOut() { + suite.prepare() + suite.prepareAvs([]string{"0xdac17f958d2ee523a2206206994597c13d831ec7_0x65"}) + err := suite.App.OperatorKeeper.OptOut(suite.Ctx, suite.operatorAddr, suite.avsAddr) + suite.EqualError(err, operatorTypes.ErrNotOptedIn.Error()) + + err = suite.App.OperatorKeeper.OptIn(suite.Ctx, suite.operatorAddr, suite.avsAddr) + suite.NoError(err) + optInHeight := suite.Ctx.BlockHeight() + suite.NextBlock() + + err = suite.App.OperatorKeeper.OptOut(suite.Ctx, suite.operatorAddr, suite.avsAddr) + suite.NoError(err) + + expectedState := &StateForCheck{ + OptedInfo: &operatorTypes.OptedInfo{ + OptedInHeight: uint64(optInHeight), + OptedOutHeight: uint64(suite.Ctx.BlockHeight()), + }, + AVSTotalShare: sdkmath.LegacyNewDec(0), + AVSOperatorShare: sdkmath.LegacyNewDec(0), + AssetState: nil, + OperatorShare: sdkmath.LegacyDec{}, + StakerShare: sdkmath.LegacyDec{}, + } + suite.CommitAfter(time.Hour*1 + time.Nanosecond) + suite.CommitAfter(time.Hour*2 + time.Nanosecond) + suite.CheckState(expectedState) +} diff --git a/precompiles/testutil/logs.go b/precompiles/testutil/logs.go index ae6356483..1f587f80a 100644 --- a/precompiles/testutil/logs.go +++ b/precompiles/testutil/logs.go @@ -37,7 +37,7 @@ func CheckLogs(logArgs LogCheckArgs) error { int64(float64(logArgs.Res.GasUsed)/float64(logArgs.Res.GasWanted)*100), ) } - + // nolint if err := CheckVMError(logArgs.Res, logArgs.ErrContains); err != nil { return err } diff --git a/testutil/abci.go b/testutil/abci.go index 368446e31..7c8bccbf8 100644 --- a/testutil/abci.go +++ b/testutil/abci.go @@ -3,7 +3,6 @@ package testutil import ( "time" - errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" abci "github.com/cometbft/cometbft/abci/types" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" @@ -191,7 +190,7 @@ func BroadcastTxBytes(app *app.ExocoreApp, txEncoder sdk.TxEncoder, tx sdk.Tx) ( req := abci.RequestDeliverTx{Tx: bz} res := app.BaseApp.DeliverTx(req) if res.Code != 0 { - return abci.ResponseDeliverTx{}, errorsmod.Wrapf(errortypes.ErrInvalidRequest, res.Log) + return abci.ResponseDeliverTx{}, errortypes.ErrInvalidRequest.Wrap(res.Log) } return res, nil @@ -238,7 +237,7 @@ func checkTxBytes(app *app.ExocoreApp, txEncoder sdk.TxEncoder, tx sdk.Tx) (abci req := abci.RequestCheckTx{Tx: bz} res := app.BaseApp.CheckTx(req) if res.Code != 0 { - return abci.ResponseCheckTx{}, errorsmod.Wrapf(errortypes.ErrInvalidRequest, res.Log) + return abci.ResponseCheckTx{}, errortypes.ErrInvalidRequest.Wrap(res.Log) } return res, nil diff --git a/x/avs/keeper/keeper.go b/x/avs/keeper/keeper.go index 9db3b68b0..9f4a549ff 100644 --- a/x/avs/keeper/keeper.go +++ b/x/avs/keeper/keeper.go @@ -52,6 +52,11 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } +// GetOperatorKeeper returns the operatorKeeper from the Keeper struct. +func (k Keeper) GetOperatorKeeper() types.OperatorKeeper { + return k.operatorKeeper +} + func (k Keeper) AVSInfoUpdate(ctx sdk.Context, params *types.AVSRegisterOrDeregisterParams) error { avsInfo, _ := k.GetAVSInfo(ctx, params.AvsAddress) action := params.Action diff --git a/x/avs/types/expected_keepers.go b/x/avs/types/expected_keepers.go index b6a020ad0..b0c69e1fd 100644 --- a/x/avs/types/expected_keepers.go +++ b/x/avs/types/expected_keepers.go @@ -1,8 +1,10 @@ package types import ( + sdkmath "cosmossdk.io/math" assetstype "github.com/ExocoreNetwork/exocore/x/assets/types" epochstypes "github.com/ExocoreNetwork/exocore/x/epochs/types" + operatortypes "github.com/ExocoreNetwork/exocore/x/operator/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/ethereum/go-ethereum/common" @@ -38,6 +40,8 @@ type OperatorKeeper interface { OptIn(ctx sdk.Context, operatorAddress sdk.AccAddress, avsAddr string) error OptOut(ctx sdk.Context, operatorAddress sdk.AccAddress, avsAddr string) (err error) GetOptedInOperatorListByAVS(ctx sdk.Context, avsAddr string) ([]string, error) + GetOperatorOptedUSDValue(ctx sdk.Context, avsAddr, operatorAddr string) (operatortypes.OperatorOptedUSDValue, error) + GetAVSUSDValue(ctx sdk.Context, avsAddr string) (sdkmath.LegacyDec, error) } // AssetsKeeper represents the expected keeper interface for the assets module. diff --git a/x/oracle/keeper/aggregator/context.go b/x/oracle/keeper/aggregator/context.go index d4b0f4de0..dc42f8288 100644 --- a/x/oracle/keeper/aggregator/context.go +++ b/x/oracle/keeper/aggregator/context.go @@ -90,12 +90,12 @@ func (agc *AggregatorContext) sanityCheck(msg *types.MsgCreatePrice) error { } // TODO: sanity check for price(no more than maxDetId count for each source, this should be take care in anteHandler) - if msg.Prices == nil || len(msg.Prices) == 0 { + if len(msg.Prices) == 0 { return errors.New("msg should provide at least one price") } for _, pSource := range msg.Prices { - if pSource.Prices == nil || len(pSource.Prices) == 0 || len(pSource.Prices) > int(common.MaxDetID) || !agc.params.IsValidSource(pSource.SourceID) { + if len(pSource.Prices) == 0 || len(pSource.Prices) > int(common.MaxDetID) || !agc.params.IsValidSource(pSource.SourceID) { return errors.New("source should be valid and provide at least one price") } // check with params is coressponding source is deteministic diff --git a/x/oracle/types/params.go b/x/oracle/types/params.go index 7e7368a05..eb6b4c70b 100644 --- a/x/oracle/types/params.go +++ b/x/oracle/types/params.go @@ -525,7 +525,7 @@ func (p Params) CheckRules(feederID uint64, prices []*PriceSource) (bool, error) feeder := p.TokenFeeders[feederID] rule := p.Rules[feeder.RuleID] // specified sources set, v1 use this rule to set `chainlink` as official source - if rule.SourceIDs != nil && len(rule.SourceIDs) > 0 { + if len(rule.SourceIDs) > 0 { if len(rule.SourceIDs) != len(prices) { return false, errors.New("count prices should match rule") }