Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Archive Node Online Migration #1863

Merged
merged 31 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e58f02a
Archive Node Online Migration
Kbhat1 Sep 18, 2024
27a1676
Add migrate-iavl flag for online migration
Kbhat1 Sep 23, 2024
c28c783
Update sei cosmos
Kbhat1 Sep 25, 2024
3ac501f
Add QMS for online migration (#1870)
yzang2019 Sep 25, 2024
30a8311
Add migrate-height flag to start cmd
Kbhat1 Sep 25, 2024
e75b222
Bump seidb
Kbhat1 Oct 7, 2024
fa3c6b6
Reduce logging
Kbhat1 Oct 9, 2024
f5defc1
Archive Migration doc
Kbhat1 Oct 16, 2024
0cc3b81
Update form
Kbhat1 Oct 16, 2024
241632b
Update
Kbhat1 Oct 16, 2024
387c96d
Export metric
Kbhat1 Oct 16, 2024
a586dbc
latest height
Kbhat1 Oct 16, 2024
0b9647b
Update start module logic
Kbhat1 Oct 27, 2024
8dc5c9d
Remove migrate SS command
Kbhat1 Oct 28, 2024
df491b9
Enable ss store
Kbhat1 Oct 28, 2024
1f5ec16
Update archive node migration docs
Kbhat1 Oct 28, 2024
e304dc0
More migration doc updates
Kbhat1 Oct 28, 2024
8f928f7
Update metrics in readme
Kbhat1 Oct 28, 2024
1acc2df
Merge main
Kbhat1 Oct 28, 2024
98ab03c
Remove dex
Kbhat1 Oct 28, 2024
9f17ea7
Merge branch 'main' into ArchiveNodeMigrationOnline
Kbhat1 Oct 29, 2024
95137f7
Add Faq section
Kbhat1 Oct 29, 2024
de06161
Add requirements section
Kbhat1 Oct 29, 2024
95b6762
Systemd instructions
Kbhat1 Oct 29, 2024
240ccb1
Minor add to readme
Kbhat1 Oct 29, 2024
3fea70e
Add more examples for migration height
Kbhat1 Oct 29, 2024
2e3376a
Bump sei cosmos
Kbhat1 Oct 29, 2024
e2714b5
Update error log
Kbhat1 Oct 30, 2024
58498de
Merge branch 'main' into ArchiveNodeMigrationOnline
Kbhat1 Oct 30, 2024
9a538af
Update command sc migration background
Kbhat1 Oct 30, 2024
ec3d810
Merge branch 'main' into ArchiveNodeMigrationOnline
Kbhat1 Oct 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ Ref: https://keepachangelog.com/en/1.0.0/
-->

# Changelog
## v5.9.0
sei-chain
* [#1867](https://github.com/sei-protocol/sei-chain/pull/1867) Add synthetic events in separate sei endpoints
* [#1861](https://github.com/sei-protocol/sei-chain/pull/1861) Revert showing wasm txs in EVM RPCs
* [#1857](https://github.com/sei-protocol/sei-chain/pull/1857) Fix events in 2-hop scenarios
* [#1856](https://github.com/sei-protocol/sei-chain/pull/1856) Add delegatecall flag to properly detect delegatecalls
* [#1850](https://github.com/sei-protocol/sei-chain/pull/1853) Fix websocket from_height
* [#1849](https://github.com/sei-protocol/sei-chain/pull/1849) Reduce block bloom storage
* [#1844](https://github.com/sei-protocol/sei-chain/pull/1844) Allowlist for token extensions

sei-iavl
*[#41](https://github.com/sei-protocol/sei-iavl/pull/41) Fix tree versions causing slow restart and OOM
## v5.8.0
sei-chain
* [#1840](https://github.com/sei-protocol/sei-chain/pull/1840) Add migration for new params
Expand Down
1 change: 1 addition & 0 deletions aclmapping/evm/mappings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func (suite *KeeperTestSuite) TestMsgEVMTransaction() {
ctx, err := suite.preprocessor.AnteHandle(handlerCtx, tx.GetTx(), false, func(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil })
suite.Require().Nil(err)
cms.ResetEvents()
suite.App.EvmKeeper.SetDynamicBaseFeePerGas(ctx, sdk.ZeroDec())
_, err = suite.msgServer.EVMTransaction(sdk.WrapSDKContext(ctx), tc.msg)
suite.Require().Nil(err)

Expand Down
16 changes: 14 additions & 2 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ type App struct {

genesisImportConfig genesistypes.GenesisImportConfig

stateStore seidb.StateStore
receiptStore seidb.StateStore
}

Expand Down Expand Up @@ -396,7 +397,7 @@ func New(
cdc := encodingConfig.Amino
interfaceRegistry := encodingConfig.InterfaceRegistry

bAppOptions := SetupSeiDB(logger, homePath, appOpts, baseAppOptions)
bAppOptions, stateStore := SetupSeiDB(logger, homePath, appOpts, baseAppOptions)

bApp := baseapp.NewBaseApp(AppName, logger, db, encodingConfig.TxConfig.TxDecoder(), tmConfig, appOpts, bAppOptions...)
bApp.SetCommitMultiStoreTracer(traceStore)
Expand Down Expand Up @@ -429,6 +430,7 @@ func New(
versionInfo: version.NewInfo(),
metricCounter: &map[string]float32{},
encodingConfig: encodingConfig,
stateStore: stateStore,
}

for _, option := range appOptions {
Expand Down Expand Up @@ -1064,6 +1066,9 @@ func (app *App) Name() string { return app.BaseApp.Name() }
// GetBaseApp returns the base app of the application
func (app App) GetBaseApp() *baseapp.BaseApp { return app.BaseApp }

// GetStateStore returns the state store of the application
func (app App) GetStateStore() seidb.StateStore { return app.stateStore }

// BeginBlocker application updates every begin block
func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
metrics.GaugeSeidVersionAndCommit(app.versionInfo.Version, app.versionInfo.GitCommit)
Expand Down Expand Up @@ -1599,8 +1604,15 @@ func (app *App) ProcessBlock(ctx sdk.Context, txs [][]byte, req BlockProcessRequ
lazyWriteEvents := app.BankKeeper.WriteDeferredBalances(ctx)
events = append(events, lazyWriteEvents...)

// Sum up total used per block
blockTotalGasUsed := int64(0)
for _, txResult := range txResults {
blockTotalGasUsed += txResult.GasUsed
}

endBlockResp := app.EndBlock(ctx, abci.RequestEndBlock{
Height: req.GetHeight(),
Height: req.GetHeight(),
BlockGasUsed: blockTotalGasUsed,
})

events = append(events, endBlockResp.Events...)
Expand Down
1 change: 1 addition & 0 deletions app/eth_replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ func BlockTest(a *App, bt *ethtests.BlockTest) {
}
params := a.EvmKeeper.GetParams(a.GetContextForDeliverTx([]byte{}))
params.MinimumFeePerGas = sdk.NewDecFromInt(sdk.NewInt(0))
// params.BaseFeePerGas = sdk.NewDecFromInt(sdk.NewInt(0))
a.EvmKeeper.SetParams(a.GetContextForDeliverTx([]byte{}), params)
}

Expand Down
8 changes: 3 additions & 5 deletions app/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,8 @@ type AllowanceResponse struct {
Expires json.RawMessage `json:"expires"`
}

func (app *App) AddCosmosEventsToEVMReceiptIfApplicable(ctx sdk.Context, tx sdk.Tx, checksum [32]byte, response abci.ResponseDeliverTx) {
if response.Code > 0 {
return
}
func (app *App) AddCosmosEventsToEVMReceiptIfApplicable(ctx sdk.Context, tx sdk.Tx, checksum [32]byte, response sdk.DeliverTxHookInput) {
// hooks will only be called if DeliverTx is successful
wasmEvents := GetEventsOfType(response, wasmtypes.WasmModuleEventType)
if len(wasmEvents) == 0 {
return
Expand Down Expand Up @@ -283,7 +281,7 @@ func (app *App) GetEvmAddressAttribute(ctx sdk.Context, event abci.Event, attrib
return EmptyHash
}

func GetEventsOfType(rdtx abci.ResponseDeliverTx, ty string) (res []abci.Event) {
func GetEventsOfType(rdtx sdk.DeliverTxHookInput, ty string) (res []abci.Event) {
for _, event := range rdtx.Events {
if event.Type == ty {
res = append(res, event)
Expand Down
18 changes: 14 additions & 4 deletions app/seidb.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/storev2/rootmulti"
"github.com/sei-protocol/sei-db/config"
seidb "github.com/sei-protocol/sei-db/ss/types"
"github.com/spf13/cast"
"github.com/tendermint/tendermint/libs/log"
)
Expand All @@ -33,18 +34,20 @@ const (

// Other configs
FlagSnapshotInterval = "state-sync.snapshot-interval"
FlagMigrateIAVL = "migrate-iavl"
FlagMigrateHeight = "migrate-height"
)

func SetupSeiDB(
logger log.Logger,
homePath string,
appOpts servertypes.AppOptions,
baseAppOptions []func(*baseapp.BaseApp),
) []func(*baseapp.BaseApp) {
) ([]func(*baseapp.BaseApp), seidb.StateStore) {
scEnabled := cast.ToBool(appOpts.Get(FlagSCEnable))
if !scEnabled {
logger.Info("SeiDB is disabled, falling back to IAVL")
return baseAppOptions
return baseAppOptions, nil
}
logger.Info("SeiDB SC is enabled, running node with StoreV2 commit store")
scConfig := parseSCConfigs(appOpts)
Expand All @@ -56,14 +59,21 @@ func SetupSeiDB(

// cms must be overridden before the other options, because they may use the cms,
// make sure the cms aren't be overridden by the other options later on.
cms := rootmulti.NewStore(homePath, logger, scConfig, ssConfig)
cms := rootmulti.NewStore(homePath, logger, scConfig, ssConfig, cast.ToBool(appOpts.Get("migrate-iavl")))
migrationEnabled := cast.ToBool(appOpts.Get(FlagMigrateIAVL))
migrationHeight := cast.ToInt64(appOpts.Get(FlagMigrateHeight))
baseAppOptions = append([]func(*baseapp.BaseApp){
func(baseApp *baseapp.BaseApp) {
if migrationEnabled {
originalCMS := baseApp.CommitMultiStore()
baseApp.SetQueryMultiStore(originalCMS)
baseApp.SetMigrationHeight(migrationHeight)
}
baseApp.SetCMS(cms)
},
}, baseAppOptions...)

return baseAppOptions
return baseAppOptions, cms.GetStateStore()
}

func parseSCConfigs(appOpts servertypes.AppOptions) config.StateCommitConfig {
Expand Down
20 changes: 20 additions & 0 deletions app/test_state_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,26 @@ func (s *InMemoryStateStore) RawImport(ch <-chan types.RawSnapshotNode) error {
return nil
}

func (s *InMemoryStateStore) SetLatestMigratedModule(module string) error {
// TODO: Add set call here
return nil
}

func (s *InMemoryStateStore) GetLatestMigratedModule() (string, error) {
// TODO: Add get call here
return "", nil
}

func (s *InMemoryStateStore) SetLatestMigratedKey(key []byte) error {
// TODO: Add set call here
return nil
}

func (s *InMemoryStateStore) GetLatestMigratedKey() ([]byte, error) {
// TODO: Add get call here
return nil, nil
}

func (s *InMemoryStateStore) Prune(version int64) error {
s.mu.Lock()
defer s.mu.Unlock()
Expand Down
1 change: 1 addition & 0 deletions app/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ var upgradesList = []string{
"v5.7.4",
"v5.7.5",
"v5.8.0",
"v5.9.0",
}

// if there is an override list, use that instead, for integration tests
Expand Down
20 changes: 19 additions & 1 deletion cmd/seid/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/sei-protocol/sei-chain/app/params"
"github.com/sei-protocol/sei-chain/evmrpc"
"github.com/sei-protocol/sei-chain/tools"
"github.com/sei-protocol/sei-chain/tools/migration/ss"
"github.com/sei-protocol/sei-chain/x/evm/blocktest"
"github.com/sei-protocol/sei-chain/x/evm/querier"
"github.com/sei-protocol/sei-chain/x/evm/replay"
Expand Down Expand Up @@ -220,6 +221,8 @@ func txCommand() *cobra.Command {

func addModuleInitFlags(startCmd *cobra.Command) {
crisis.AddModuleInitFlags(startCmd)
startCmd.Flags().Bool("migrate-iavl", false, "Run migration of IAVL data store to SeiDB State Store")
startCmd.Flags().Int64("migrate-height", 0, "Height at which to start the migration")
}

// newApp creates a new Cosmos SDK app
Expand Down Expand Up @@ -266,7 +269,7 @@ func newApp(
// This makes it such that the wasm VM gas converts to sdk gas at a 6.66x rate vs that of the previous multiplier
wasmGasRegisterConfig.GasMultiplier = 21_000_000

return app.New(
app := app.New(
logger,
db,
traceStore,
Expand Down Expand Up @@ -302,6 +305,21 @@ func newApp(
baseapp.SetSnapshotDirectory(cast.ToString(appOpts.Get(server.FlagStateSyncSnapshotDir))),
baseapp.SetOccEnabled(cast.ToBool(appOpts.Get(baseapp.FlagOccEnabled))),
)

// Start migration if --migrate flag is set
if cast.ToBool(appOpts.Get("migrate-iavl")) {
go func() {
homeDir := cast.ToString(appOpts.Get(flags.FlagHome))
stateStore := app.GetStateStore()
migrationHeight := cast.ToInt64(appOpts.Get("migrate-height"))
migrator := ss.NewMigrator(homeDir, db, stateStore)
if err := migrator.Migrate(migrationHeight, homeDir); err != nil {
panic(err)
}
}()
Comment on lines +311 to +319

Check notice

Code scanning / CodeQL

Spawning a Go routine Note

Spawning a Go routine may be a possible source of non-determinism
}

return app
}

// appExport creates a new simapp (optionally at a given height)
Expand Down
68 changes: 44 additions & 24 deletions contracts/test/ERC20toCW20PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,16 @@ describe("ERC20 to CW20 Pointer", function () {
});

describe("transfer()", function () {
it("should transfer", async function () {
it.only("should transfer", async function () {
let sender = accounts[0];
let recipient = accounts[1];

expect(await pointer.balanceOf(sender.evmAddress)).to.equal(balances.account0);
expect(await pointer.balanceOf(recipient.evmAddress)).to.equal(balances.account1);

const blockNumber = await ethers.provider.getBlockNumber();
const tx = await pointer.transfer(recipient.evmAddress, 1);
await tx.wait();
const receipt = await tx.wait();
const blockNumber = receipt.blockNumber;

expect(await pointer.balanceOf(sender.evmAddress)).to.equal(balances.account0-1);
expect(await pointer.balanceOf(recipient.evmAddress)).to.equal(balances.account1+1);
Expand All @@ -107,18 +107,38 @@ describe("ERC20 to CW20 Pointer", function () {
address: await pointer.getAddress(),
topics: [ethers.id("Transfer(address,address,uint256)")]
};
// send via eth_ endpoint - synthetic event doesn't show up
// send via eth_ endpoint - synthetic event should show up because we are using the
// synthetic event in place of a real EVM event
const ethlogs = await ethers.provider.send('eth_getLogs', [filter]);
expect(ethlogs.length).to.equal(0);
expect(ethlogs.length).to.equal(1);

// send via sei_ endpoint - synthetic event shows up
const seilogs = await ethers.provider.send('sei_getLogs', [filter]);
expect(seilogs.length).to.equal(1);
expect(seilogs.length).to.equal(1);
expect(seilogs[0]["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(seilogs[0]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(seilogs[0]["topics"][1].substring(26)).to.equal(sender.evmAddress.substring(2).toLowerCase());
expect(seilogs[0]["topics"][2].substring(26)).to.equal(recipient.evmAddress.substring(2).toLowerCase());

const logs = [...ethlogs, ...seilogs];
logs.forEach(async (log) => {
expect(log["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(log["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)"));
expect(log["topics"][1].substring(26)).to.equal(sender.evmAddress.substring(2).toLowerCase());
expect(log["topics"][2].substring(26)).to.equal(recipient.evmAddress.substring(2).toLowerCase());
});

const ethBlock = await ethers.provider.send('eth_getBlockByNumber', ['0x' + blockNumber.toString(16), false]);
const seiBlock = await ethers.provider.send('sei_getBlockByNumber', ['0x' + blockNumber.toString(16), false]);
expect(ethBlock.transactions.length).to.equal(1);
expect(seiBlock.transactions.length).to.equal(1);

const ethReceipts = await ethers.provider.send('eth_getBlockReceipts', ['0x' + blockNumber.toString(16)]);
const seiReceipts = await ethers.provider.send('sei_getBlockReceipts', ['0x' + blockNumber.toString(16)]);
expect(ethReceipts.length).to.equal(1);
expect(seiReceipts.length).to.equal(1);
expect(ethReceipts[0].transactionHash).to.equal(seiReceipts[0].transactionHash);

const ethTx = await ethers.provider.send('eth_getTransactionReceipt', [receipt.hash]);
expect(ethTx.logs.length).to.equal(1); // check for transfer event
const ethTxByHash = await ethers.provider.send('eth_getTransactionByHash', [tx.hash]);
expect(ethTxByHash).to.not.be.null;

const cleanupTx = await pointer.connect(recipient.signer).transfer(sender.evmAddress, 1);
await cleanupTx.wait();
Expand Down Expand Up @@ -147,7 +167,7 @@ describe("ERC20 to CW20 Pointer", function () {
const spender = accounts[1].evmAddress;
const blockNumber = await ethers.provider.getBlockNumber();
const tx = await pointer.approve(spender, 1000000);
await tx.wait();
const receipt = await tx.wait();
const allowance = await pointer.allowance(owner, spender);
expect(Number(allowance)).to.equal(1000000);

Expand All @@ -160,21 +180,21 @@ describe("ERC20 to CW20 Pointer", function () {
};
// send via eth_ endpoint - synthetic event doesn't show up
const ethlogs = await ethers.provider.send('eth_getLogs', [filter]);
expect(ethlogs.length).to.equal(0);
expect(ethlogs.length).to.equal(1);
expect(ethlogs[0]["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(ethlogs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)"));
expect(ethlogs[0]["topics"][1].substring(26)).to.equal(owner.substring(2).toLowerCase());
expect(ethlogs[0]["topics"][2].substring(26)).to.equal(spender.substring(2).toLowerCase());

// send via sei_ endpoint - synthetic event shows up
const seilogs = await ethers.provider.send('sei_getLogs', [filter]);
expect(seilogs.length).to.equal(1);
expect(seilogs[0]["address"].toLowerCase()).to.equal((await pointer.getAddress()).toLowerCase());
expect(seilogs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)"));
expect(seilogs[0]["topics"][1].substring(26)).to.equal(owner.substring(2).toLowerCase());
expect(seilogs[0]["topics"][2].substring(26)).to.equal(spender.substring(2).toLowerCase());
});

it("should lower approval", async function () {
const owner = accounts[0].evmAddress;
const spender = accounts[1].evmAddress;
const tx = await pointer.approve(spender, 0);
const tx = await pointer.approve(spender, 0, { gasPrice: ethers.parseUnits('100', 'gwei') });
await tx.wait();
const allowance = await pointer.allowance(owner, spender);
expect(Number(allowance)).to.equal(0);
Expand All @@ -184,21 +204,21 @@ describe("ERC20 to CW20 Pointer", function () {
const owner = accounts[0].evmAddress;
const spender = accounts[1].evmAddress;
const maxUint128 = new BigNumber("0xffffffffffffffffffffffffffffffff", 16);
const tx = await pointer.approve(spender, maxUint128.toFixed());
const tx = await pointer.approve(spender, maxUint128.toFixed(), { gasPrice: ethers.parseUnits('100', 'gwei') });
await tx.wait();
const allowance = await pointer.allowance(owner, spender);
expect(allowance).to.equal(maxUint128.toFixed());

// approving uint128 max int + 1 should work but only approve uint128
const maxUint128Plus1 = maxUint128.plus(1);
const tx128plus1 = await pointer.approve(spender, maxUint128Plus1.toFixed());
const tx128plus1 = await pointer.approve(spender, maxUint128Plus1.toFixed(), { gasPrice: ethers.parseUnits('100', 'gwei') });
await tx128plus1.wait();
const allowance128plus1 = await pointer.allowance(owner, spender);
expect(allowance128plus1).to.equal(maxUint128.toFixed());

// approving uint256 should also work but only approve uint128
const maxUint256 = new BigNumber("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
const tx256 = await pointer.approve(spender, maxUint256.toFixed());
const tx256 = await pointer.approve(spender, maxUint256.toFixed(), { gasPrice: ethers.parseUnits('100', 'gwei') });
await tx256.wait();
const allowance256 = await pointer.allowance(owner, spender);
expect(allowance256).to.equal(maxUint128.toFixed());
Expand All @@ -224,7 +244,7 @@ describe("ERC20 to CW20 Pointer", function () {
expect(Number(allowanceBefore)).to.be.greaterThanOrEqual(amountToTransfer);

// transfer
const tfTx = await pointer.connect(spender.signer).transferFrom(owner.evmAddress, recipient.evmAddress, amountToTransfer);
const tfTx = await pointer.connect(spender.signer).transferFrom(owner.evmAddress, recipient.evmAddress, amountToTransfer, { gasPrice: ethers.parseUnits('100', 'gwei') });
const receipt = await tfTx.wait();

// capture balances after
Expand Down Expand Up @@ -260,12 +280,12 @@ describe("ERC20 to CW20 Pointer", function () {
const owner = accounts[0];
const spender = accounts[1];

const tx = await pointer.approve(spender.evmAddress, 10);
const tx = await pointer.approve(spender.evmAddress, 10, { gasPrice: ethers.parseUnits('100', 'gwei') });
await tx.wait();

await expect(pointer.connect(spender.signer).transferFrom(owner.evmAddress, recipient.evmAddress, 20)).to.be.revertedWith("CosmWasm execute failed");
await expect(pointer.connect(spender.signer).transferFrom(owner.evmAddress, recipient.evmAddress, 20, { gasPrice: ethers.parseUnits('100', 'gwei') })).to.be.revertedWith("CosmWasm execute failed");
// put it back
await (await pointer.approve(spender.evmAddress, 0)).wait()
await (await pointer.approve(spender.evmAddress, 0, { gasPrice: ethers.parseUnits('100', 'gwei') })).wait()
});
});
});
Expand Down
Loading