-
Notifications
You must be signed in to change notification settings - Fork 193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(app): Make post price txs gasless #817
Merged
Merged
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
6f63ea2
feat: Gasless txs for post price and liquidate
caa2add
fix: Fix anteHandler call to include keepers
e452d55
fix: Update changelog and lint fix
b2a32ae
Merge remote-tracking branch 'origin/master' into mat/make-oracle-liq…
21f1eed
fix: merge with master
471efa5
fix,docs: vpool gov cli test
Unique-Divine c7717db
docs(vpool,pricefeed): readadbility and cli commenets for the gov cli…
Unique-Divine b69a432
fix: check for error on chainedhandler
e816bad
wip: Update the fees to 0 in cli test for pricefeed
1351de1
Merge branch 'master' into mat/make-oracle-liquidate-gasless
AgentSmithMatrix 6c6a5f6
feat: Fork fee deduct ante-handler
matthiasmatt 9246d6b
Merge remote-tracking branch 'origin/master' into mat/make-oracle-liq…
matthiasmatt 03f1bb8
fix: Remove unused function
matthiasmatt 3a36e26
tests: Add test for gas or no gas in post price cli test
matthiasmatt 9901125
feat: Update changelog
matthiasmatt a20fb30
fix: Remove perpkeeper from gasless ante
matthiasmatt 903c44f
fix: Remove println from codebase
matthiasmatt 4c9a9e4
test gasmeter infinite
AgentSmithMatrix 1ab48a0
change test for gassless decorator
AgentSmithMatrix b56cd3a
update gasless test
AgentSmithMatrix 06ab94a
remove unused code
AgentSmithMatrix 3ef21a2
fix comment
AgentSmithMatrix 681ab28
remove unused code
AgentSmithMatrix 5614d37
linter
AgentSmithMatrix af767cd
linter 2
AgentSmithMatrix 48511f1
final clean
AgentSmithMatrix 1a5d14d
Merge remote-tracking branch 'origin/master' into mat/make-oracle-liq…
AgentSmithMatrix 386d344
remove unused func
AgentSmithMatrix 12594ab
remove comment
AgentSmithMatrix 9eac5e1
Merge branch 'master' into mat/make-oracle-liquidate-gasless
AgentSmithMatrix 598e5f3
Merge branch 'master' into mat/make-oracle-liquidate-gasless
matthiasmatt d9eade7
fix: Fix logic on pricefeed query tests for cli_test
matthiasmatt 0da7c22
test: Add test for simulation run
matthiasmatt edfebca
fix: Remove nil check for address of variable
matthiasmatt 74e36d2
Merge branch 'master' into mat/make-oracle-liquidate-gasless
AgentSmithMatrix 40fba6c
Merge branch 'master' into mat/make-oracle-liquidate-gasless
matthiasmatt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package fee | ||
|
||
import ( | ||
"fmt" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" | ||
"github.com/cosmos/cosmos-sdk/x/auth/ante" | ||
"github.com/cosmos/cosmos-sdk/x/auth/types" | ||
) | ||
|
||
// DeductFeeDecorator deducts fees from the first signer of the tx | ||
// If the first signer does not have the funds to pay for the fees, return with InsufficientFunds error | ||
// Call next AnteHandler if fees successfully deducted | ||
// CONTRACT: Tx must implement FeeTx interface to use DeductFeeDecorator | ||
type DeductFeeDecorator struct { | ||
ak ante.AccountKeeper | ||
bankKeeper types.BankKeeper | ||
feegrantKeeper ante.FeegrantKeeper | ||
} | ||
|
||
func NewDeductFeeDecorator(ak ante.AccountKeeper, bk types.BankKeeper, fk ante.FeegrantKeeper) DeductFeeDecorator { | ||
return DeductFeeDecorator{ | ||
ak: ak, | ||
bankKeeper: bk, | ||
feegrantKeeper: fk, | ||
} | ||
} | ||
|
||
func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { | ||
feeTx, ok := tx.(sdk.FeeTx) | ||
if !ok { | ||
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") | ||
} | ||
|
||
if addr := dfd.ak.GetModuleAddress(types.FeeCollectorName); addr == nil { | ||
return ctx, fmt.Errorf("fee collector module account (%s) has not been set", types.FeeCollectorName) | ||
} | ||
|
||
fee := feeTx.GetFee() | ||
feePayer := feeTx.FeePayer() | ||
feeGranter := feeTx.FeeGranter() | ||
|
||
deductFeesFrom := feePayer | ||
|
||
// if feegranter set deduct fee from feegranter account. | ||
// this works with only when feegrant enabled. | ||
if feeGranter != nil { | ||
if dfd.feegrantKeeper == nil { | ||
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "fee grants are not enabled") | ||
} else if !feeGranter.Equals(feePayer) { | ||
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, tx.GetMsgs()) | ||
|
||
if err != nil { | ||
return ctx, sdkerrors.Wrapf(err, "%s not allowed to pay fees from %s", feeGranter, feePayer) | ||
} | ||
} | ||
|
||
deductFeesFrom = feeGranter | ||
} | ||
|
||
deductFeesFromAcc := dfd.ak.GetAccount(ctx, deductFeesFrom) | ||
if deductFeesFromAcc == nil { | ||
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "fee payer address: %s does not exist", deductFeesFrom) | ||
} | ||
|
||
// Gas meter is set to 1 for gasless transactions. | ||
// Depends on GasLessDecorator for this to happen. | ||
if ctx.GasMeter().GasConsumed() == 1 { | ||
// do nothing | ||
} else if !feeTx.GetFee().IsZero() { | ||
err = ante.DeductFees(dfd.bankKeeper, ctx, deductFeesFromAcc, feeTx.GetFee()) | ||
if err != nil { | ||
return ctx, err | ||
} | ||
} | ||
|
||
events := sdk.Events{sdk.NewEvent(sdk.EventTypeTx, | ||
sdk.NewAttribute(sdk.AttributeKeyFee, feeTx.GetFee().String()), | ||
)} | ||
ctx.EventManager().EmitEvents(events) | ||
|
||
return next(ctx, tx, simulate) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package fee_test | ||
|
||
import ( | ||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" | ||
"github.com/cosmos/cosmos-sdk/simapp" | ||
"github.com/cosmos/cosmos-sdk/testutil/testdata" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
"github.com/NibiruChain/nibiru/app" | ||
"github.com/NibiruChain/nibiru/app/antedecorators/fee" | ||
) | ||
|
||
func (suite *AnteTestSuite) TestDeductFees() { | ||
suite.SetupTest(false) // setup | ||
suite.txBuilder = suite.clientCtx.TxConfig.NewTxBuilder() | ||
|
||
// keys and addresses | ||
priv1, _, addr1 := testdata.KeyTestPubAddr() | ||
|
||
// msg and signatures | ||
msg := testdata.NewTestMsg(addr1) | ||
feeAmount := sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 150)) | ||
gasLimit := testdata.NewTestGasLimit() | ||
suite.Require().NoError(suite.txBuilder.SetMsgs(msg)) | ||
suite.txBuilder.SetFeeAmount(feeAmount) | ||
suite.txBuilder.SetGasLimit(gasLimit) | ||
|
||
privs, accNums, accSeqs := []cryptotypes.PrivKey{priv1}, []uint64{0}, []uint64{0} | ||
tx, err := suite.CreateTestTx(privs, accNums, accSeqs, suite.ctx.ChainID()) | ||
suite.Require().NoError(err) | ||
|
||
// Set account with insufficient funds | ||
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) | ||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc) | ||
coins := sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(10))) | ||
err = simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, coins) | ||
suite.Require().NoError(err) | ||
|
||
dfd := fee.NewDeductFeeDecorator(suite.app.AccountKeeper, suite.app.BankKeeper, nil) | ||
antehandler := sdk.ChainAnteDecorators(dfd) | ||
|
||
_, err = antehandler(suite.ctx, tx, false) | ||
|
||
suite.Require().NotNil(err, "Tx did not error when fee payer had insufficient funds") | ||
|
||
// Set account with sufficient funds | ||
suite.app.AccountKeeper.SetAccount(suite.ctx, acc) | ||
err = simapp.FundAccount(suite.app.BankKeeper, suite.ctx, addr1, sdk.NewCoins(sdk.NewCoin(app.BondDenom, sdk.NewInt(200)))) | ||
suite.Require().NoError(err) | ||
|
||
_, err = antehandler(suite.ctx, tx, false) | ||
|
||
suite.Require().Nil(err, "Tx errored after account has been set with sufficient funds") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package fee_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/client/tx" | ||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" | ||
"github.com/cosmos/cosmos-sdk/testutil/testdata" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/tx/signing" | ||
"github.com/cosmos/cosmos-sdk/x/auth/ante" | ||
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" | ||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" | ||
"github.com/stretchr/testify/suite" | ||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" | ||
|
||
"github.com/NibiruChain/nibiru/app" | ||
"github.com/NibiruChain/nibiru/x/testutil/testapp" | ||
) | ||
|
||
// AnteTestSuite is a test suite to be used with ante handler tests. | ||
type AnteTestSuite struct { | ||
suite.Suite | ||
|
||
app *app.NibiruApp | ||
anteHandler sdk.AnteHandler | ||
ctx sdk.Context | ||
clientCtx client.Context | ||
txBuilder client.TxBuilder | ||
} | ||
|
||
// returns context and app with params set on account keeper | ||
func createTestApp(isCheckTx bool) (*app.NibiruApp, sdk.Context) { | ||
app := testapp.NewNibiruApp(true) | ||
ctx := app.BaseApp.NewContext(isCheckTx, tmproto.Header{}) | ||
app.AccountKeeper.SetParams(ctx, authtypes.DefaultParams()) | ||
|
||
return app, ctx | ||
} | ||
|
||
// SetupTest setups a new test, with new app, context, and anteHandler. | ||
func (suite *AnteTestSuite) SetupTest(isCheckTx bool) { | ||
suite.app, suite.ctx = createTestApp(isCheckTx) | ||
suite.ctx = suite.ctx.WithBlockHeight(1) | ||
|
||
// Set up TxConfig. | ||
encodingConfig := app.MakeTestEncodingConfig() | ||
// We're using TestMsg encoding in some tests, so register it here. | ||
encodingConfig.Amino.RegisterConcrete(&testdata.TestMsg{}, "testdata.TestMsg", nil) | ||
testdata.RegisterInterfaces(encodingConfig.InterfaceRegistry) | ||
|
||
suite.clientCtx = client.Context{}. | ||
WithTxConfig(encodingConfig.TxConfig) | ||
|
||
anteHandler, err := ante.NewAnteHandler( | ||
ante.HandlerOptions{ | ||
AccountKeeper: suite.app.AccountKeeper, | ||
BankKeeper: suite.app.BankKeeper, | ||
FeegrantKeeper: suite.app.FeeGrantKeeper, | ||
SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), | ||
SigGasConsumer: ante.DefaultSigVerificationGasConsumer, | ||
}, | ||
) | ||
|
||
suite.Require().NoError(err) | ||
suite.anteHandler = anteHandler | ||
} | ||
|
||
// CreateTestTx is a helper function to create a tx given multiple inputs. | ||
func (suite *AnteTestSuite) CreateTestTx(privs []cryptotypes.PrivKey, accNums []uint64, accSeqs []uint64, chainID string) (xauthsigning.Tx, error) { | ||
// First round: we gather all the signer infos. We use the "set empty | ||
// signature" hack to do that. | ||
var sigsV2 []signing.SignatureV2 | ||
for i, priv := range privs { | ||
sigV2 := signing.SignatureV2{ | ||
PubKey: priv.PubKey(), | ||
Data: &signing.SingleSignatureData{ | ||
SignMode: suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), | ||
Signature: nil, | ||
}, | ||
Sequence: accSeqs[i], | ||
} | ||
|
||
sigsV2 = append(sigsV2, sigV2) | ||
} | ||
err := suite.txBuilder.SetSignatures(sigsV2...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Second round: all signer infos are set, so each signer can sign. | ||
sigsV2 = []signing.SignatureV2{} | ||
for i, priv := range privs { | ||
signerData := xauthsigning.SignerData{ | ||
ChainID: chainID, | ||
AccountNumber: accNums[i], | ||
Sequence: accSeqs[i], | ||
} | ||
sigV2, err := tx.SignWithPrivKey( | ||
suite.clientCtx.TxConfig.SignModeHandler().DefaultMode(), signerData, | ||
suite.txBuilder, priv, suite.clientCtx.TxConfig, accSeqs[i]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
sigsV2 = append(sigsV2, sigV2) | ||
} | ||
err = suite.txBuilder.SetSignatures(sigsV2...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return suite.txBuilder.GetTx(), nil | ||
} | ||
|
||
func TestAnteTestSuite(t *testing.T) { | ||
suite.Run(t, new(AnteTestSuite)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package gasless | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
types "github.com/NibiruChain/nibiru/app/antedecorators/types" | ||
"github.com/NibiruChain/nibiru/x/common" | ||
pricefeedkeeper "github.com/NibiruChain/nibiru/x/pricefeed/keeper" | ||
pricefeedtypes "github.com/NibiruChain/nibiru/x/pricefeed/types" | ||
) | ||
|
||
type GaslessDecorator struct { | ||
pricefeedKeeper pricefeedkeeper.Keeper | ||
} | ||
|
||
func NewGaslessDecorator(pricefeedKeeper pricefeedkeeper.Keeper) GaslessDecorator { | ||
return GaslessDecorator{pricefeedKeeper: pricefeedKeeper} | ||
} | ||
|
||
func (gd GaslessDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { | ||
if simulate || !isTxGasless(tx, ctx, gd.pricefeedKeeper) { | ||
return next(ctx, tx, simulate) | ||
} | ||
|
||
gaslessMeter := types.GasLessMeter() | ||
return next(ctx.WithGasMeter(gaslessMeter), tx, simulate) | ||
} | ||
|
||
func isTxGasless(tx sdk.Tx, ctx sdk.Context, pricefeedKeeper pricefeedkeeper.Keeper) bool { | ||
if len(tx.GetMsgs()) == 0 { | ||
// empty TX shouldn't be gasless | ||
return false | ||
} | ||
for _, msg := range tx.GetMsgs() { | ||
switch m := msg.(type) { | ||
case *pricefeedtypes.MsgPostPrice: | ||
if pricefeedPostPriceIsGasless(m, ctx, pricefeedKeeper) { | ||
continue | ||
} | ||
return false | ||
default: | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// Check if the sender is a whitelisted oracle | ||
func pricefeedPostPriceIsGasless(msg *pricefeedtypes.MsgPostPrice, ctx sdk.Context, keeper pricefeedkeeper.Keeper) bool { | ||
valAddr, err := sdk.AccAddressFromBech32(msg.Oracle) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
pair := common.AssetPair{Token0: msg.Token0, Token1: msg.Token1} | ||
return keeper.IsWhitelistedOracle(ctx, pair.String(), valAddr) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of creating our own custom
DeductFeeDecorator
, what if we over-write thet.AuthInfo.Fee.Amount
inside ofGaslessDecorator
so that the defaultDeductFeeDecorator
takes zero coins away from the oracle?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of that behavior in the fee.go ante:
if you provide 0 coins for required fees, it will fail with:
insufficient fees; got: 0 required: 0