diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db4ee456..81592e14 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.17.6 + go-version: 1.20.7 id: go - name: Check out code into the Go module directory diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 2f081d90..5b353d08 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.17.6 + go-version: 1.20.7 id: go - name: Check out code into the Go module directory diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml index 171b2a4e..6a8da390 100644 --- a/.github/workflows/create_release.yml +++ b/.github/workflows/create_release.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.17.6 + go-version: 1.20.7 id: go - name: Check out code into the Go module directory diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 2a02d2b9..9dbf4ef7 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -13,13 +13,13 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17.6 + go-version: 1.20.7 - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.45.2 + version: v1.53.2 # Optional: working directory, useful for monorepos # working-directory: somedir diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 3d7bb2e0..fd7a003a 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -8,13 +8,13 @@ on: jobs: test: - name: Unit + name: Unit & Integration runs-on: ubuntu-latest steps: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.17.6 + go-version: 1.20.7 id: go - name: Check out code diff --git a/.github/workflows/slow-tests.yml b/.github/workflows/slow-tests.yml new file mode 100644 index 00000000..90abc341 --- /dev/null +++ b/.github/workflows/slow-tests.yml @@ -0,0 +1,27 @@ +name: Tests + +on: + push: + branches: [ main, feat/* ] + pull_request: + branches: [ main, feat/* ] + +jobs: + test: + name: Slow + runs-on: ubuntu-latest + steps: + - name: Set up Go 1.x + uses: actions/setup-go@v3 + with: + go-version: 1.20.7 + id: go + + - name: Check out code + uses: actions/checkout@v3 + + - name: Get dependencies + run: | + go get -v -t -d ./... + - name: Slow tests + run: make slow-tests diff --git a/.gitignore b/.gitignore index 0cadc0fa..3e71f65d 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ /esdata .env *.log +# prevent accidental commit of key files +*.pem mytestnet/** diff --git a/Makefile b/Makefile index e13cdcc0..87345d8b 100644 --- a/Makefile +++ b/Makefile @@ -8,11 +8,7 @@ build-cmd: (cd cmd && go build) clean-test: - go clean -testcache ./... - -clean: clean-test - go clean -cache ./... - go clean ./... + go clean -testcache test: clean-test go test ./... @@ -21,6 +17,11 @@ test-coverage: @echo "Running unit tests" CURRENT_DIRECTORY=$(CURRENT_DIRECTORY) go test -cover -coverprofile=coverage.txt -covermode=atomic -v ${TESTS_TO_RUN} +slow-tests: clean-test + @docker compose -f docker/docker-compose.yml build + @docker compose -f docker/docker-compose.yml up & go test ./integrationTests/... -v -timeout 40m -tags slow + @docker compose -f docker/docker-compose.yml down -v + lint-install: ifeq (,$(wildcard test -f bin/golangci-lint)) @echo "Installing golint" diff --git a/api/gin/webServer.go b/api/gin/webServer.go index fe1e38b4..83159ebc 100644 --- a/api/gin/webServer.go +++ b/api/gin/webServer.go @@ -30,14 +30,14 @@ var log = logger.GetOrCreate("api") type ArgsNewWebServer struct { Facade shared.FacadeHandler ApiConfig config.ApiRoutesConfig - AntiFloodConfig config.AntifloodConfig + AntiFloodConfig config.WebAntifloodConfig } type webServer struct { sync.RWMutex facade shared.FacadeHandler apiConfig config.ApiRoutesConfig - antiFloodConfig config.AntifloodConfig + antiFloodConfig config.WebAntifloodConfig httpServer chainShared.HttpServerCloser groups map[string]shared.GroupHandler cancelFunc func() @@ -116,9 +116,9 @@ func (ws *webServer) StartHttpServer() error { ws.registerRoutes(engine) - server := &http.Server{Addr: ws.facade.RestApiInterface(), Handler: engine} + serverInstance := &http.Server{Addr: ws.facade.RestApiInterface(), Handler: engine} log.Debug("creating gin web sever", "interface", ws.facade.RestApiInterface()) - ws.httpServer, err = NewHttpServer(server) + ws.httpServer, err = NewHttpServer(serverInstance) if err != nil { return err } diff --git a/api/gin/webServer_test.go b/api/gin/webServer_test.go index 1938125d..c30b39e8 100644 --- a/api/gin/webServer_test.go +++ b/api/gin/webServer_test.go @@ -34,7 +34,7 @@ func createMockArgsNewWebServer() ArgsNewWebServer { }, APIPackages: make(map[string]config.APIPackageConfig), }, - AntiFloodConfig: config.AntifloodConfig{ + AntiFloodConfig: config.WebAntifloodConfig{ Enabled: true, WebServer: config.WebServerAntifloodConfig{ SimultaneousRequests: 1, diff --git a/bridges/ethMultiversX/bridgeExecutor.go b/bridges/ethMultiversX/bridgeExecutor.go index 2570dda8..52bf6483 100644 --- a/bridges/ethMultiversX/bridgeExecutor.go +++ b/bridges/ethMultiversX/bridgeExecutor.go @@ -2,12 +2,18 @@ package ethmultiversx import ( "context" + "encoding/binary" + "encoding/hex" "fmt" + "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" "github.com/multiversx/mx-chain-core-go/core/check" logger "github.com/multiversx/mx-chain-logger-go" ) @@ -15,7 +21,6 @@ import ( // splits - represent the number of times we split the maximum interval // we wait for the transfer confirmation on Ethereum const splits = 10 - const minRetries = 1 // ArgsBridgeExecutor is the arguments DTO struct used in both bridges @@ -27,7 +32,7 @@ type ArgsBridgeExecutor struct { TimeForWaitOnEthereum time.Duration StatusHandler core.StatusHandler SignaturesHolder SignaturesHolder - BatchValidator clients.BatchValidator + BalanceValidator BalanceValidator MaxQuorumRetriesOnEthereum uint64 MaxQuorumRetriesOnMultiversX uint64 MaxRestriesOnWasProposed uint64 @@ -41,12 +46,12 @@ type bridgeExecutor struct { timeForWaitOnEthereum time.Duration statusHandler core.StatusHandler sigsHolder SignaturesHolder - batchValidator clients.BatchValidator + balanceValidator BalanceValidator maxQuorumRetriesOnEthereum uint64 maxQuorumRetriesOnMultiversX uint64 maxRetriesOnWasProposed uint64 - batch *clients.TransferBatch + batch *bridgeCore.TransferBatch actionID uint64 msgHash common.Hash quorumRetriesOnEthereum uint64 @@ -87,8 +92,8 @@ func checkArgs(args ArgsBridgeExecutor) error { if check.IfNil(args.SignaturesHolder) { return ErrNilSignaturesHolder } - if check.IfNil(args.BatchValidator) { - return ErrNilBatchValidator + if check.IfNil(args.BalanceValidator) { + return ErrNilBalanceValidator } if args.MaxQuorumRetriesOnEthereum < minRetries { return fmt.Errorf("%w for args.MaxQuorumRetriesOnEthereum, got: %d, minimum: %d", @@ -114,7 +119,7 @@ func createBridgeExecutor(args ArgsBridgeExecutor) *bridgeExecutor { statusHandler: args.StatusHandler, timeForWaitOnEthereum: args.TimeForWaitOnEthereum, sigsHolder: args.SignaturesHolder, - batchValidator: args.BatchValidator, + balanceValidator: args.BalanceValidator, maxQuorumRetriesOnEthereum: args.MaxQuorumRetriesOnEthereum, maxQuorumRetriesOnMultiversX: args.MaxQuorumRetriesOnMultiversX, maxRetriesOnWasProposed: args.MaxRestriesOnWasProposed, @@ -146,8 +151,8 @@ func (executor *bridgeExecutor) MyTurnAsLeader() bool { } // GetBatchFromMultiversX fetches the pending batch from MultiversX -func (executor *bridgeExecutor) GetBatchFromMultiversX(ctx context.Context) (*clients.TransferBatch, error) { - batch, err := executor.multiversXClient.GetPending(ctx) +func (executor *bridgeExecutor) GetBatchFromMultiversX(ctx context.Context) (*bridgeCore.TransferBatch, error) { + batch, err := executor.multiversXClient.GetPendingBatch(ctx) if err == nil { executor.statusHandler.SetIntMetric(core.MetricNumBatches, int(batch.ID)-1) } @@ -155,7 +160,7 @@ func (executor *bridgeExecutor) GetBatchFromMultiversX(ctx context.Context) (*cl } // StoreBatchFromMultiversX saves the pending batch from MultiversX -func (executor *bridgeExecutor) StoreBatchFromMultiversX(batch *clients.TransferBatch) error { +func (executor *bridgeExecutor) StoreBatchFromMultiversX(batch *bridgeCore.TransferBatch) error { if batch == nil { return ErrNilBatch } @@ -165,7 +170,7 @@ func (executor *bridgeExecutor) StoreBatchFromMultiversX(batch *clients.Transfer } // GetStoredBatch returns the stored batch -func (executor *bridgeExecutor) GetStoredBatch() *clients.TransferBatch { +func (executor *bridgeExecutor) GetStoredBatch() *bridgeCore.TransferBatch { return executor.batch } @@ -178,7 +183,7 @@ func (executor *bridgeExecutor) GetLastExecutedEthBatchIDFromMultiversX(ctx cont return batchID, err } -// VerifyLastDepositNonceExecutedOnEthereumBatch will check the deposit nonces from the fetched batch from Ethereum client +// VerifyLastDepositNonceExecutedOnEthereumBatch will check the deposit Nonces from the fetched batch from Ethereum client func (executor *bridgeExecutor) VerifyLastDepositNonceExecutedOnEthereumBatch(ctx context.Context) error { if executor.batch == nil { return ErrNilBatch @@ -436,22 +441,81 @@ func (executor *bridgeExecutor) ResetRetriesCountOnMultiversX() { // GetAndStoreBatchFromEthereum fetches and stores the batch from the ethereum client func (executor *bridgeExecutor) GetAndStoreBatchFromEthereum(ctx context.Context, nonce uint64) error { - batch, err := executor.ethereumClient.GetBatch(ctx, nonce) + batch, isFinal, err := executor.ethereumClient.GetBatch(ctx, nonce) if err != nil { return err } - isBatchInvalid := batch.ID != nonce || len(batch.Deposits) == 0 + isBatchInvalid := batch.ID != nonce || len(batch.Deposits) == 0 || !isFinal if isBatchInvalid { - return fmt.Errorf("%w, requested nonce: %d, fetched nonce: %d, num deposits: %d", - ErrBatchNotFound, nonce, batch.ID, len(batch.Deposits)) + return fmt.Errorf("%w, requested nonce: %d, fetched nonce: %d, num deposits: %d, isFinal: %v", + ErrFinalBatchNotFound, nonce, batch.ID, len(batch.Deposits), isFinal) } + batch, err = executor.addBatchSCMetadata(ctx, batch) + if err != nil { + return err + } executor.batch = batch return nil } +// addBatchSCMetadata fetches the logs containing sc calls metadata for the current batch +func (executor *bridgeExecutor) addBatchSCMetadata(ctx context.Context, transfers *bridgeCore.TransferBatch) (*bridgeCore.TransferBatch, error) { + if transfers == nil { + return nil, ErrNilBatch + } + + events, err := executor.ethereumClient.GetBatchSCMetadata(ctx, transfers.ID, int64(transfers.BlockNumber)) + if err != nil { + return nil, err + } + + for i, t := range transfers.Deposits { + transfers.Deposits[i] = executor.addMetadataToTransfer(t, events) + } + + return transfers, nil +} + +func (executor *bridgeExecutor) addMetadataToTransfer(transfer *bridgeCore.DepositTransfer, events []*contract.ERC20SafeERC20SCDeposit) *bridgeCore.DepositTransfer { + for _, event := range events { + if event.DepositNonce.Uint64() == transfer.Nonce { + processData(transfer, event.CallData) + return transfer + } + } + + transfer.Data = []byte{bridgeCore.MissingDataProtocolMarker} + transfer.DisplayableData = "" + + return transfer +} + +func processData(transfer *bridgeCore.DepositTransfer, buff []byte) { + transfer.Data = buff + dataLen := len(transfer.Data) + if dataLen == 0 { + transfer.Data = []byte{bridgeCore.MissingDataProtocolMarker} + transfer.DisplayableData = "" + return + } + // this check is optional, but brings an optimisation to reduce the gas used in case of a bad callData + if dataLen == 1 && buff[0] == bridgeCore.MissingDataProtocolMarker { + return + } + + // we have a data field, add the marker & the correct length + transfer.DisplayableData = hex.EncodeToString(transfer.Data) + buff32 := make([]byte, bridgeCore.Uint32ArgBytes) + binary.BigEndian.PutUint32(buff32, uint32(dataLen)) + + prefix := append([]byte{bridgeCore.DataPresentProtocolMarker}, buff32...) + + transfer.Data = append(prefix, transfer.Data...) +} + // WasTransferPerformedOnEthereum returns true if the batch was performed on Ethereum func (executor *bridgeExecutor) WasTransferPerformedOnEthereum(ctx context.Context) (bool, error) { if executor.batch == nil { @@ -467,7 +531,8 @@ func (executor *bridgeExecutor) SignTransferOnEthereum() error { return ErrNilBatch } - hash, err := executor.ethereumClient.GenerateMessageHash(executor.batch) + argLists := batchProcessor.ExtractListMvxToEth(executor.batch) + hash, err := executor.ethereumClient.GenerateMessageHash(argLists, executor.batch.ID) if err != nil { return err } @@ -493,7 +558,11 @@ func (executor *bridgeExecutor) PerformTransferOnEthereum(ctx context.Context) e executor.log.Debug("fetched quorum size", "quorum", quorumSize.Int64()) - hash, err := executor.ethereumClient.ExecuteTransfer(ctx, executor.msgHash, executor.batch, int(quorumSize.Int64())) + argLists := batchProcessor.ExtractListMvxToEth(executor.batch) + + executor.log.Info("executing transfer " + executor.batch.String()) + + hash, err := executor.ethereumClient.ExecuteTransfer(ctx, executor.msgHash, argLists, executor.batch.ID, int(quorumSize.Int64())) if err != nil { return err } @@ -504,6 +573,48 @@ func (executor *bridgeExecutor) PerformTransferOnEthereum(ctx context.Context) e return nil } +func (executor *bridgeExecutor) checkCumulatedTransfers(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + for i, ethToken := range ethTokens { + err := executor.balanceValidator.CheckToken(ctx, ethToken, mvxTokens[i], amounts[i], direction) + if err != nil { + return err + } + } + return nil +} + +// CheckAvailableTokens checks the available balances +func (executor *bridgeExecutor) CheckAvailableTokens(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + ethTokens, mvxTokens, amounts = executor.getCumulatedTransfers(ethTokens, mvxTokens, amounts) + + return executor.checkCumulatedTransfers(ctx, ethTokens, mvxTokens, amounts, direction) +} + +func (executor *bridgeExecutor) getCumulatedTransfers(ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int) ([]common.Address, [][]byte, []*big.Int) { + cumulatedAmounts := make(map[common.Address]*big.Int) + uniqueTokens := make([]common.Address, 0) + uniqueConvertedTokens := make([][]byte, 0) + + for i, token := range ethTokens { + existingValue, exists := cumulatedAmounts[token] + if exists { + existingValue.Add(existingValue, amounts[i]) + continue + } + + cumulatedAmounts[token] = big.NewInt(0).Set(amounts[i]) // work on a new pointer + uniqueTokens = append(uniqueTokens, token) + uniqueConvertedTokens = append(uniqueConvertedTokens, mvxTokens[i]) + } + + finalAmounts := make([]*big.Int, len(uniqueTokens)) + for i, token := range uniqueTokens { + finalAmounts[i] = cumulatedAmounts[token] + } + + return uniqueTokens, uniqueConvertedTokens, finalAmounts +} + // ProcessQuorumReachedOnEthereum returns true if the proposed transfer reached the set quorum func (executor *bridgeExecutor) ProcessQuorumReachedOnEthereum(ctx context.Context) (bool, error) { return executor.ethereumClient.IsQuorumReached(ctx, executor.msgHash) @@ -530,11 +641,6 @@ func (executor *bridgeExecutor) ClearStoredP2PSignaturesForEthereum() { executor.log.Info("cleared stored P2P signatures") } -// ValidateBatch returns true if the given batch is validated on microservice side -func (executor *bridgeExecutor) ValidateBatch(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return executor.batchValidator.ValidateBatch(ctx, batch) -} - // CheckMultiversXClientAvailability trigger a self availability check for the MultiversX client func (executor *bridgeExecutor) CheckMultiversXClientAvailability(ctx context.Context) error { return executor.multiversXClient.CheckClientAvailability(ctx) diff --git a/bridges/ethMultiversX/bridgeExecutor_test.go b/bridges/ethMultiversX/bridgeExecutor_test.go index 3cfc5421..2751ae31 100644 --- a/bridges/ethMultiversX/bridgeExecutor_test.go +++ b/bridges/ethMultiversX/bridgeExecutor_test.go @@ -11,7 +11,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" "github.com/multiversx/mx-bridge-eth-go/testsCommon" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/multiversx/mx-chain-core-go/core/check" @@ -20,7 +22,7 @@ import ( ) var expectedErr = errors.New("expected error") -var providedBatch = &clients.TransferBatch{} +var providedBatch = &bridgeCore.TransferBatch{} var expectedMaxRetries = uint64(3) func createMockExecutorArgs() ArgsBridgeExecutor { @@ -32,7 +34,7 @@ func createMockExecutorArgs() ArgsBridgeExecutor { StatusHandler: testsCommon.NewStatusHandlerMock("test"), TimeForWaitOnEthereum: time.Second, SignaturesHolder: &testsCommon.SignaturesHolderStub{}, - BatchValidator: &testsCommon.BatchValidatorStub{}, + BalanceValidator: &testsCommon.BalanceValidatorStub{}, MaxQuorumRetriesOnEthereum: minRetries, MaxQuorumRetriesOnMultiversX: minRetries, MaxRestriesOnWasProposed: minRetries, @@ -112,15 +114,15 @@ func TestNewBridgeExecutor(t *testing.T) { assert.True(t, check.IfNil(executor)) assert.Equal(t, ErrNilSignaturesHolder, err) }) - t.Run("nil batch validator", func(t *testing.T) { + t.Run("nil balance validator", func(t *testing.T) { t.Parallel() args := createMockExecutorArgs() - args.BatchValidator = nil + args.BalanceValidator = nil executor, err := NewBridgeExecutor(args) assert.True(t, check.IfNil(executor)) - assert.Equal(t, ErrNilBatchValidator, err) + assert.Equal(t, ErrNilBalanceValidator, err) }) t.Run("invalid MaxQuorumRetriesOnEthereum value", func(t *testing.T) { t.Parallel() @@ -199,7 +201,7 @@ func testPrintInfo(t *testing.T, logLevel logger.LogLevel, shouldOutputToStatusH assert.True(t, wasCalled) if shouldOutputToStatusHandler { - assert.True(t, len(statusHandler.GetStringMetric(core.MetricLastError)) > 0) + assert.True(t, len(statusHandler.GetStringMetric(bridgeCore.MetricLastError)) > 0) } } @@ -238,7 +240,7 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreActionIDForProposeTransferOnMu args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - GetActionIDForProposeTransferCalled: func(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { + GetActionIDForProposeTransferCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { assert.True(t, providedBatch == batch) return 0, expectedErr }, @@ -257,7 +259,7 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreActionIDForProposeTransferOnMu providedActionID := uint64(48939) args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - GetActionIDForProposeTransferCalled: func(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { + GetActionIDForProposeTransferCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { assert.True(t, providedBatch == batch) return providedActionID, nil }, @@ -284,9 +286,9 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreBatchFromEthereum(t *testing.T args := createMockExecutorArgs() providedNonce := uint64(8346) args.EthereumClient = &bridgeTests.EthereumClientStub{ - GetBatchCalled: func(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) { + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { assert.Equal(t, providedNonce, nonce) - return nil, expectedErr + return nil, false, expectedErr }, } executor, _ := NewBridgeExecutor(args) @@ -299,19 +301,19 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreBatchFromEthereum(t *testing.T args := createMockExecutorArgs() providedNonce := uint64(8346) - expectedBatch := &clients.TransferBatch{ + expectedBatch := &bridgeCore.TransferBatch{ ID: 0, } args.EthereumClient = &bridgeTests.EthereumClientStub{ - GetBatchCalled: func(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) { + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { assert.Equal(t, providedNonce, nonce) - return expectedBatch, nil + return expectedBatch, true, nil }, } executor, _ := NewBridgeExecutor(args) err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) - assert.True(t, errors.Is(err, ErrBatchNotFound)) + assert.True(t, errors.Is(err, ErrFinalBatchNotFound)) assert.True(t, strings.Contains(err.Error(), fmt.Sprintf("%d", providedNonce))) assert.Nil(t, executor.GetStoredBatch()) assert.Nil(t, executor.batch) @@ -321,19 +323,47 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreBatchFromEthereum(t *testing.T args := createMockExecutorArgs() providedNonce := uint64(8346) - expectedBatch := &clients.TransferBatch{ + expectedBatch := &bridgeCore.TransferBatch{ ID: providedNonce, } args.EthereumClient = &bridgeTests.EthereumClientStub{ - GetBatchCalled: func(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) { + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { assert.Equal(t, providedNonce, nonce) - return expectedBatch, nil + return expectedBatch, true, nil }, } executor, _ := NewBridgeExecutor(args) err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) - assert.True(t, errors.Is(err, ErrBatchNotFound)) + assert.True(t, errors.Is(err, ErrFinalBatchNotFound)) + assert.True(t, strings.Contains(err.Error(), fmt.Sprintf("%d", providedNonce))) + assert.Nil(t, executor.GetStoredBatch()) + assert.Nil(t, executor.batch) + }) + t.Run("not a final batch should error", func(t *testing.T) { + t.Parallel() + + args := createMockExecutorArgs() + providedNonce := uint64(8346) + expectedBatch := &bridgeCore.TransferBatch{ + ID: providedNonce, + Deposits: []*bridgeCore.DepositTransfer{ + {}, + }, + } + args.EthereumClient = &bridgeTests.EthereumClientStub{ + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { + assert.Equal(t, providedNonce, nonce) + return expectedBatch, false, nil + }, + GetBatchSCMetadataCalled: func(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + return make([]*contract.ERC20SafeERC20SCDeposit, 0), nil + }, + } + executor, _ := NewBridgeExecutor(args) + err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) + + assert.True(t, errors.Is(err, ErrFinalBatchNotFound)) assert.True(t, strings.Contains(err.Error(), fmt.Sprintf("%d", providedNonce))) assert.Nil(t, executor.GetStoredBatch()) assert.Nil(t, executor.batch) @@ -343,16 +373,19 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreBatchFromEthereum(t *testing.T args := createMockExecutorArgs() providedNonce := uint64(8346) - expectedBatch := &clients.TransferBatch{ + expectedBatch := &bridgeCore.TransferBatch{ ID: providedNonce, - Deposits: []*clients.DepositTransfer{ + Deposits: []*bridgeCore.DepositTransfer{ {}, }, } args.EthereumClient = &bridgeTests.EthereumClientStub{ - GetBatchCalled: func(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) { + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { assert.Equal(t, providedNonce, nonce) - return expectedBatch, nil + return expectedBatch, true, nil + }, + GetBatchSCMetadataCalled: func(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + return make([]*contract.ERC20SafeERC20SCDeposit, 0), nil }, } executor, _ := NewBridgeExecutor(args) @@ -362,6 +395,142 @@ func TestEthToMultiversXBridgeExecutor_GetAndStoreBatchFromEthereum(t *testing.T assert.True(t, expectedBatch == executor.GetStoredBatch()) // pointer testing assert.True(t, expectedBatch == executor.batch) }) + t.Run("should add deposits metadata for sc calls", func(t *testing.T) { + t.Parallel() + + args := createMockExecutorArgs() + providedNonce := uint64(8346) + depositNonce := uint64(100) + depositData := []byte("testData") + expectedBatch := &bridgeCore.TransferBatch{ + ID: providedNonce, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: depositNonce, + }, + }, + } + args.EthereumClient = &bridgeTests.EthereumClientStub{ + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { + assert.Equal(t, providedNonce, nonce) + return expectedBatch, true, nil + }, + GetBatchSCMetadataCalled: func(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + return []*contract.ERC20SafeERC20SCDeposit{{ + DepositNonce: big.NewInt(0).SetUint64(depositNonce), + CallData: depositData, + }}, nil + }, + } + executor, _ := NewBridgeExecutor(args) + err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) + + assert.Nil(t, err) + assert.True(t, expectedBatch == executor.GetStoredBatch()) // pointer testing + expectedDepositData := []byte{bridgeCore.DataPresentProtocolMarker, 0, 0, 0, byte(len(depositData))} + expectedDepositData = append(expectedDepositData, []byte(depositData)...) + assert.Equal(t, string(expectedDepositData), string(executor.batch.Deposits[0].Data)) + }) + t.Run("should add deposits metadata for sc calls with a data starting with missing data marker", func(t *testing.T) { + t.Parallel() + + args := createMockExecutorArgs() + providedNonce := uint64(8346) + depositNonce := uint64(100) + depositData := append([]byte{bridgeCore.MissingDataProtocolMarker}, "testData"...) + expectedBatch := &bridgeCore.TransferBatch{ + ID: providedNonce, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: depositNonce, + }, + }, + } + args.EthereumClient = &bridgeTests.EthereumClientStub{ + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { + assert.Equal(t, providedNonce, nonce) + return expectedBatch, true, nil + }, + GetBatchSCMetadataCalled: func(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + return []*contract.ERC20SafeERC20SCDeposit{{ + DepositNonce: big.NewInt(0).SetUint64(depositNonce), + CallData: depositData, + }}, nil + }, + } + executor, _ := NewBridgeExecutor(args) + err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) + + assert.Nil(t, err) + assert.True(t, expectedBatch == executor.GetStoredBatch()) // pointer testing + expectedDepositData := []byte{bridgeCore.DataPresentProtocolMarker, 0, 0, 0, byte(len(depositData))} + expectedDepositData = append(expectedDepositData, []byte(depositData)...) + assert.Equal(t, string(expectedDepositData), string(executor.batch.Deposits[0].Data)) + }) + t.Run("should add deposits metadata for sc calls even if with no data", func(t *testing.T) { + args := createMockExecutorArgs() + providedNonce := uint64(8346) + depositNonce := uint64(100) + depositData := make([]byte, 0) + expectedBatch := &bridgeCore.TransferBatch{ + ID: providedNonce, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: depositNonce, + }, + }, + } + args.EthereumClient = &bridgeTests.EthereumClientStub{ + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { + assert.Equal(t, providedNonce, nonce) + return expectedBatch, true, nil + }, + GetBatchSCMetadataCalled: func(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + return []*contract.ERC20SafeERC20SCDeposit{{ + DepositNonce: big.NewInt(0).SetUint64(depositNonce), + CallData: depositData, + }}, nil + }, + } + executor, _ := NewBridgeExecutor(args) + err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) + + assert.Nil(t, err) + assert.True(t, expectedBatch == executor.GetStoredBatch()) // pointer testing + assert.Equal(t, string([]byte{bridgeCore.MissingDataProtocolMarker}), string(executor.batch.Deposits[0].Data)) + }) + t.Run("should bypass data if the data is the missing marker", func(t *testing.T) { + args := createMockExecutorArgs() + providedNonce := uint64(8346) + depositNonce := uint64(100) + depositData := []byte{bridgeCore.MissingDataProtocolMarker} + expectedBatch := &bridgeCore.TransferBatch{ + ID: providedNonce, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: depositNonce, + }, + }, + } + args.EthereumClient = &bridgeTests.EthereumClientStub{ + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { + assert.Equal(t, providedNonce, nonce) + return expectedBatch, true, nil + }, + GetBatchSCMetadataCalled: func(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + return []*contract.ERC20SafeERC20SCDeposit{{ + DepositNonce: big.NewInt(0).SetUint64(depositNonce), + CallData: depositData, + }}, nil + }, + } + executor, _ := NewBridgeExecutor(args) + err := executor.GetAndStoreBatchFromEthereum(context.Background(), providedNonce) + + assert.Nil(t, err) + assert.True(t, expectedBatch == executor.GetStoredBatch()) // pointer testing + assert.Equal(t, depositData, executor.batch.Deposits[0].Data) + }) } func TestEthToMultiversXBridgeExecutor_GetLastExecutedEthBatchIDFromMultiversX(t *testing.T) { @@ -377,7 +546,7 @@ func TestEthToMultiversXBridgeExecutor_GetLastExecutedEthBatchIDFromMultiversX(t setIntCalled := false args.StatusHandler = &testsCommon.StatusHandlerStub{ SetIntMetricCalled: func(metric string, value int) { - assert.Equal(t, core.MetricNumBatches, metric) + assert.Equal(t, bridgeCore.MetricNumBatches, metric) assert.Equal(t, int(providedBatchID), value) setIntCalled = true }, @@ -412,7 +581,7 @@ func TestEthToMultiversXBridgeExecutor_VerifyLastDepositNonceExecutedOnEthereumB }, } executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{} + executor.batch = &bridgeCore.TransferBatch{} err := executor.VerifyLastDepositNonceExecutedOnEthereumBatch(context.Background()) assert.Equal(t, expectedErr, err) @@ -430,8 +599,8 @@ func TestEthToMultiversXBridgeExecutor_VerifyLastDepositNonceExecutedOnEthereumB t.Parallel() executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{ - Deposits: []*clients.DepositTransfer{ + executor.batch = &bridgeCore.TransferBatch{ + Deposits: []*bridgeCore.DepositTransfer{ { Nonce: txId, }, @@ -446,8 +615,8 @@ func TestEthToMultiversXBridgeExecutor_VerifyLastDepositNonceExecutedOnEthereumB t.Parallel() executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{ - Deposits: []*clients.DepositTransfer{ + executor.batch = &bridgeCore.TransferBatch{ + Deposits: []*bridgeCore.DepositTransfer{ { Nonce: txId - 1, }, @@ -462,8 +631,8 @@ func TestEthToMultiversXBridgeExecutor_VerifyLastDepositNonceExecutedOnEthereumB t.Parallel() executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{ - Deposits: []*clients.DepositTransfer{ + executor.batch = &bridgeCore.TransferBatch{ + Deposits: []*bridgeCore.DepositTransfer{ { Nonce: txId + 1, }, @@ -481,8 +650,8 @@ func TestEthToMultiversXBridgeExecutor_VerifyLastDepositNonceExecutedOnEthereumB t.Parallel() executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{ - Deposits: []*clients.DepositTransfer{ + executor.batch = &bridgeCore.TransferBatch{ + Deposits: []*bridgeCore.DepositTransfer{ { Nonce: txId + 1, }, @@ -492,8 +661,8 @@ func TestEthToMultiversXBridgeExecutor_VerifyLastDepositNonceExecutedOnEthereumB err := executor.VerifyLastDepositNonceExecutedOnEthereumBatch(context.Background()) assert.Nil(t, err) - executor.batch = &clients.TransferBatch{ - Deposits: []*clients.DepositTransfer{ + executor.batch = &bridgeCore.TransferBatch{ + Deposits: []*bridgeCore.DepositTransfer{ { Nonce: txId + 1, }, @@ -527,7 +696,7 @@ func TestEthToMultiversXBridgeExecutor_WasTransferProposedOnMultiversX(t *testin args := createMockExecutorArgs() wasCalled := false args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - WasProposedTransferCalled: func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { + WasProposedTransferCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) { assert.True(t, providedBatch == batch) wasCalled = true return true, nil @@ -561,7 +730,7 @@ func TestEthToMultiversXBridgeExecutor_ProposeTransferOnMultiversX(t *testing.T) args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - ProposeTransferCalled: func(ctx context.Context, batch *clients.TransferBatch) (string, error) { + ProposeTransferCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { assert.True(t, providedBatch == batch) return "", expectedErr @@ -579,7 +748,7 @@ func TestEthToMultiversXBridgeExecutor_ProposeTransferOnMultiversX(t *testing.T) args := createMockExecutorArgs() wasCalled := false args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - ProposeTransferCalled: func(ctx context.Context, batch *clients.TransferBatch) (string, error) { + ProposeTransferCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { assert.True(t, providedBatch == batch) wasCalled = true @@ -723,7 +892,7 @@ func TestEthToMultiversXBridgeExecutor_PerformActionOnMultiversX(t *testing.T) { args := createMockExecutorArgs() providedActionID := uint64(7383) args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - PerformActionCalled: func(ctx context.Context, actionID uint64, batch *clients.TransferBatch) (string, error) { + PerformActionCalled: func(ctx context.Context, actionID uint64, batch *bridgeCore.TransferBatch) (string, error) { assert.Equal(t, providedActionID, actionID) assert.True(t, providedBatch == batch) return "", expectedErr @@ -743,7 +912,7 @@ func TestEthToMultiversXBridgeExecutor_PerformActionOnMultiversX(t *testing.T) { wasCalled := false providedActionID := uint64(7383) args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - PerformActionCalled: func(ctx context.Context, actionID uint64, batch *clients.TransferBatch) (string, error) { + PerformActionCalled: func(ctx context.Context, actionID uint64, batch *bridgeCore.TransferBatch) (string, error) { assert.Equal(t, providedActionID, actionID) assert.True(t, providedBatch == batch) wasCalled = true @@ -800,7 +969,7 @@ func TestMultiversXToEthBridgeExecutor_GetAndStoreBatchFromMultiversX(t *testing args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - GetPendingCalled: func(ctx context.Context) (*clients.TransferBatch, error) { + GetPendingBatchCalled: func(ctx context.Context) (*bridgeCore.TransferBatch, error) { return nil, expectedErr }, } @@ -828,7 +997,7 @@ func TestMultiversXToEthBridgeExecutor_GetAndStoreBatchFromMultiversX(t *testing wasCalled := false args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - GetPendingCalled: func(ctx context.Context) (*clients.TransferBatch, error) { + GetPendingBatchCalled: func(ctx context.Context) (*bridgeCore.TransferBatch, error) { wasCalled = true return providedBatch, nil }, @@ -864,7 +1033,7 @@ func TestMultiversXToEthBridgeExecutor_GetAndStoreActionIDForProposeSetStatusFro args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - GetActionIDForSetStatusOnPendingTransferCalled: func(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { + GetActionIDForSetStatusOnPendingTransferCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { return uint64(0), expectedErr }, } @@ -881,7 +1050,7 @@ func TestMultiversXToEthBridgeExecutor_GetAndStoreActionIDForProposeSetStatusFro providedActionId := uint64(1123) args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - GetActionIDForSetStatusOnPendingTransferCalled: func(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { + GetActionIDForSetStatusOnPendingTransferCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { wasCalled = true return providedActionId, nil }, @@ -898,6 +1067,7 @@ func TestMultiversXToEthBridgeExecutor_GetAndStoreActionIDForProposeSetStatusFro assert.Equal(t, providedActionId, actionId) }) } + func TestMultiversXToEthBridgeExecutor_WasSetStatusProposedOnMultiversX(t *testing.T) { t.Parallel() @@ -916,7 +1086,7 @@ func TestMultiversXToEthBridgeExecutor_WasSetStatusProposedOnMultiversX(t *testi args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - WasProposedSetStatusCalled: func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { + WasProposedSetStatusCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) { return false, expectedErr }, } @@ -932,7 +1102,7 @@ func TestMultiversXToEthBridgeExecutor_WasSetStatusProposedOnMultiversX(t *testi wasCalled := false args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - WasProposedSetStatusCalled: func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { + WasProposedSetStatusCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) { assert.True(t, providedBatch == batch) wasCalled = true return true, nil @@ -965,7 +1135,7 @@ func TestEthToMultiversXBridgeExecutor_ProposeSetStatusOnMultiversX(t *testing.T args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - ProposeSetStatusCalled: func(ctx context.Context, batch *clients.TransferBatch) (string, error) { + ProposeSetStatusCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { return "", expectedErr }, } @@ -981,7 +1151,7 @@ func TestEthToMultiversXBridgeExecutor_ProposeSetStatusOnMultiversX(t *testing.T wasCalled := false args := createMockExecutorArgs() args.MultiversXClient = &bridgeTests.MultiversXClientStub{ - ProposeSetStatusCalled: func(ctx context.Context, batch *clients.TransferBatch) (string, error) { + ProposeSetStatusCalled: func(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { assert.True(t, providedBatch == batch) wasCalled = true @@ -1083,7 +1253,7 @@ func TestMultiversXToEthBridgeExecutor_SignTransferOnEthereum(t *testing.T) { args := createMockExecutorArgs() args.EthereumClient = &bridgeTests.EthereumClientStub{ - GenerateMessageHashCalled: func(batch *clients.TransferBatch) (common.Hash, error) { + GenerateMessageHashCalled: func(batch *batchProcessor.ArgListsBatch, batchID uint64) (common.Hash, error) { return common.Hash{}, expectedErr }, } @@ -1100,7 +1270,7 @@ func TestMultiversXToEthBridgeExecutor_SignTransferOnEthereum(t *testing.T) { wasCalledBroadcastSignatureForMessageHashCalled := false args := createMockExecutorArgs() args.EthereumClient = &bridgeTests.EthereumClientStub{ - GenerateMessageHashCalled: func(batch *clients.TransferBatch) (common.Hash, error) { + GenerateMessageHashCalled: func(batch *batchProcessor.ArgListsBatch, batchID uint64) (common.Hash, error) { wasCalledGenerateMessageHashCalled = true return common.Hash{}, nil }, @@ -1153,7 +1323,7 @@ func TestMultiversXToEthBridgeExecutor_PerformTransferOnEthereum(t *testing.T) { GetQuorumSizeCalled: func(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil }, - ExecuteTransferCalled: func(ctx context.Context, msgHash common.Hash, batch *clients.TransferBatch, quorum int) (string, error) { + ExecuteTransferCalled: func(ctx context.Context, msgHash common.Hash, batch *batchProcessor.ArgListsBatch, batchId uint64, quorum int) (string, error) { return "", expectedErr }, } @@ -1176,9 +1346,16 @@ func TestMultiversXToEthBridgeExecutor_PerformTransferOnEthereum(t *testing.T) { wasCalledGetQuorumSizeCalled = true return big.NewInt(int64(providedQuorum)), nil }, - ExecuteTransferCalled: func(ctx context.Context, msgHash common.Hash, batch *clients.TransferBatch, quorum int) (string, error) { + ExecuteTransferCalled: func(ctx context.Context, msgHash common.Hash, batch *batchProcessor.ArgListsBatch, batchId uint64, quorum int) (string, error) { assert.True(t, providedHash == msgHash) - assert.True(t, providedBatch == batch) + assert.True(t, providedBatch.ID == batchId) + for i := 0; i < len(providedBatch.Deposits); i++ { + assert.Equal(t, providedBatch.Deposits[i].Amount, batch.Amounts[i]) + assert.Equal(t, providedBatch.Deposits[i].Nonce, batch.Nonces[i].Uint64()) + assert.Equal(t, providedBatch.Deposits[i].ToBytes, batch.Recipients[i].Bytes()) + assert.Equal(t, providedBatch.Deposits[i].SourceTokenBytes, batch.EthTokens[i].Bytes()) + assert.Equal(t, providedBatch.Deposits[i].DestinationTokenBytes, batch.MvxTokenBytes[i]) + } assert.True(t, providedQuorum == quorum) wasCalledExecuteTransferCalled = true @@ -1297,7 +1474,7 @@ func TestWaitForTransferConfirmation(t *testing.T) { }, } executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{} + executor.batch = &bridgeCore.TransferBatch{} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1323,7 +1500,7 @@ func TestWaitForTransferConfirmation(t *testing.T) { }, } executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{} + executor.batch = &bridgeCore.TransferBatch{} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1368,7 +1545,7 @@ func TestGetBatchStatusesFromEthereum(t *testing.T) { t.Parallel() wasCalled := false - providedStatuses := []byte{clients.Executed, clients.Rejected} + providedStatuses := []byte{bridgeCore.Executed, bridgeCore.Rejected} args := createMockExecutorArgs() args.EthereumClient = &bridgeTests.EthereumClientStub{ GetTransactionsStatusesCalled: func(ctx context.Context, batchId uint64) ([]byte, error) { @@ -1433,7 +1610,7 @@ func TestWaitAndReturnFinalBatchStatuses(t *testing.T) { }, } executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{} + executor.batch = &bridgeCore.TransferBatch{} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1446,7 +1623,7 @@ func TestWaitAndReturnFinalBatchStatuses(t *testing.T) { t.Run("GetBatchStatusesFromEthereum always returns success+statuses only after 4 checks", func(t *testing.T) { t.Parallel() - providedStatuses := []byte{clients.Executed, clients.Rejected} + providedStatuses := []byte{bridgeCore.Executed, bridgeCore.Rejected} args := createMockExecutorArgs() args.TimeForWaitOnEthereum = 10 * time.Second counter := 0 @@ -1460,7 +1637,7 @@ func TestWaitAndReturnFinalBatchStatuses(t *testing.T) { }, } executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{} + executor.batch = &bridgeCore.TransferBatch{} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1477,7 +1654,7 @@ func TestWaitAndReturnFinalBatchStatuses(t *testing.T) { t.Run("GetBatchStatusesFromEthereum always returns success+statuses only after 4 checks, otherwise empty slice", func(t *testing.T) { t.Parallel() - providedStatuses := []byte{clients.Executed, clients.Rejected} + providedStatuses := []byte{bridgeCore.Executed, bridgeCore.Rejected} args := createMockExecutorArgs() args.TimeForWaitOnEthereum = 10 * time.Second counter := 0 @@ -1491,7 +1668,7 @@ func TestWaitAndReturnFinalBatchStatuses(t *testing.T) { }, } executor, _ := NewBridgeExecutor(args) - executor.batch = &clients.TransferBatch{} + executor.batch = &bridgeCore.TransferBatch{} ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1510,8 +1687,8 @@ func TestWaitAndReturnFinalBatchStatuses(t *testing.T) { func TestResolveNewDepositsStatuses(t *testing.T) { t.Parallel() - providedBatchForResolve := &clients.TransferBatch{ - Deposits: []*clients.DepositTransfer{ + providedBatchForResolve := &bridgeCore.TransferBatch{ + Deposits: []*bridgeCore.DepositTransfer{ { DisplayableTo: "to1", }, @@ -1530,11 +1707,11 @@ func TestResolveNewDepositsStatuses(t *testing.T) { executor.batch = providedBatchForResolve.Clone() executor.ResolveNewDepositsStatuses(uint64(0)) - assert.Equal(t, []byte{clients.Rejected, clients.Rejected}, executor.batch.Statuses) + assert.Equal(t, []byte{bridgeCore.Rejected, bridgeCore.Rejected}, executor.batch.Statuses) executor.batch = providedBatchForResolve.Clone() executor.batch.ResolveNewDeposits(1) - assert.Equal(t, []byte{0, clients.Rejected}, executor.batch.Statuses) + assert.Equal(t, []byte{0, bridgeCore.Rejected}, executor.batch.Statuses) }) t.Run("equal new deposits", func(t *testing.T) { t.Parallel() @@ -1554,7 +1731,7 @@ func TestResolveNewDepositsStatuses(t *testing.T) { executor.batch = providedBatchForResolve.Clone() executor.ResolveNewDepositsStatuses(uint64(3)) - assert.Equal(t, []byte{0, 0, clients.Rejected}, executor.batch.Statuses) + assert.Equal(t, []byte{0, 0, bridgeCore.Rejected}, executor.batch.Statuses) }) } @@ -1569,7 +1746,7 @@ func TestEthToMultiversXBridgeExecutor_setExecutionMessageInStatusHandler(t *tes SetStringMetricCalled: func(metric string, val string) { wasCalled = true - assert.Equal(t, metric, core.MetricLastError) + assert.Equal(t, metric, bridgeCore.MetricLastError) assert.Equal(t, expectedString, val) }, } @@ -1632,26 +1809,93 @@ func TestBridgeExecutor_CheckEthereumClientAvailability(t *testing.T) { assert.True(t, checkAvailabilityCalled) } -func TestBridgeExecutor_ValidateBatch(t *testing.T) { +func TestBridgeExecutor_CheckAvailableTokens(t *testing.T) { t.Parallel() - validateBatchCalled := false - args := createMockExecutorArgs() - validationBatch := &clients.TransferBatch{ - ID: 45, + ethTokens := []common.Address{ + common.BytesToAddress([]byte("eth token 1")), + common.BytesToAddress([]byte("eth token 1")), + common.BytesToAddress([]byte("eth token 2")), } - args.BatchValidator = &testsCommon.BatchValidatorStub{ - ValidateBatchCalled: func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - assert.True(t, validationBatch == batch) // pointer testing - validateBatchCalled = true - return true, nil + mvxTokens := [][]byte{ + []byte("mvx token 1"), + []byte("mvx token 1"), + []byte("mvx token 2"), + } + + amounts := []*big.Int{ + big.NewInt(37), + big.NewInt(38), + big.NewInt(39), + } + + testDirection := batchProcessor.FromMultiversX + checkedEthTokens := make([]common.Address, 0) + checkedMvxTokens := make([][]byte, 0) + checkedAmounts := make([]*big.Int, 0) + + args := createMockExecutorArgs() + var returnedError error + args.BalanceValidator = &testsCommon.BalanceValidatorStub{ + CheckTokenCalled: func(ctx context.Context, ethToken common.Address, mvxToken []byte, amount *big.Int, direction batchProcessor.Direction) error { + checkedEthTokens = append(checkedEthTokens, ethToken) + checkedMvxTokens = append(checkedMvxTokens, mvxToken) + checkedAmounts = append(checkedAmounts, amount) + + assert.Equal(t, testDirection, direction) + + return returnedError }, } executor, _ := NewBridgeExecutor(args) - result, err := executor.ValidateBatch(context.Background(), validationBatch) - assert.Nil(t, err) - assert.True(t, result) - assert.True(t, validateBatchCalled) + // do not run these tests in parallel + t.Run("check validator does not error", func(t *testing.T) { + returnedError = nil + checkedEthTokens = make([]common.Address, 0) + checkedMvxTokens = make([][]byte, 0) + checkedAmounts = make([]*big.Int, 0) + err := executor.CheckAvailableTokens(context.Background(), ethTokens, mvxTokens, amounts, testDirection) + + expectedEthTokens := []common.Address{ + common.BytesToAddress([]byte("eth token 1")), + common.BytesToAddress([]byte("eth token 2")), + } + expectedMvxTokens := [][]byte{ + []byte("mvx token 1"), + []byte("mvx token 2"), + } + expectedAmounts := []*big.Int{ + big.NewInt(75), // 37 + 38 + big.NewInt(39), + } + + assert.Nil(t, err) + assert.Equal(t, expectedEthTokens, checkedEthTokens) + assert.Equal(t, expectedMvxTokens, checkedMvxTokens) + assert.Equal(t, expectedAmounts, checkedAmounts) + }) + t.Run("check validator returns error", func(t *testing.T) { + returnedError = fmt.Errorf("expected error") + checkedEthTokens = make([]common.Address, 0) + checkedMvxTokens = make([][]byte, 0) + checkedAmounts = make([]*big.Int, 0) + err := executor.CheckAvailableTokens(context.Background(), ethTokens, mvxTokens, amounts, testDirection) + + expectedEthTokens := []common.Address{ + common.BytesToAddress([]byte("eth token 1")), // only the first token is checked + } + expectedMvxTokens := [][]byte{ + []byte("mvx token 1"), + } + expectedAmounts := []*big.Int{ + big.NewInt(75), // 37 + 38 + } + + assert.Equal(t, returnedError, err) + assert.Equal(t, expectedEthTokens, checkedEthTokens) + assert.Equal(t, expectedMvxTokens, checkedMvxTokens) + assert.Equal(t, expectedAmounts, checkedAmounts) + }) } diff --git a/bridges/ethMultiversX/constants.go b/bridges/ethMultiversX/constants.go index 7bb7c492..f6dfcff9 100644 --- a/bridges/ethMultiversX/constants.go +++ b/bridges/ethMultiversX/constants.go @@ -1,7 +1,6 @@ package ethmultiversx import ( - "fmt" "time" ) @@ -9,23 +8,3 @@ import ( const InvalidActionID = uint64(0) const durationLimit = time.Second - -// ClientStatus represents the possible statuses of a client -type ClientStatus int - -const ( - Available ClientStatus = 0 - Unavailable ClientStatus = 1 -) - -// String will return status as string based on the int value -func (cs ClientStatus) String() string { - switch cs { - case Available: - return "Available" - case Unavailable: - return "Unavailable" - default: - return fmt.Sprintf("Invalid status %d", cs) - } -} diff --git a/bridges/ethMultiversX/errors.go b/bridges/ethMultiversX/errors.go index 9749d127..6986f858 100644 --- a/bridges/ethMultiversX/errors.go +++ b/bridges/ethMultiversX/errors.go @@ -32,11 +32,11 @@ var ErrDuplicatedStepIdentifier = errors.New("duplicated step identifier used in // ErrNilStatusHandler signals that a nil status handler was provided var ErrNilStatusHandler = errors.New("nil status handler") -// ErrBatchNotFound signals that the batch was not found -var ErrBatchNotFound = errors.New("batch not found") +// ErrFinalBatchNotFound signals that a final batch was not found +var ErrFinalBatchNotFound = errors.New("final batch not found") // ErrNilSignaturesHolder signals that a nil signatures holder was provided var ErrNilSignaturesHolder = errors.New("nil signatures holder") -// ErrNilBatchValidator signals that a nil batch validator was provided -var ErrNilBatchValidator = errors.New("nil batch validator") +// ErrNilBalanceValidator signals that a nil balance validator was provided +var ErrNilBalanceValidator = errors.New("nil balance validator") diff --git a/bridges/ethMultiversX/interface.go b/bridges/ethMultiversX/interface.go index 63a6f706..d514d171 100644 --- a/bridges/ethMultiversX/interface.go +++ b/bridges/ethMultiversX/interface.go @@ -5,46 +5,64 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" ) // MultiversXClient defines the behavior of the MultiversX client able to communicate with the MultiversX chain type MultiversXClient interface { - GetPending(ctx context.Context) (*clients.TransferBatch, error) + GetPendingBatch(ctx context.Context) (*bridgeCore.TransferBatch, error) + GetBatch(ctx context.Context, batchID uint64) (*bridgeCore.TransferBatch, error) GetCurrentBatchAsDataBytes(ctx context.Context) ([][]byte, error) - WasProposedTransfer(ctx context.Context, batch *clients.TransferBatch) (bool, error) + WasProposedTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) QuorumReached(ctx context.Context, actionID uint64) (bool, error) WasExecuted(ctx context.Context, actionID uint64) (bool, error) - GetActionIDForProposeTransfer(ctx context.Context, batch *clients.TransferBatch) (uint64, error) - WasProposedSetStatus(ctx context.Context, batch *clients.TransferBatch) (bool, error) + GetActionIDForProposeTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) + WasProposedSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) GetTransactionsStatuses(ctx context.Context, batchID uint64) ([]byte, error) - GetActionIDForSetStatusOnPendingTransfer(ctx context.Context, batch *clients.TransferBatch) (uint64, error) + GetActionIDForSetStatusOnPendingTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) GetLastExecutedEthBatchID(ctx context.Context) (uint64, error) GetLastExecutedEthTxID(ctx context.Context) (uint64, error) + GetLastMvxBatchID(ctx context.Context) (uint64, error) GetCurrentNonce(ctx context.Context) (uint64, error) - ProposeSetStatus(ctx context.Context, batch *clients.TransferBatch) (string, error) - ProposeTransfer(ctx context.Context, batch *clients.TransferBatch) (string, error) + ProposeSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) + ProposeTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) Sign(ctx context.Context, actionID uint64) (string, error) WasSigned(ctx context.Context, actionID uint64) (bool, error) - PerformAction(ctx context.Context, actionID uint64, batch *clients.TransferBatch) (string, error) + PerformAction(ctx context.Context, actionID uint64, batch *bridgeCore.TransferBatch) (string, error) CheckClientAvailability(ctx context.Context) error + IsMintBurnToken(ctx context.Context, token []byte) (bool, error) + IsNativeToken(ctx context.Context, token []byte) (bool, error) + TotalBalances(ctx context.Context, token []byte) (*big.Int, error) + MintBalances(ctx context.Context, token []byte) (*big.Int, error) + BurnBalances(ctx context.Context, token []byte) (*big.Int, error) + CheckRequiredBalance(ctx context.Context, token []byte, value *big.Int) error Close() error IsInterfaceNil() bool } // EthereumClient defines the behavior of the Ethereum client able to communicate with the Ethereum chain type EthereumClient interface { - GetBatch(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) + GetBatch(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) WasExecuted(ctx context.Context, batchID uint64) (bool, error) - GenerateMessageHash(batch *clients.TransferBatch) (common.Hash, error) + GenerateMessageHash(batch *batchProcessor.ArgListsBatch, batchId uint64) (common.Hash, error) BroadcastSignatureForMessageHash(msgHash common.Hash) - ExecuteTransfer(ctx context.Context, msgHash common.Hash, batch *clients.TransferBatch, quorum int) (string, error) + ExecuteTransfer(ctx context.Context, msgHash common.Hash, batch *batchProcessor.ArgListsBatch, batchId uint64, quorum int) (string, error) GetTransactionsStatuses(ctx context.Context, batchId uint64) ([]byte, error) GetQuorumSize(ctx context.Context) (*big.Int, error) IsQuorumReached(ctx context.Context, msgHash common.Hash) (bool, error) + GetBatchSCMetadata(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) CheckClientAvailability(ctx context.Context) error + CheckRequiredBalance(ctx context.Context, erc20Address common.Address, value *big.Int) error + TotalBalances(ctx context.Context, token common.Address) (*big.Int, error) + MintBalances(ctx context.Context, token common.Address) (*big.Int, error) + BurnBalances(ctx context.Context, token common.Address) (*big.Int, error) + MintBurnTokens(ctx context.Context, token common.Address) (bool, error) + NativeTokens(ctx context.Context, token common.Address) (bool, error) + WhitelistedTokens(ctx context.Context, token common.Address) (bool, error) IsInterfaceNil() bool } @@ -60,3 +78,9 @@ type SignaturesHolder interface { ClearStoredSignatures() IsInterfaceNil() bool } + +// BalanceValidator defines the operations for a component that can validate the balances on both chains for a provided token +type BalanceValidator interface { + CheckToken(ctx context.Context, ethToken common.Address, mvxToken []byte, amount *big.Int, direction batchProcessor.Direction) error + IsInterfaceNil() bool +} diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/constants.go b/bridges/ethMultiversX/steps/ethToMultiversX/constants.go index 4aec9996..32cf2aa5 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/constants.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/constants.go @@ -4,7 +4,7 @@ const ( // GettingPendingBatchFromEthereum is the step identifier for fetching the pending batch from the Ethereum chain GettingPendingBatchFromEthereum = "get pending batch from Ethereum" - // ProposingTransferOnMultiversX is the step idetifier for proposing transfer on MultiversX + // ProposingTransferOnMultiversX is the step identifier for proposing transfer on MultiversX ProposingTransferOnMultiversX = "propose transfer" // SigningProposedTransferOnMultiversX is the step identifier for signing proposed transfer diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/semiIntegrated_test.go b/bridges/ethMultiversX/steps/ethToMultiversX/semiIntegrated_test.go index 0dfda84e..f7c57987 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/semiIntegrated_test.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/semiIntegrated_test.go @@ -7,8 +7,7 @@ import ( "testing" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/multiversx/mx-bridge-eth-go/testsCommon/stateMachine" "github.com/stretchr/testify/assert" @@ -19,7 +18,6 @@ const ( getAndStoreBatchFromEthereum = "GetAndStoreBatchFromEthereum" getLastExecutedEthBatchIDFromMultiversX = "GetLastExecutedEthBatchIDFromMultiversX" verifyLastDepositNonceExecutedOnEthereumBatch = "VerifyLastDepositNonceExecutedOnEthereumBatch" - validateBatch = "ValidateBatch" wasTransferProposedOnMultiversX = "WasTransferProposedOnMultiversX" wasActionSignedOnMultiversX = "WasActionSignedOnMultiversX" signActionOnMultiversX = "SignActionOnMultiversX" @@ -80,8 +78,8 @@ func createMockBridge(args argsBridgeStub) (*bridgeTests.BridgeExecutorStub, *er return errHandler.storeAndReturnError(nil) } - stub.GetStoredBatchCalled = func() *clients.TransferBatch { - return &clients.TransferBatch{} + stub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { + return &bridgeCore.TransferBatch{} } stub.GetLastExecutedEthBatchIDFromMultiversXCalled = func(ctx context.Context) (uint64, error) { if args.failingStep == getLastExecutedEthBatchIDFromMultiversX { @@ -149,17 +147,11 @@ func createMockBridge(args argsBridgeStub) (*bridgeTests.BridgeExecutorStub, *er stub.ProcessMaxQuorumRetriesOnMultiversXCalled = func() bool { return args.maxRetriesReachedHandler() } - stub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - if args.failingStep == validateBatch { - return false, errHandler.storeAndReturnError(expectedErr) - } - return args.validateBatchHandler(), errHandler.storeAndReturnError(nil) - } return stub, errHandler } -func createStateMachine(t *testing.T, executor steps.Executor, initialStep core.StepIdentifier) *stateMachine.StateMachineMock { +func createStateMachine(t *testing.T, executor steps.Executor, initialStep bridgeCore.StepIdentifier) *stateMachine.StateMachineMock { stepsSlice, err := CreateSteps(executor) require.Nil(t, err) @@ -259,12 +251,11 @@ func TestHappyCaseWhenLeaderAndActionIdNotPerformed(t *testing.T) { } func TestOneStepErrors_ShouldReturnToPendingBatch(t *testing.T) { - stepsThatCanError := []core.StepIdentifier{ + stepsThatCanError := []bridgeCore.StepIdentifier{ getAndStoreActionIDForProposeTransferOnMultiversX, getAndStoreBatchFromEthereum, getLastExecutedEthBatchIDFromMultiversX, verifyLastDepositNonceExecutedOnEthereumBatch, - validateBatch, wasTransferProposedOnMultiversX, proposeTransferOnMultiversX, wasTransferProposedOnMultiversX, @@ -279,7 +270,7 @@ func TestOneStepErrors_ShouldReturnToPendingBatch(t *testing.T) { } } -func testErrorFlow(t *testing.T, stepThatErrors core.StepIdentifier) { +func testErrorFlow(t *testing.T, stepThatErrors bridgeCore.StepIdentifier) { numCalled := 0 args := argsBridgeStub{ failingStep: string(stepThatErrors), diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending.go b/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending.go index bffedc67..8b88544d 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending.go @@ -2,10 +2,10 @@ package ethtomultiversx import ( "context" - "encoding/json" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps" "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" logger "github.com/multiversx/mx-chain-logger-go" ) @@ -42,18 +42,6 @@ func (step *getPendingStep) Execute(ctx context.Context) core.StepIdentifier { return step.Identifier() } - isValid, err := step.bridge.ValidateBatch(ctx, batch) - if err != nil { - body, _ := json.Marshal(batch) - step.bridge.PrintInfo(logger.LogError, "error validating Ethereum batch", "error", err, "batch", string(body)) - return step.Identifier() - } - - if !isValid { - step.bridge.PrintInfo(logger.LogError, "batch not valid "+batch.String()) - return step.Identifier() - } - step.bridge.PrintInfo(logger.LogInfo, "fetched new batch from Ethereum "+batch.String()) err = step.bridge.VerifyLastDepositNonceExecutedOnEthereumBatch(ctx) @@ -62,6 +50,13 @@ func (step *getPendingStep) Execute(ctx context.Context) core.StepIdentifier { return step.Identifier() } + argLists := batchProcessor.ExtractListEthToMvx(batch) + err = step.bridge.CheckAvailableTokens(ctx, argLists.EthTokens, argLists.MvxTokenBytes, argLists.Amounts, argLists.Direction) + if err != nil { + step.bridge.PrintInfo(logger.LogError, "error checking available tokens", "error", err, "batch", batch.String()) + return step.Identifier() + } + return ProposingTransferOnMultiversX } diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go b/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go index 3ba437a1..52aefb52 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/step01GetPending_test.go @@ -3,19 +3,31 @@ package ethtomultiversx import ( "context" "errors" + "math/big" "testing" - "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/stretchr/testify/assert" ) var expectedError = errors.New("expected error") -var testBatch = &clients.TransferBatch{ - ID: 112233, - Deposits: nil, - Statuses: nil, +var testBatch = &bridgeCore.TransferBatch{ + ID: 112233, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: 0, + ToBytes: []byte("to"), + FromBytes: []byte("from"), + SourceTokenBytes: []byte("source token"), + DestinationTokenBytes: []byte("destination token"), + Amount: big.NewInt(37), + }, + }, + Statuses: []byte{0}, } func TestExecuteGetPending(t *testing.T) { @@ -36,7 +48,6 @@ func TestExecuteGetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - t.Run("error on GetAndStoreBatchFromEthereum", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() @@ -55,7 +66,6 @@ func TestExecuteGetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - t.Run("nil on GetStoredBatch", func(t *testing.T) { bridgeStub := createStubExecutor() bridgeStub.GetLastExecutedEthBatchIDFromMultiversXCalled = func(ctx context.Context) (uint64, error) { @@ -64,7 +74,7 @@ func TestExecuteGetPending(t *testing.T) { bridgeStub.GetAndStoreBatchFromEthereumCalled = func(ctx context.Context, nonce uint64) error { return nil } - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return nil } @@ -76,8 +86,7 @@ func TestExecuteGetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - - t.Run("error on ValidateBatch", func(t *testing.T) { + t.Run("error on VerifyLastDepositNonceExecutedOnEthereumBatch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() bridgeStub.GetLastExecutedEthBatchIDFromMultiversXCalled = func(ctx context.Context) (uint64, error) { @@ -86,11 +95,11 @@ func TestExecuteGetPending(t *testing.T) { bridgeStub.GetAndStoreBatchFromEthereumCalled = func(ctx context.Context, nonce uint64) error { return nil } - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } - bridgeStub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return false, expectedError + bridgeStub.VerifyLastDepositNonceExecutedOnEthereumBatchCalled = func(ctx context.Context) error { + return expectedError } step := getPendingStep{ @@ -101,49 +110,23 @@ func TestExecuteGetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - - t.Run("batch not validated on ValidateBatch", func(t *testing.T) { + t.Run("error on CheckAvailableTokens", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetLastExecutedEthBatchIDFromMultiversXCalled = func(ctx context.Context) (uint64, error) { - return 1122, nil - } - bridgeStub.GetAndStoreBatchFromEthereumCalled = func(ctx context.Context, nonce uint64) error { - return nil - } - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { - return testBatch - } - bridgeStub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return false, nil - } - - step := getPendingStep{ - bridge: bridgeStub, + bridgeStub.CheckAvailableTokensCalled = func(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + return expectedError } - - expectedStepIdentifier := step.Identifier() - stepIdentifier := step.Execute(context.Background()) - assert.Equal(t, expectedStepIdentifier, stepIdentifier) - }) - - t.Run("error on VerifyLastDepositNonceExecutedOnEthereumBatch", func(t *testing.T) { - t.Parallel() - bridgeStub := createStubExecutor() bridgeStub.GetLastExecutedEthBatchIDFromMultiversXCalled = func(ctx context.Context) (uint64, error) { return 1122, nil } bridgeStub.GetAndStoreBatchFromEthereumCalled = func(ctx context.Context, nonce uint64) error { return nil } - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } - bridgeStub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return true, nil - } bridgeStub.VerifyLastDepositNonceExecutedOnEthereumBatchCalled = func(ctx context.Context) error { - return expectedError + return nil } step := getPendingStep{ @@ -154,7 +137,6 @@ func TestExecuteGetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - t.Run("should work", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() @@ -164,14 +146,16 @@ func TestExecuteGetPending(t *testing.T) { bridgeStub.GetAndStoreBatchFromEthereumCalled = func(ctx context.Context, nonce uint64) error { return nil } - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.VerifyLastDepositNonceExecutedOnEthereumBatchCalled = func(ctx context.Context) error { return nil } - bridgeStub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return true, nil + checkAvailableTokensCalled := false + bridgeStub.CheckAvailableTokensCalled = func(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + checkAvailableTokensCalled = true + return nil } step := getPendingStep{ @@ -188,6 +172,7 @@ func TestExecuteGetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) assert.Equal(t, testBatch, step.bridge.GetStoredBatch()) + assert.True(t, checkAvailableTokensCalled) }) } diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/step02ProposeTransfer_test.go b/bridges/ethMultiversX/steps/ethToMultiversX/step02ProposeTransfer_test.go index ac03e6fa..c4ff2936 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/step02ProposeTransfer_test.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/step02ProposeTransfer_test.go @@ -4,8 +4,7 @@ import ( "context" "testing" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" "github.com/stretchr/testify/assert" ) @@ -15,7 +14,7 @@ func TestExecuteProposeTransfer(t *testing.T) { t.Run("nil batch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return nil } @@ -23,7 +22,7 @@ func TestExecuteProposeTransfer(t *testing.T) { bridge: bridgeStub, } - expectedStepIdentifier := core.StepIdentifier(GettingPendingBatchFromEthereum) + expectedStepIdentifier := bridgeCore.StepIdentifier(GettingPendingBatchFromEthereum) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) @@ -31,7 +30,7 @@ func TestExecuteProposeTransfer(t *testing.T) { t.Run("error on WasTransferProposedOnMultiversX", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasTransferProposedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -42,7 +41,7 @@ func TestExecuteProposeTransfer(t *testing.T) { bridge: bridgeStub, } - expectedStepIdentifier := core.StepIdentifier(GettingPendingBatchFromEthereum) + expectedStepIdentifier := bridgeCore.StepIdentifier(GettingPendingBatchFromEthereum) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) @@ -50,7 +49,7 @@ func TestExecuteProposeTransfer(t *testing.T) { t.Run("not leader", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasTransferProposedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -72,7 +71,7 @@ func TestExecuteProposeTransfer(t *testing.T) { t.Run("error on ProposeTransferOnMultiversX", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasTransferProposedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -89,7 +88,7 @@ func TestExecuteProposeTransfer(t *testing.T) { bridge: bridgeStub, } - expectedStepIdentifier := core.StepIdentifier(GettingPendingBatchFromEthereum) + expectedStepIdentifier := bridgeCore.StepIdentifier(GettingPendingBatchFromEthereum) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) @@ -97,7 +96,7 @@ func TestExecuteProposeTransfer(t *testing.T) { t.Run("should work - transfer already proposed", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasTransferProposedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -108,7 +107,7 @@ func TestExecuteProposeTransfer(t *testing.T) { bridge: bridgeStub, } - expectedStepIdentifier := core.StepIdentifier(SigningProposedTransferOnMultiversX) + expectedStepIdentifier := bridgeCore.StepIdentifier(SigningProposedTransferOnMultiversX) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) @@ -116,7 +115,7 @@ func TestExecuteProposeTransfer(t *testing.T) { t.Run("should work", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasTransferProposedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -135,7 +134,7 @@ func TestExecuteProposeTransfer(t *testing.T) { // Test IsInterfaceNil assert.NotNil(t, step.IsInterfaceNil()) - expectedStepIdentifier := core.StepIdentifier(SigningProposedTransferOnMultiversX) + expectedStepIdentifier := bridgeCore.StepIdentifier(SigningProposedTransferOnMultiversX) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) diff --git a/bridges/ethMultiversX/steps/ethToMultiversX/step03SignProposedTransfer_test.go b/bridges/ethMultiversX/steps/ethToMultiversX/step03SignProposedTransfer_test.go index b0f2751d..1791c0a8 100644 --- a/bridges/ethMultiversX/steps/ethToMultiversX/step03SignProposedTransfer_test.go +++ b/bridges/ethMultiversX/steps/ethToMultiversX/step03SignProposedTransfer_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" - "github.com/multiversx/mx-bridge-eth-go/clients" "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" "github.com/stretchr/testify/assert" ) @@ -17,7 +17,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Run("nil batch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return nil } @@ -33,7 +33,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Run("error on WasProposedTransferSigned", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasActionSignedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -52,7 +52,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Run("error on SignProposedTransfer", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasActionSignedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -75,7 +75,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Parallel() expectedErr := errors.New("expected error") bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasActionSignedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -97,7 +97,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Run("invalid action ID", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasActionSignedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -119,7 +119,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Run("error on WasActionSignedOnMultiversX", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasActionSignedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -141,7 +141,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Run("should work - transfer was already signed", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasActionSignedOnMultiversXCalled = func(ctx context.Context) (bool, error) { @@ -163,7 +163,7 @@ func TestExecuteSignProposedTransferStep(t *testing.T) { t.Run("should work", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutor() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } bridgeStub.WasActionSignedOnMultiversXCalled = func(ctx context.Context) (bool, error) { diff --git a/bridges/ethMultiversX/steps/interface.go b/bridges/ethMultiversX/steps/interface.go index d842b6c3..779649ae 100644 --- a/bridges/ethMultiversX/steps/interface.go +++ b/bridges/ethMultiversX/steps/interface.go @@ -2,8 +2,11 @@ package steps import ( "context" + "math/big" - "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/ethereum/go-ethereum/common" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" logger "github.com/multiversx/mx-chain-logger-go" ) @@ -12,9 +15,10 @@ type Executor interface { PrintInfo(logLevel logger.LogLevel, message string, extras ...interface{}) MyTurnAsLeader() bool - GetBatchFromMultiversX(ctx context.Context) (*clients.TransferBatch, error) - StoreBatchFromMultiversX(batch *clients.TransferBatch) error - GetStoredBatch() *clients.TransferBatch + GetBatchFromMultiversX(ctx context.Context) (*bridgeCore.TransferBatch, error) + StoreBatchFromMultiversX(batch *bridgeCore.TransferBatch) error + GetStoredBatch() *bridgeCore.TransferBatch + GetLastExecutedEthBatchIDFromMultiversX(ctx context.Context) (uint64, error) VerifyLastDepositNonceExecutedOnEthereumBatch(ctx context.Context) error @@ -54,9 +58,9 @@ type Executor interface { ResetRetriesCountOnEthereum() ClearStoredP2PSignaturesForEthereum() - ValidateBatch(ctx context.Context, batch *clients.TransferBatch) (bool, error) CheckMultiversXClientAvailability(ctx context.Context) error CheckEthereumClientAvailability(ctx context.Context) error + CheckAvailableTokens(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error IsInterfaceNil() bool } diff --git a/bridges/ethMultiversX/steps/multiversxToEth/semiIntegrated_test.go b/bridges/ethMultiversX/steps/multiversxToEth/semiIntegrated_test.go index e511bf21..37334d45 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/semiIntegrated_test.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/semiIntegrated_test.go @@ -7,8 +7,7 @@ import ( "testing" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/multiversx/mx-bridge-eth-go/testsCommon/stateMachine" "github.com/stretchr/testify/assert" @@ -68,7 +67,7 @@ func (eh *errorHandler) storeAndReturnError(err error) error { return err } -func createStateMachine(t *testing.T, executor steps.Executor, initialStep core.StepIdentifier) *stateMachine.StateMachineMock { +func createStateMachine(t *testing.T, executor steps.Executor, initialStep bridgeCore.StepIdentifier) *stateMachine.StateMachineMock { stepsSlice, err := CreateSteps(executor) require.Nil(t, err) @@ -96,17 +95,17 @@ func createMockBridge(args argsBridgeStub) (*bridgeTests.BridgeExecutorStub, *er stub.GetStoredActionIDCalled = func() uint64 { return 2 } - stub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*clients.TransferBatch, error) { + stub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*bridgeCore.TransferBatch, error) { if args.failingStep == getBatchFromMultiversX { - return &clients.TransferBatch{}, errHandler.storeAndReturnError(expectedErr) + return &bridgeCore.TransferBatch{}, errHandler.storeAndReturnError(expectedErr) } - return &clients.TransferBatch{}, errHandler.storeAndReturnError(nil) + return &bridgeCore.TransferBatch{}, errHandler.storeAndReturnError(nil) } - stub.StoreBatchFromMultiversXCalled = func(batch *clients.TransferBatch) error { + stub.StoreBatchFromMultiversXCalled = func(batch *bridgeCore.TransferBatch) error { return nil } - stub.GetStoredBatchCalled = func() *clients.TransferBatch { - return &clients.TransferBatch{} + stub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { + return &bridgeCore.TransferBatch{} } stub.WasTransferPerformedOnEthereumCalled = func(ctx context.Context) (bool, error) { if args.failingStep == wasTransferPerformedOnEthereum { @@ -209,9 +208,6 @@ func createMockBridge(args argsBridgeStub) (*bridgeTests.BridgeExecutorStub, *er stub.ProcessMaxQuorumRetriesOnEthereumCalled = func() bool { return args.maxRetriesReachedMultiversXHandler() } - stub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return true, nil - } return stub, errHandler } @@ -272,7 +268,7 @@ func TestHappyCaseWhenLeaderSetStatusAlreadySigned(t *testing.T) { } func TestOneStepErrors_ShouldReturnToPendingBatch(t *testing.T) { - stepsThatCanError := []core.StepIdentifier{ + stepsThatCanError := []bridgeCore.StepIdentifier{ getBatchFromMultiversX, wasTransferPerformedOnEthereum, signTransferOnEthereum, @@ -293,7 +289,7 @@ func TestOneStepErrors_ShouldReturnToPendingBatch(t *testing.T) { } } -func testErrorFlow(t *testing.T, stepThatErrors core.StepIdentifier) { +func testErrorFlow(t *testing.T, stepThatErrors bridgeCore.StepIdentifier) { t.Logf("\n\n\nnew test for stepThatError: %s", stepThatErrors) numCalled := 0 args := argsBridgeStub{ diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending.go b/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending.go index d1b96888..3e66d430 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending.go @@ -2,10 +2,10 @@ package multiversxtoeth import ( "context" - "encoding/json" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps" "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" logger "github.com/multiversx/mx-chain-logger-go" ) @@ -42,18 +42,6 @@ func (step *getPendingStep) Execute(ctx context.Context) core.StepIdentifier { return step.Identifier() } - isValid, err := step.bridge.ValidateBatch(ctx, batch) - if err != nil { - body, _ := json.Marshal(batch) - step.bridge.PrintInfo(logger.LogError, "error validating MultiversX batch", "error", err, "batch", string(body)) - return step.Identifier() - } - - if !isValid { - step.bridge.PrintInfo(logger.LogError, "batch not valid "+batch.String()) - return step.Identifier() - } - step.bridge.PrintInfo(logger.LogInfo, "fetched new batch from MultiversX "+batch.String()) wasPerformed, err := step.bridge.WasTransferPerformedOnEthereum(ctx) @@ -66,6 +54,13 @@ func (step *getPendingStep) Execute(ctx context.Context) core.StepIdentifier { return ResolvingSetStatusOnMultiversX } + argLists := batchProcessor.ExtractListMvxToEth(batch) + err = step.bridge.CheckAvailableTokens(ctx, argLists.EthTokens, argLists.MvxTokenBytes, argLists.Amounts, argLists.Direction) + if err != nil { + step.bridge.PrintInfo(logger.LogError, "error checking available tokens", "error", err, "batch", batch.String()) + return step.Identifier() + } + return SigningProposedTransferOnEthereum } diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending_test.go b/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending_test.go index 2202f9be..55c097f2 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending_test.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step01GetPending_test.go @@ -3,16 +3,18 @@ package multiversxtoeth import ( "context" "errors" + "math/big" "testing" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/ethereum/go-ethereum/common" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/stretchr/testify/assert" ) var expectedError = errors.New("expected error") -var testBatch = &clients.TransferBatch{ +var testBatch = &bridgeCore.TransferBatch{ ID: 112233, Deposits: nil, Statuses: nil, @@ -24,7 +26,7 @@ func TestExecute_GetPending(t *testing.T) { t.Run("error on GetBatchFromMultiversX", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorGetPending() - bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*clients.TransferBatch, error) { + bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*bridgeCore.TransferBatch, error) { return nil, expectedError } @@ -36,11 +38,10 @@ func TestExecute_GetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - t.Run("nil batch on GetBatchFromMultiversX", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorGetPending() - bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*clients.TransferBatch, error) { + bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*bridgeCore.TransferBatch, error) { return nil, nil } @@ -52,11 +53,10 @@ func TestExecute_GetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - t.Run("error on StoreBatchFromMultiversX", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorGetPending() - bridgeStub.StoreBatchFromMultiversXCalled = func(batch *clients.TransferBatch) error { + bridgeStub.StoreBatchFromMultiversXCalled = func(batch *bridgeCore.TransferBatch) error { return expectedError } @@ -68,11 +68,10 @@ func TestExecute_GetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - - t.Run("error on ValidateBatch", func(t *testing.T) { + t.Run("error on WasTransferPerformedOnEthereum", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorGetPending() - bridgeStub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { + bridgeStub.WasTransferPerformedOnEthereumCalled = func(ctx context.Context) (bool, error) { return false, expectedError } @@ -84,28 +83,14 @@ func TestExecute_GetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - - t.Run("batch not validated on ValidateBatch", func(t *testing.T) { - t.Parallel() - bridgeStub := createStubExecutorGetPending() - bridgeStub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return false, nil - } - - step := getPendingStep{ - bridge: bridgeStub, - } - - expectedStepIdentifier := step.Identifier() - stepIdentifier := step.Execute(context.Background()) - assert.Equal(t, expectedStepIdentifier, stepIdentifier) - }) - t.Run("error on WasTransferPerformedOnEthereum", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorGetPending() bridgeStub.WasTransferPerformedOnEthereumCalled = func(ctx context.Context) (bool, error) { - return false, expectedError + return false, nil + } + bridgeStub.CheckAvailableTokensCalled = func(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + return expectedError } step := getPendingStep{ @@ -116,7 +101,6 @@ func TestExecute_GetPending(t *testing.T) { stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) - t.Run("should work", func(t *testing.T) { t.Parallel() t.Run("if transfer already performed next step should be ResolvingSetStatusOnMultiversX", func(t *testing.T) { @@ -125,6 +109,11 @@ func TestExecute_GetPending(t *testing.T) { bridgeStub.WasTransferPerformedOnEthereumCalled = func(ctx context.Context) (bool, error) { return true, nil } + checkAvailableTokensCalled := false + bridgeStub.CheckAvailableTokensCalled = func(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + checkAvailableTokensCalled = true + return nil + } step := getPendingStep{ bridge: bridgeStub, @@ -132,9 +121,10 @@ func TestExecute_GetPending(t *testing.T) { assert.False(t, step.IsInterfaceNil()) - expectedStepIdentifier := core.StepIdentifier(ResolvingSetStatusOnMultiversX) + expectedStepIdentifier := bridgeCore.StepIdentifier(ResolvingSetStatusOnMultiversX) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) + assert.False(t, checkAvailableTokensCalled) }) t.Run("if transfer was not performed next step should be SigningProposedTransferOnEthereum", func(t *testing.T) { t.Parallel() @@ -142,28 +132,31 @@ func TestExecute_GetPending(t *testing.T) { bridgeStub.WasTransferPerformedOnEthereumCalled = func(ctx context.Context) (bool, error) { return false, nil } + checkAvailableTokensCalled := false + bridgeStub.CheckAvailableTokensCalled = func(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + checkAvailableTokensCalled = true + return nil + } step := getPendingStep{ bridge: bridgeStub, } - expectedStepIdentifier := core.StepIdentifier(SigningProposedTransferOnEthereum) + expectedStepIdentifier := bridgeCore.StepIdentifier(SigningProposedTransferOnEthereum) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) + assert.True(t, checkAvailableTokensCalled) }) }) } func createStubExecutorGetPending() *bridgeTests.BridgeExecutorStub { stub := bridgeTests.NewBridgeExecutorStub() - stub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*clients.TransferBatch, error) { + stub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*bridgeCore.TransferBatch, error) { return testBatch, nil } - stub.StoreBatchFromMultiversXCalled = func(batch *clients.TransferBatch) error { + stub.StoreBatchFromMultiversXCalled = func(batch *bridgeCore.TransferBatch) error { return nil } - stub.ValidateBatchCalled = func(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - return true, nil - } return stub } diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step02SignProposedTransfer_test.go b/bridges/ethMultiversX/steps/multiversxToEth/step02SignProposedTransfer_test.go index 0163f6e9..a0f4a9a4 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step02SignProposedTransfer_test.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step02SignProposedTransfer_test.go @@ -4,13 +4,12 @@ import ( "context" "testing" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/stretchr/testify/assert" ) -var initialStep = core.StepIdentifier(GettingPendingBatchFromMultiversX) +var initialStep = bridgeCore.StepIdentifier(GettingPendingBatchFromMultiversX) func TestExecute_SignProposedTransfer(t *testing.T) { t.Parallel() @@ -18,7 +17,7 @@ func TestExecute_SignProposedTransfer(t *testing.T) { t.Run("nil batch on GetStoredBatch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorSignProposedTransfer() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return nil } @@ -55,7 +54,7 @@ func TestExecute_SignProposedTransfer(t *testing.T) { assert.False(t, step.IsInterfaceNil()) - expectedStepIdentifier := core.StepIdentifier(WaitingForQuorumOnTransfer) + expectedStepIdentifier := bridgeCore.StepIdentifier(WaitingForQuorumOnTransfer) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStepIdentifier, stepIdentifier) }) @@ -63,7 +62,7 @@ func TestExecute_SignProposedTransfer(t *testing.T) { func createStubExecutorSignProposedTransfer() *bridgeTests.BridgeExecutorStub { stub := bridgeTests.NewBridgeExecutorStub() - stub.GetStoredBatchCalled = func() *clients.TransferBatch { + stub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } stub.SignTransferOnEthereumCalled = func() error { diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus.go b/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus.go index 4c0b7a18..6ffc9464 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus.go @@ -2,8 +2,10 @@ package multiversxtoeth import ( "context" + "errors" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps" + "github.com/multiversx/mx-bridge-eth-go/clients" "github.com/multiversx/mx-bridge-eth-go/core" logger "github.com/multiversx/mx-chain-logger-go" ) @@ -22,12 +24,13 @@ func (step *resolveSetStatusStep) Execute(ctx context.Context) core.StepIdentifi } batch, err := step.bridge.GetBatchFromMultiversX(ctx) - if err != nil { - step.bridge.PrintInfo(logger.LogError, "error while fetching batch", "error", err) + isEmptyBatch := batch == nil || (err != nil && errors.Is(err, clients.ErrNoPendingBatchAvailable)) + if isEmptyBatch { + step.bridge.PrintInfo(logger.LogDebug, "nil/empty batch fetched") return GettingPendingBatchFromMultiversX } - if batch == nil { - step.bridge.PrintInfo(logger.LogDebug, "nil batch fetched") + if err != nil { + step.bridge.PrintInfo(logger.LogError, "error while fetching batch", "error", err) return GettingPendingBatchFromMultiversX } diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus_test.go b/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus_test.go index efffb0ba..dcb81d9c 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus_test.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step06ResolveSetStatus_test.go @@ -4,8 +4,7 @@ import ( "context" "testing" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/stretchr/testify/assert" ) @@ -16,7 +15,7 @@ func TestExecute_ResolveSetStatus(t *testing.T) { t.Run("nil batch on GetStoredBatch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorResolveSetStatus() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return nil } clearWasCalled := false @@ -35,7 +34,7 @@ func TestExecute_ResolveSetStatus(t *testing.T) { t.Run("error on GetStoredBatch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorResolveSetStatus() - bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*clients.TransferBatch, error) { + bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*bridgeCore.TransferBatch, error) { return nil, expectedError } clearWasCalled := false @@ -54,7 +53,7 @@ func TestExecute_ResolveSetStatus(t *testing.T) { t.Run("nil batch on GetBatchFromMultiversX", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorResolveSetStatus() - bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*clients.TransferBatch, error) { + bridgeStub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*bridgeCore.TransferBatch, error) { return nil, nil } clearWasCalled := false @@ -101,7 +100,7 @@ func TestExecute_ResolveSetStatus(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorResolveSetStatus() bridgeStub.WaitAndReturnFinalBatchStatusesCalled = func(ctx context.Context) []byte { - return []byte{clients.Executed, clients.Rejected} + return []byte{bridgeCore.Executed, bridgeCore.Rejected} } wasCalled := false @@ -119,7 +118,7 @@ func TestExecute_ResolveSetStatus(t *testing.T) { assert.False(t, step.IsInterfaceNil()) - expectedStep := core.StepIdentifier(ProposingSetStatusOnMultiversX) + expectedStep := bridgeCore.StepIdentifier(ProposingSetStatusOnMultiversX) stepIdentifier := step.Execute(context.Background()) assert.True(t, wasCalled) assert.NotEqual(t, step.Identifier(), stepIdentifier) @@ -130,10 +129,10 @@ func TestExecute_ResolveSetStatus(t *testing.T) { func createStubExecutorResolveSetStatus() *bridgeTests.BridgeExecutorStub { stub := bridgeTests.NewBridgeExecutorStub() - stub.GetStoredBatchCalled = func() *clients.TransferBatch { + stub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } - stub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*clients.TransferBatch, error) { + stub.GetBatchFromMultiversXCalled = func(ctx context.Context) (*bridgeCore.TransferBatch, error) { return testBatch, nil } return stub diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step07ProposeSetStatus_test.go b/bridges/ethMultiversX/steps/multiversxToEth/step07ProposeSetStatus_test.go index f31e8564..b636be9b 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step07ProposeSetStatus_test.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step07ProposeSetStatus_test.go @@ -4,8 +4,7 @@ import ( "context" "testing" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/stretchr/testify/assert" ) @@ -15,7 +14,7 @@ func TestExecute_ProposeSetStatus(t *testing.T) { t.Run("nil batch on GetStoredBatch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorProposeSetStatus() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return nil } @@ -86,7 +85,7 @@ func TestExecute_ProposeSetStatus(t *testing.T) { } assert.False(t, step.IsInterfaceNil()) - expectedStep := core.StepIdentifier(SigningProposedSetStatusOnMultiversX) + expectedStep := bridgeCore.StepIdentifier(SigningProposedSetStatusOnMultiversX) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStep, stepIdentifier) @@ -115,7 +114,7 @@ func TestExecute_ProposeSetStatus(t *testing.T) { bridge: bridgeStub, } - expectedStep := core.StepIdentifier(SigningProposedSetStatusOnMultiversX) + expectedStep := bridgeCore.StepIdentifier(SigningProposedSetStatusOnMultiversX) stepIdentifier := step.Execute(context.Background()) assert.Equal(t, expectedStep, stepIdentifier) @@ -127,7 +126,7 @@ func TestExecute_ProposeSetStatus(t *testing.T) { func createStubExecutorProposeSetStatus() *bridgeTests.BridgeExecutorStub { stub := bridgeTests.NewBridgeExecutorStub() - stub.GetStoredBatchCalled = func() *clients.TransferBatch { + stub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } stub.WasSetStatusProposedOnMultiversXCalled = func(ctx context.Context) (bool, error) { diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus.go b/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus.go index a602d5b4..7a50c0cd 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus.go @@ -47,7 +47,7 @@ func (step *signProposedSetStatusStep) Execute(ctx context.Context) core.StepIde err = step.bridge.SignActionOnMultiversX(ctx) if err != nil { - step.bridge.PrintInfo(logger.LogError, "error signing the proposed transfer", + step.bridge.PrintInfo(logger.LogError, "error signing the proposed set status", "batch ID", storedBatch.ID, "error", err) return GettingPendingBatchFromMultiversX } diff --git a/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus_test.go b/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus_test.go index 1e7e60f9..8c64681d 100644 --- a/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus_test.go +++ b/bridges/ethMultiversX/steps/multiversxToEth/step08SignProposedSetStatus_test.go @@ -5,8 +5,7 @@ import ( "testing" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/stretchr/testify/assert" ) @@ -18,7 +17,7 @@ func TestExecute_SignProposedSetStatus(t *testing.T) { t.Run("nil batch on GetStoredBatch", func(t *testing.T) { t.Parallel() bridgeStub := createStubExecutorSignProposedSetStatus() - bridgeStub.GetStoredBatchCalled = func() *clients.TransferBatch { + bridgeStub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return nil } @@ -104,7 +103,7 @@ func TestExecute_SignProposedSetStatus(t *testing.T) { bridge: bridgeStub, } - expectedStep := core.StepIdentifier(WaitingForQuorumOnSetStatus) + expectedStep := bridgeCore.StepIdentifier(WaitingForQuorumOnSetStatus) stepIdentifier := step.Execute(context.Background()) assert.False(t, wasCalled) assert.Equal(t, expectedStep, stepIdentifier) @@ -123,7 +122,7 @@ func TestExecute_SignProposedSetStatus(t *testing.T) { } assert.False(t, step.IsInterfaceNil()) - expectedStep := core.StepIdentifier(WaitingForQuorumOnSetStatus) + expectedStep := bridgeCore.StepIdentifier(WaitingForQuorumOnSetStatus) stepIdentifier := step.Execute(context.Background()) assert.True(t, wasCalled) assert.NotEqual(t, step.Identifier(), stepIdentifier) @@ -135,7 +134,7 @@ func TestExecute_SignProposedSetStatus(t *testing.T) { func createStubExecutorSignProposedSetStatus() *bridgeTests.BridgeExecutorStub { stub := bridgeTests.NewBridgeExecutorStub() - stub.GetStoredBatchCalled = func() *clients.TransferBatch { + stub.GetStoredBatchCalled = func() *bridgeCore.TransferBatch { return testBatch } stub.GetAndStoreActionIDForProposeSetStatusFromMultiversXCalled = func(ctx context.Context) (uint64, error) { diff --git a/bridges/ethMultiversX/topology/topologyHandler.go b/bridges/ethMultiversX/topology/topologyHandler.go index 49e69534..d3fed22d 100644 --- a/bridges/ethMultiversX/topology/topologyHandler.go +++ b/bridges/ethMultiversX/topology/topologyHandler.go @@ -69,9 +69,9 @@ func (t *topologyHandler) MyTurnAsLeader() bool { } t.log.Debug(msg, - "leader", t.addressConverter.ToBech32String(leaderAddress), + "leader", t.addressConverter.ToBech32StringSilent(leaderAddress), "index", index, - "self address", t.addressConverter.ToBech32String(t.addressBytes)) + "self address", t.addressConverter.ToBech32StringSilent(t.addressBytes)) return isLeader } diff --git a/clients/balanceValidator/balanceValidator.go b/clients/balanceValidator/balanceValidator.go new file mode 100644 index 00000000..0824dc41 --- /dev/null +++ b/clients/balanceValidator/balanceValidator.go @@ -0,0 +1,330 @@ +package balanceValidator + +import ( + "bytes" + "context" + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/clients" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" + "github.com/multiversx/mx-chain-core-go/core/check" + logger "github.com/multiversx/mx-chain-logger-go" +) + +// ArgsBalanceValidator represents the DTO struct used in the NewBalanceValidator constructor function +type ArgsBalanceValidator struct { + Log logger.Logger + MultiversXClient MultiversXClient + EthereumClient EthereumClient +} + +type balanceValidator struct { + log logger.Logger + multiversXClient MultiversXClient + ethereumClient EthereumClient +} + +// NewBalanceValidator creates a new instance of type balanceValidator +func NewBalanceValidator(args ArgsBalanceValidator) (*balanceValidator, error) { + err := checkArgs(args) + if err != nil { + return nil, err + } + + return &balanceValidator{ + log: args.Log, + multiversXClient: args.MultiversXClient, + ethereumClient: args.EthereumClient, + }, nil +} + +func checkArgs(args ArgsBalanceValidator) error { + if check.IfNil(args.Log) { + return ErrNilLogger + } + if check.IfNil(args.MultiversXClient) { + return ErrNilMultiversXClient + } + if check.IfNil(args.EthereumClient) { + return ErrNilEthereumClient + } + + return nil +} + +// CheckToken returns error if the bridge can not happen to the provided token due to faulty balance values in the contracts +func (validator *balanceValidator) CheckToken(ctx context.Context, ethToken common.Address, mvxToken []byte, amount *big.Int, direction batchProcessor.Direction) error { + err := validator.checkRequiredBalance(ctx, ethToken, mvxToken, amount, direction) + if err != nil { + return err + } + + isMintBurnOnEthereum, err := validator.isMintBurnOnEthereum(ctx, ethToken) + if err != nil { + return err + } + + isMintBurnOnMultiversX, err := validator.isMintBurnOnMultiversX(ctx, mvxToken) + if err != nil { + return err + } + + isNativeOnEthereum, err := validator.isNativeOnEthereum(ctx, ethToken) + if err != nil { + return err + } + + isNativeOnMultiversX, err := validator.isNativeOnMultiversX(ctx, mvxToken) + if err != nil { + return err + } + + if !isNativeOnEthereum && !isMintBurnOnEthereum { + return fmt.Errorf("%w isNativeOnEthereum = %v, isMintBurnOnEthereum = %v", ErrInvalidSetup, isNativeOnEthereum, isMintBurnOnEthereum) + } + + if !isNativeOnMultiversX && !isMintBurnOnMultiversX { + return fmt.Errorf("%w isNativeOnMultiversX = %v, isMintBurnOnMultiversX = %v", ErrInvalidSetup, isNativeOnMultiversX, isMintBurnOnMultiversX) + } + + if isNativeOnEthereum == isNativeOnMultiversX { + return fmt.Errorf("%w isNativeOnEthereum = %v, isNativeOnMultiversX = %v", ErrInvalidSetup, isNativeOnEthereum, isNativeOnMultiversX) + } + + ethAmount, err := validator.computeEthAmount(ctx, ethToken, isMintBurnOnEthereum, isNativeOnEthereum) + if err != nil { + return err + } + mvxAmount, err := validator.computeMvxAmount(ctx, mvxToken, isMintBurnOnMultiversX, isNativeOnMultiversX) + if err != nil { + return err + } + + validator.log.Debug("balanceValidator.CheckToken", + "ERC20 token", ethToken.String(), + "ERC20 balance", ethAmount.String(), + "ESDT token", mvxToken, + "ESDT balance", mvxAmount.String(), + "amount", amount.String(), + ) + + if ethAmount.Cmp(mvxAmount) != 0 { + return fmt.Errorf("%w, balance for ERC20 token %s is %s and the balance for ESDT token %s is %s, direction %s", + ErrBalanceMismatch, ethToken.String(), ethAmount.String(), mvxToken, mvxAmount.String(), direction) + } + return nil +} + +func (validator *balanceValidator) checkRequiredBalance(ctx context.Context, ethToken common.Address, mvxToken []byte, amount *big.Int, direction batchProcessor.Direction) error { + switch direction { + case batchProcessor.FromMultiversX: + return validator.ethereumClient.CheckRequiredBalance(ctx, ethToken, amount) + case batchProcessor.ToMultiversX: + return validator.multiversXClient.CheckRequiredBalance(ctx, mvxToken, amount) + default: + return fmt.Errorf("%w, direction: %s", ErrInvalidDirection, direction) + } +} + +func (validator *balanceValidator) isMintBurnOnEthereum(ctx context.Context, erc20Address common.Address) (bool, error) { + isMintBurn, err := validator.ethereumClient.MintBurnTokens(ctx, erc20Address) + if err != nil { + return false, err + } + + return isMintBurn, nil +} + +func (validator *balanceValidator) isNativeOnEthereum(ctx context.Context, erc20Address common.Address) (bool, error) { + isNative, err := validator.ethereumClient.NativeTokens(ctx, erc20Address) + if err != nil { + return false, err + } + return isNative, nil +} + +func (validator *balanceValidator) isMintBurnOnMultiversX(ctx context.Context, token []byte) (bool, error) { + isMintBurn, err := validator.multiversXClient.IsMintBurnToken(ctx, token) + if err != nil { + return false, err + } + return isMintBurn, nil +} + +func (validator *balanceValidator) isNativeOnMultiversX(ctx context.Context, token []byte) (bool, error) { + isNative, err := validator.multiversXClient.IsNativeToken(ctx, token) + if err != nil { + return false, err + } + return isNative, nil +} + +func (validator *balanceValidator) computeEthAmount( + ctx context.Context, + token common.Address, + isMintBurn bool, + isNative bool, +) (*big.Int, error) { + ethAmountInPendingBatches, err := validator.getTotalTransferAmountInPendingEthBatches(ctx, token) + if err != nil { + return nil, err + } + + if !isMintBurn { + // we need to subtract all locked balances on the Ethereum side (all pending, un-executed batches) so the balances + // with the minted MultiversX tokens will match + total, errTotal := validator.ethereumClient.TotalBalances(ctx, token) + if errTotal != nil { + return nil, errTotal + } + + return total.Sub(total, ethAmountInPendingBatches), nil + } + + burnBalances, err := validator.ethereumClient.BurnBalances(ctx, token) + if err != nil { + return nil, err + } + mintBalances, err := validator.ethereumClient.MintBalances(ctx, token) + if err != nil { + return nil, err + } + + // we need to cancel out what was burned in advance when the deposit was registered in the contract + burnBalances.Sub(burnBalances, ethAmountInPendingBatches) + + var ethAmount *big.Int + if isNative { + ethAmount = big.NewInt(0).Sub(burnBalances, mintBalances) + } else { + ethAmount = big.NewInt(0).Sub(mintBalances, burnBalances) + } + + if ethAmount.Cmp(big.NewInt(0)) < 0 { + return big.NewInt(0), fmt.Errorf("%w, ethAmount: %s", ErrNegativeAmount, ethAmount.String()) + } + return ethAmount, nil +} + +func (validator *balanceValidator) computeMvxAmount( + ctx context.Context, + token []byte, + isMintBurn bool, + isNative bool, +) (*big.Int, error) { + mvxAmountInPendingBatches, err := validator.getTotalTransferAmountInPendingMvxBatches(ctx, token) + if err != nil { + return nil, err + } + + if !isMintBurn { + // we need to subtract all locked balances on the MultiversX side (all pending, un-executed batches) so the balances + // with the minted Ethereum tokens will match + total, errTotal := validator.multiversXClient.TotalBalances(ctx, token) + if errTotal != nil { + return nil, errTotal + } + + return total.Sub(total, mvxAmountInPendingBatches), nil + } + + burnBalances, err := validator.multiversXClient.BurnBalances(ctx, token) + if err != nil { + return nil, err + } + mintBalances, err := validator.multiversXClient.MintBalances(ctx, token) + if err != nil { + return nil, err + } + var mvxAmount *big.Int + + // we need to cancel out what was burned in advance when the deposit was registered in the contract + burnBalances.Sub(burnBalances, mvxAmountInPendingBatches) + + if isNative { + mvxAmount = big.NewInt(0).Sub(burnBalances, mintBalances) + } else { + mvxAmount = big.NewInt(0).Sub(mintBalances, burnBalances) + } + + if mvxAmount.Cmp(big.NewInt(0)) < 0 { + return big.NewInt(0), fmt.Errorf("%w, mvxAmount: %s", ErrNegativeAmount, mvxAmount.String()) + } + return mvxAmount, nil +} + +func getTotalAmountFromBatch(batch *bridgeCore.TransferBatch, token []byte) *big.Int { + amount := big.NewInt(0) + for _, deposit := range batch.Deposits { + if bytes.Equal(deposit.SourceTokenBytes, token) { + amount.Add(amount, deposit.Amount) + } + } + + return amount +} + +func (validator *balanceValidator) getTotalTransferAmountInPendingMvxBatches(ctx context.Context, mvxToken []byte) (*big.Int, error) { + batchID, err := validator.multiversXClient.GetLastMvxBatchID(ctx) + if err != nil { + return nil, err + } + + var batch *bridgeCore.TransferBatch + amount := big.NewInt(0) + for { + batch, err = validator.multiversXClient.GetBatch(ctx, batchID) + if errors.Is(err, clients.ErrNoBatchAvailable) { + return amount, nil + } + if err != nil { + return nil, err + } + + wasExecuted, errWasExecuted := validator.ethereumClient.WasExecuted(ctx, batch.ID) + if errWasExecuted != nil { + return nil, errWasExecuted + } + if wasExecuted { + return amount, nil + } + + amountFromBatch := getTotalAmountFromBatch(batch, mvxToken) + amount.Add(amount, amountFromBatch) + batchID-- // go to the previous batch + } +} + +func (validator *balanceValidator) getTotalTransferAmountInPendingEthBatches(ctx context.Context, ethToken common.Address) (*big.Int, error) { + batchID, err := validator.multiversXClient.GetLastExecutedEthBatchID(ctx) + if err != nil { + return nil, err + } + + var batch *bridgeCore.TransferBatch + amount := big.NewInt(0) + for { + batch, _, err = validator.ethereumClient.GetBatch(ctx, batchID+1) // we take all batches, regardless if they are final or not + if err != nil { + return nil, err + } + + isBatchInvalid := batch.ID != batchID+1 || len(batch.Deposits) == 0 + if isBatchInvalid { + return amount, nil + } + + amountFromBatch := getTotalAmountFromBatch(batch, ethToken.Bytes()) + amount.Add(amount, amountFromBatch) + batchID++ + } +} + +// IsInterfaceNil returns true if there is no value under the interface +func (validator *balanceValidator) IsInterfaceNil() bool { + return validator == nil +} diff --git a/clients/balanceValidator/balanceValidator_test.go b/clients/balanceValidator/balanceValidator_test.go new file mode 100644 index 00000000..2f823872 --- /dev/null +++ b/clients/balanceValidator/balanceValidator_test.go @@ -0,0 +1,1601 @@ +package balanceValidator + +import ( + "context" + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/clients" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/assert" +) + +var ( + ethToken = common.BytesToAddress([]byte("eth token")) + mvxToken = []byte("mvx token") + amount = big.NewInt(100) + amount2 = big.NewInt(200) +) + +func createMockArgsBalanceValidator() ArgsBalanceValidator { + return ArgsBalanceValidator{ + Log: &testscommon.LoggerStub{}, + MultiversXClient: &bridge.MultiversXClientStub{}, + EthereumClient: &bridge.EthereumClientStub{}, + } +} + +type testConfiguration struct { + isNativeOnEth bool + isMintBurnOnEth bool + totalBalancesOnEth *big.Int + burnBalancesOnEth *big.Int + mintBalancesOnEth *big.Int + + isNativeOnMvx bool + isMintBurnOnMvx bool + totalBalancesOnMvx *big.Int + burnBalancesOnMvx *big.Int + mintBalancesOnMvx *big.Int + + errorsOnCalls map[string]error + + ethToken common.Address + mvxToken []byte + amount *big.Int + direction batchProcessor.Direction + + lastExecutedEthBatch uint64 + pendingMvxBatchId uint64 + amountsOnMvxPendingBatches map[uint64][]*big.Int + amountsOnEthPendingBatches map[uint64][]*big.Int +} + +func (cfg *testConfiguration) deepClone() testConfiguration { + result := testConfiguration{ + isNativeOnEth: cfg.isNativeOnEth, + isMintBurnOnEth: cfg.isMintBurnOnEth, + isNativeOnMvx: cfg.isNativeOnMvx, + isMintBurnOnMvx: cfg.isMintBurnOnMvx, + errorsOnCalls: make(map[string]error), + ethToken: common.HexToAddress(cfg.ethToken.Hex()), + mvxToken: make([]byte, len(cfg.mvxToken)), + direction: cfg.direction, + lastExecutedEthBatch: cfg.lastExecutedEthBatch, + pendingMvxBatchId: cfg.pendingMvxBatchId, + amountsOnMvxPendingBatches: make(map[uint64][]*big.Int), + amountsOnEthPendingBatches: make(map[uint64][]*big.Int), + } + if cfg.totalBalancesOnEth != nil { + result.totalBalancesOnEth = big.NewInt(0).Set(cfg.totalBalancesOnEth) + } + if cfg.burnBalancesOnEth != nil { + result.burnBalancesOnEth = big.NewInt(0).Set(cfg.burnBalancesOnEth) + } + if cfg.mintBalancesOnEth != nil { + result.mintBalancesOnEth = big.NewInt(0).Set(cfg.mintBalancesOnEth) + } + if cfg.totalBalancesOnMvx != nil { + result.totalBalancesOnMvx = big.NewInt(0).Set(cfg.totalBalancesOnMvx) + } + if cfg.burnBalancesOnMvx != nil { + result.burnBalancesOnMvx = big.NewInt(0).Set(cfg.burnBalancesOnMvx) + } + if cfg.mintBalancesOnMvx != nil { + result.mintBalancesOnMvx = big.NewInt(0).Set(cfg.mintBalancesOnMvx) + } + if cfg.amount != nil { + result.amount = big.NewInt(0).Set(cfg.amount) + } + + for key, err := range cfg.errorsOnCalls { + result.errorsOnCalls[key] = err + } + copy(result.mvxToken, cfg.mvxToken) + for nonce, values := range cfg.amountsOnMvxPendingBatches { + result.amountsOnMvxPendingBatches[nonce] = make([]*big.Int, 0, len(values)) + for _, value := range values { + result.amountsOnMvxPendingBatches[nonce] = append(result.amountsOnMvxPendingBatches[nonce], big.NewInt(0).Set(value)) + } + } + for nonce, values := range cfg.amountsOnEthPendingBatches { + result.amountsOnEthPendingBatches[nonce] = make([]*big.Int, 0, len(values)) + for _, value := range values { + result.amountsOnEthPendingBatches[nonce] = append(result.amountsOnEthPendingBatches[nonce], big.NewInt(0).Set(value)) + } + } + + return result +} + +type testResult struct { + checkRequiredBalanceOnEthCalled bool + checkRequiredBalanceOnMvxCalled bool + error error +} + +func TestNewBalanceValidator(t *testing.T) { + t.Parallel() + + t.Run("nil logger should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsBalanceValidator() + args.Log = nil + instance, err := NewBalanceValidator(args) + assert.Nil(t, instance) + assert.Equal(t, ErrNilLogger, err) + }) + t.Run("nil MultiversX client should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsBalanceValidator() + args.MultiversXClient = nil + instance, err := NewBalanceValidator(args) + assert.Nil(t, instance) + assert.Equal(t, ErrNilMultiversXClient, err) + }) + t.Run("nil Ethereum client should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsBalanceValidator() + args.EthereumClient = nil + instance, err := NewBalanceValidator(args) + assert.Nil(t, instance) + assert.Equal(t, ErrNilEthereumClient, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsBalanceValidator() + instance, err := NewBalanceValidator(args) + assert.NotNil(t, instance) + assert.Nil(t, err) + }) +} + +func TestBalanceValidator_IsInterfaceNil(t *testing.T) { + t.Parallel() + + var instance *balanceValidator + assert.True(t, instance.IsInterfaceNil()) + + instance = &balanceValidator{} + assert.False(t, instance.IsInterfaceNil()) +} + +func TestBridgeExecutor_CheckToken(t *testing.T) { + t.Parallel() + + expectedError := errors.New("expected error") + t.Run("unknown direction should error", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: "", + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrInvalidDirection) + }) + t.Run("query operations error", func(t *testing.T) { + t.Parallel() + + t.Run("on isMintBurnOnEthereum", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + errorsOnCalls: map[string]error{ + "MintBurnTokensEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on isMintBurnOnMultiversX", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + errorsOnCalls: map[string]error{ + "IsMintBurnTokenMvx": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on isNativeOnEthereum", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + errorsOnCalls: map[string]error{ + "NativeTokensEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on isNativeOnMultiversX", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + errorsOnCalls: map[string]error{ + "IsNativeTokenMvx": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeEthAmount, TotalBalances", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + errorsOnCalls: map[string]error{ + "TotalBalancesEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeEthAmount, BurnBalances", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isNativeOnMvx: true, + isMintBurnOnEth: true, + errorsOnCalls: map[string]error{ + "BurnBalancesEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeEthAmount, MintBalances", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isNativeOnMvx: true, + isMintBurnOnEth: true, + errorsOnCalls: map[string]error{ + "MintBalancesEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeEthAmount, GetLastExecutedEthBatchID", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isNativeOnMvx: true, + isMintBurnOnEth: true, + errorsOnCalls: map[string]error{ + "GetLastExecutedEthBatchIDMvx": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeEthAmount, GetBatch", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isNativeOnMvx: true, + isMintBurnOnEth: true, + errorsOnCalls: map[string]error{ + "GetBatchEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeMvxAmount, TotalBalances", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isNativeOnMvx: true, + isMintBurnOnEth: true, + errorsOnCalls: map[string]error{ + "TotalBalancesMvx": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeMvxAmount, BurnBalances", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + errorsOnCalls: map[string]error{ + "BurnBalancesMvx": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeMvxAmount, MintBalances", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + errorsOnCalls: map[string]error{ + "MintBalancesMvx": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeMvxAmount, GetLastMvxBatchID", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + errorsOnCalls: map[string]error{ + "GetLastMvxBatchID": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeMvxAmount, GetBatch", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + errorsOnCalls: map[string]error{ + "GetBatchMvx": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on computeMvxAmount, WasExecuted", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + errorsOnCalls: map[string]error{ + "WasExecutedEth": expectedError, + }, + } + result := validatorTester(cfg) + assert.Equal(t, expectedError, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + }) + t.Run("invalid setup", func(t *testing.T) { + t.Parallel() + + t.Run("on Ethereum is not native nor is mint/burn, should error", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrInvalidSetup) + assert.Contains(t, result.error.Error(), "isNativeOnEthereum = false, isMintBurnOnEthereum = false") + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on MultiversX is not native nor is mint/burn, should error", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isNativeOnEth: true, + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrInvalidSetup) + assert.Contains(t, result.error.Error(), "isNativeOnMultiversX = false, isMintBurnOnMultiversX = false") + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("native on both chains, should error", func(t *testing.T) { + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isNativeOnEth: true, + isNativeOnMvx: true, + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrInvalidSetup) + assert.Contains(t, result.error.Error(), "isNativeOnEthereum = true, isNativeOnMultiversX = true") + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + }) + t.Run("bad values on mint & burn", func(t *testing.T) { + t.Parallel() + + t.Run("on Ethereum, native", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnEth: true, + isMintBurnOnMvx: true, + burnBalancesOnEth: big.NewInt(37), + mintBalancesOnEth: big.NewInt(38), + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrNegativeAmount) + assert.Contains(t, result.error.Error(), "ethAmount: -1") + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on Ethereum, non-native", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + burnBalancesOnEth: big.NewInt(38), + mintBalancesOnEth: big.NewInt(37), + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrNegativeAmount) + assert.Contains(t, result.error.Error(), "ethAmount: -1") + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on MultiversX, native", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isMintBurnOnMvx: true, + isNativeOnMvx: true, + burnBalancesOnMvx: big.NewInt(37), + mintBalancesOnMvx: big.NewInt(38), + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrNegativeAmount) + assert.Contains(t, result.error.Error(), "mvxAmount: -1") + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + t.Run("on MultiversX, non-native", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isNativeOnEth: true, + isMintBurnOnMvx: true, + burnBalancesOnMvx: big.NewInt(38), + mintBalancesOnMvx: big.NewInt(37), + } + result := validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrNegativeAmount) + assert.Contains(t, result.error.Error(), "mvxAmount: -1") + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + }) + }) + t.Run("scenarios", func(t *testing.T) { + t.Parallel() + + t.Run("Eth -> MvX", func(t *testing.T) { + t.Parallel() + + t.Run("native on MvX, mint-burn on Eth, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + burnBalancesOnEth: big.NewInt(1100), // initial burn (1000) + burn from this transfer (100) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnMvx: big.NewInt(10000), + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on MvX, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + burnBalancesOnEth: big.NewInt(1220), // initial burn (1000) + burn from this transfer (100) + burn from next batches (120) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnMvx: big.NewInt(10000), + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on MvX but with mint-burn, mint-burn on Eth, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + isMintBurnOnMvx: true, + burnBalancesOnEth: big.NewInt(1100), // initial burn (1000) + burn from this transfer (100) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnMvx: big.NewInt(12000), + mintBalancesOnMvx: big.NewInt(2000), // burn - mint on Mvx === mint - burn on Eth + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on MvX but with mint-burn, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + isMintBurnOnMvx: true, + burnBalancesOnEth: big.NewInt(1220), // initial burn (1000) + burn from this transfer (100) + next batches (120) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnMvx: big.NewInt(12000), + mintBalancesOnMvx: big.NewInt(2000), // burn - mint on Mvx === mint - burn on Eth + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth, mint-burn on MvX, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnEth: big.NewInt(10100), // initial (10000) + locked from this transfer (100) + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnEth: big.NewInt(10220), // initial (10000) + locked from this transfer (100) + next batches (120) + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth but with mint-burn, mint-burn on MvX, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + isMintBurnOnEth: true, + burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnEth: big.NewInt(12100), + mintBalancesOnEth: big.NewInt(2000), // burn - mint - transfer on Eth === mint - burn on Mvx + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth but with mint-burn, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + isMintBurnOnEth: true, + burnBalancesOnMvx: big.NewInt(1000), // initial burn (1000) + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnEth: big.NewInt(12220), + mintBalancesOnEth: big.NewInt(2000), // burn - mint - transfer on Eth - next transfers === mint - burn on Mvx + amount: amount, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + }) + + t.Run("MvX -> Eth", func(t *testing.T) { + t.Parallel() + + t.Run("native on MvX, mint-burn on Eth, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnMvx: big.NewInt(10100), // initial (10000) + transfer from this batch (100) + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on MvX, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnMvx: big.NewInt(10220), // initial (10000) + transfer from this batch (100) + next batches (120) + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on MvX but with mint-burn, mint-burn on Eth, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + isMintBurnOnMvx: true, + burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnMvx: big.NewInt(12100), + mintBalancesOnMvx: big.NewInt(2000), // burn - mint - transfer on Mvx === mint - burn on Eth + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on MvX but with mint-burn, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + isMintBurnOnMvx: true, + burnBalancesOnEth: big.NewInt(1000), // initial burn (1000) + mintBalancesOnEth: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnMvx: big.NewInt(12220), + mintBalancesOnMvx: big.NewInt(2000), // burn - mint - transfer - next batches on Mvx === mint - burn on Eth + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnEth.Add(cfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth, mint-burn on MvX, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + burnBalancesOnMvx: big.NewInt(1100), // initial burn (1000) + transfer from this batch (100) + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnEth: big.NewInt(10000), // initial (10000) + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + burnBalancesOnMvx: big.NewInt(1220), // initial burn (1000) + transfer from this batch (100) + next batches (120) + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + totalBalancesOnEth: big.NewInt(10000), // initial (10000) + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth but with mint-burn, mint-burn on MvX, ok values, no next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + isMintBurnOnEth: true, + burnBalancesOnMvx: big.NewInt(1100), // initial burn (1000) + transfer from this batch (100) + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnEth: big.NewInt(12000), + mintBalancesOnEth: big.NewInt(2000), // burn - mint on Eth === mint - burn - transfer on Mvx + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("native on Eth but with mint-burn, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + isMintBurnOnEth: true, + burnBalancesOnMvx: big.NewInt(1220), // initial burn (1000) + transfer from this batch (100) + transfer from next batches + mintBalancesOnMvx: big.NewInt(11000), // minted (10000) + initial burn (1000) + burnBalancesOnEth: big.NewInt(12000), + mintBalancesOnEth: big.NewInt(2000), // burn - mint on Eth === mint - burn - transfer - next batches on Mvx + amount: amount, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + cfg.burnBalancesOnMvx.Add(cfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(cfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + }) + + t.Run("MvX <-> Eth", func(t *testing.T) { + t.Parallel() + + t.Run("from Eth: native on MvX, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + existingNativeBalanceMvx := int64(10000) + existingBurnEth := int64(150000) + existingMintEth := int64(160000) + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + burnBalancesOnEth: big.NewInt(existingBurnEth + 100 + 30 + 40 + 50), + mintBalancesOnEth: big.NewInt(existingMintEth), + totalBalancesOnMvx: big.NewInt(existingNativeBalanceMvx + 60 + 80 + 100 + 200), + amount: amount, + pendingMvxBatchId: 1, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnEth.Add(copiedCfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("from Eth: native on MvX but with mint-burn, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + existingBurnMvx := int64(370000) // burn > mint because the token is native + existingMintMvx := int64(360000) + existingBurnEth := int64(150000) + existingMintEth := int64(160000) + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + isMintBurnOnMvx: true, + burnBalancesOnEth: big.NewInt(existingBurnEth + 100 + 30 + 40 + 50), + mintBalancesOnEth: big.NewInt(existingMintEth), + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 60 + 80 + 100 + 200), + mintBalancesOnMvx: big.NewInt(existingMintMvx), + amount: amount, + pendingMvxBatchId: 1, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnEth.Add(copiedCfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("from Eth: native on Eth, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + existingBurnMvx := int64(360000) + existingMintMvx := int64(370000) + existingNativeBalanceEth := int64(10000) + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 200 + 60 + 80 + 100), + mintBalancesOnMvx: big.NewInt(existingMintMvx), + totalBalancesOnEth: big.NewInt(existingNativeBalanceEth + 100 + 30 + 40 + 50), + amount: amount, + pendingMvxBatchId: 1, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnMvx.Add(copiedCfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("from Eth: native on Eth but with mint-burn, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + existingBurnMvx := int64(360000) + existingMintMvx := int64(370000) + existingBurnEth := int64(160000) // burn > mint because the token is native + existingMintEth := int64(150000) + + cfg := testConfiguration{ + direction: batchProcessor.ToMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + isMintBurnOnEth: true, + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 200 + 60 + 80 + 100), + mintBalancesOnMvx: big.NewInt(existingMintMvx), + burnBalancesOnEth: big.NewInt(existingBurnEth + 100 + 30 + 40 + 50), + mintBalancesOnEth: big.NewInt(existingMintEth), + amount: amount, + pendingMvxBatchId: 1, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.False(t, result.checkRequiredBalanceOnEthCalled) + assert.True(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnMvx.Add(copiedCfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("from MvX: native on MvX, mint-burn on Eth, ok values, with next pending batches on both chains", func(t *testing.T) { + t.Parallel() + + existingNativeBalanceMvx := int64(10000) + existingBurnEth := int64(150000) + existingMintEth := int64(160000) + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + burnBalancesOnEth: big.NewInt(existingBurnEth + 200 + 60 + 80 + 100), + mintBalancesOnEth: big.NewInt(existingMintEth), + totalBalancesOnMvx: big.NewInt(existingNativeBalanceMvx + 30 + 40 + 50 + 100), + amount: amount, + pendingMvxBatchId: 1, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnEth.Add(copiedCfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("from MvX: native on MvX but with mint-burn, mint-burn on Eth, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + existingBurnMvx := int64(370000) // burn > mint because the token is native + existingMintMvx := int64(360000) + existingBurnEth := int64(150000) + existingMintEth := int64(160000) + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnEth: true, + isNativeOnMvx: true, + isMintBurnOnMvx: true, + burnBalancesOnEth: big.NewInt(existingBurnEth + 200 + 60 + 80 + 100), + mintBalancesOnEth: big.NewInt(existingMintEth), + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 30 + 40 + 50 + 100), + mintBalancesOnMvx: big.NewInt(existingMintMvx), + amount: amount, + pendingMvxBatchId: 1, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnEth.Add(copiedCfg.burnBalancesOnEth, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("from MvX: native on Eth, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + existingBurnMvx := int64(360000) + existingMintMvx := int64(370000) + existingNativeBalanceEth := int64(10000) + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 100 + 30 + 40 + 50), + mintBalancesOnMvx: big.NewInt(existingMintMvx), + totalBalancesOnEth: big.NewInt(existingNativeBalanceEth + 200 + 60 + 80 + 100), + amount: amount, + pendingMvxBatchId: 1, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnMvx.Add(copiedCfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + t.Run("from MvX: native on Eth but with mint-burn, mint-burn on MvX, ok values, with next pending batches", func(t *testing.T) { + t.Parallel() + + existingBurnMvx := int64(360000) + existingMintMvx := int64(370000) + existingBurnEth := int64(160000) // burn > mint because the token is native + existingMintEth := int64(150000) + + cfg := testConfiguration{ + direction: batchProcessor.FromMultiversX, + isMintBurnOnMvx: true, + isNativeOnEth: true, + isMintBurnOnEth: true, + burnBalancesOnMvx: big.NewInt(existingBurnMvx + 100 + 30 + 40 + 50), + mintBalancesOnMvx: big.NewInt(existingMintMvx), + burnBalancesOnEth: big.NewInt(existingBurnEth + 200 + 60 + 80 + 100), + mintBalancesOnEth: big.NewInt(existingMintEth), + amount: amount, + pendingMvxBatchId: 1, + amountsOnMvxPendingBatches: map[uint64][]*big.Int{ + 1: {amount}, + 2: {big.NewInt(30), big.NewInt(40)}, + 3: {big.NewInt(50)}, + }, + amountsOnEthPendingBatches: map[uint64][]*big.Int{ + 1: {amount2}, + 2: {big.NewInt(60), big.NewInt(80)}, + 3: {big.NewInt(100)}, + }, + mvxToken: mvxToken, + ethToken: ethToken, + } + + result := validatorTester(cfg) + assert.Nil(t, result.error) + assert.True(t, result.checkRequiredBalanceOnEthCalled) + assert.False(t, result.checkRequiredBalanceOnMvxCalled) + + t.Run("mismatch should error", func(t *testing.T) { + copiedCfg := cfg.deepClone() + copiedCfg.burnBalancesOnMvx.Add(copiedCfg.burnBalancesOnMvx, big.NewInt(1)) + result = validatorTester(copiedCfg) + assert.ErrorIs(t, result.error, ErrBalanceMismatch) + }) + }) + }) + }) +} + +func validatorTester(cfg testConfiguration) testResult { + args := createMockArgsBalanceValidator() + + result := testResult{} + + lastMvxBatchID := uint64(0) + for key := range cfg.amountsOnMvxPendingBatches { + if key > lastMvxBatchID { + lastMvxBatchID = key + } + } + + args.MultiversXClient = &bridge.MultiversXClientStub{ + CheckRequiredBalanceCalled: func(ctx context.Context, token []byte, value *big.Int) error { + result.checkRequiredBalanceOnMvxCalled = true + return nil + }, + IsMintBurnTokenCalled: func(ctx context.Context, token []byte) (bool, error) { + err := cfg.errorsOnCalls["IsMintBurnTokenMvx"] + if err != nil { + return false, err + } + + return cfg.isMintBurnOnMvx, nil + }, + IsNativeTokenCalled: func(ctx context.Context, token []byte) (bool, error) { + err := cfg.errorsOnCalls["IsNativeTokenMvx"] + if err != nil { + return false, err + } + + return cfg.isNativeOnMvx, nil + }, + TotalBalancesCalled: func(ctx context.Context, token []byte) (*big.Int, error) { + err := cfg.errorsOnCalls["TotalBalancesMvx"] + if err != nil { + return nil, err + } + + return returnBigIntOrZeroIfNil(cfg.totalBalancesOnMvx), nil + }, + MintBalancesCalled: func(ctx context.Context, token []byte) (*big.Int, error) { + err := cfg.errorsOnCalls["MintBalancesMvx"] + if err != nil { + return nil, err + } + + return returnBigIntOrZeroIfNil(cfg.mintBalancesOnMvx), nil + }, + BurnBalancesCalled: func(ctx context.Context, token []byte) (*big.Int, error) { + err := cfg.errorsOnCalls["BurnBalancesMvx"] + if err != nil { + return nil, err + } + + return returnBigIntOrZeroIfNil(cfg.burnBalancesOnMvx), nil + }, + GetPendingBatchCalled: func(ctx context.Context) (*bridgeCore.TransferBatch, error) { + err := cfg.errorsOnCalls["GetPendingBatchMvx"] + if err != nil { + return nil, err + } + + batch := &bridgeCore.TransferBatch{ + ID: cfg.pendingMvxBatchId, + } + applyDummyFromMvxDepositsToBatch(cfg, batch) + + return batch, nil + }, + GetBatchCalled: func(ctx context.Context, batchID uint64) (*bridgeCore.TransferBatch, error) { + err := cfg.errorsOnCalls["GetBatchMvx"] + if err != nil { + return nil, err + } + + if batchID > getMaxMvxPendingBatchID(cfg) { + return nil, clients.ErrNoBatchAvailable + } + batch := &bridgeCore.TransferBatch{ + ID: batchID, + } + applyDummyFromMvxDepositsToBatch(cfg, batch) + + return batch, nil + }, + GetLastExecutedEthBatchIDCalled: func(ctx context.Context) (uint64, error) { + err := cfg.errorsOnCalls["GetLastExecutedEthBatchIDMvx"] + if err != nil { + return 0, err + } + + return cfg.lastExecutedEthBatch, nil + }, + GetLastMvxBatchIDCalled: func(ctx context.Context) (uint64, error) { + err := cfg.errorsOnCalls["GetLastMvxBatchID"] + if err != nil { + return 0, err + } + + return lastMvxBatchID, nil + }, + } + args.EthereumClient = &bridge.EthereumClientStub{ + CheckRequiredBalanceCalled: func(ctx context.Context, erc20Address common.Address, value *big.Int) error { + result.checkRequiredBalanceOnEthCalled = true + return nil + }, + MintBurnTokensCalled: func(ctx context.Context, account common.Address) (bool, error) { + err := cfg.errorsOnCalls["MintBurnTokensEth"] + if err != nil { + return false, err + } + + return cfg.isMintBurnOnEth, nil + }, + NativeTokensCalled: func(ctx context.Context, account common.Address) (bool, error) { + err := cfg.errorsOnCalls["NativeTokensEth"] + if err != nil { + return false, err + } + + return cfg.isNativeOnEth, nil + }, + TotalBalancesCalled: func(ctx context.Context, account common.Address) (*big.Int, error) { + err := cfg.errorsOnCalls["TotalBalancesEth"] + if err != nil { + return nil, err + } + + return returnBigIntOrZeroIfNil(cfg.totalBalancesOnEth), nil + }, + MintBalancesCalled: func(ctx context.Context, account common.Address) (*big.Int, error) { + err := cfg.errorsOnCalls["MintBalancesEth"] + if err != nil { + return nil, err + } + + return returnBigIntOrZeroIfNil(cfg.mintBalancesOnEth), nil + }, + BurnBalancesCalled: func(ctx context.Context, account common.Address) (*big.Int, error) { + err := cfg.errorsOnCalls["BurnBalancesEth"] + if err != nil { + return nil, err + } + + return returnBigIntOrZeroIfNil(cfg.burnBalancesOnEth), nil + }, + GetBatchCalled: func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { + err := cfg.errorsOnCalls["GetBatchEth"] + if err != nil { + return nil, false, err + } + + batch := &bridgeCore.TransferBatch{ + ID: nonce, + } + applyDummyFromEthDepositsToBatch(cfg, batch) + + return batch, false, nil + }, + WasExecutedCalled: func(ctx context.Context, batchID uint64) (bool, error) { + err := cfg.errorsOnCalls["WasExecutedEth"] + if err != nil { + return false, err + } + + _, found := cfg.amountsOnMvxPendingBatches[batchID] + return !found, nil + }, + } + + validator, err := NewBalanceValidator(args) + if err != nil { + result.error = err + return result + } + + result.error = validator.CheckToken(context.Background(), cfg.ethToken, cfg.mvxToken, cfg.amount, cfg.direction) + + return result +} + +func applyDummyFromMvxDepositsToBatch(cfg testConfiguration, batch *bridgeCore.TransferBatch) { + if cfg.amountsOnMvxPendingBatches != nil { + values, found := cfg.amountsOnMvxPendingBatches[batch.ID] + if found { + depositCounter := uint64(0) + + for _, deposit := range values { + batch.Deposits = append(batch.Deposits, &bridgeCore.DepositTransfer{ + Nonce: depositCounter, + Amount: big.NewInt(0).Set(deposit), + SourceTokenBytes: mvxToken, + }) + } + } + } +} + +func applyDummyFromEthDepositsToBatch(cfg testConfiguration, batch *bridgeCore.TransferBatch) { + if cfg.amountsOnEthPendingBatches != nil { + values, found := cfg.amountsOnEthPendingBatches[batch.ID] + if found { + depositCounter := uint64(0) + + for _, deposit := range values { + batch.Deposits = append(batch.Deposits, &bridgeCore.DepositTransfer{ + Nonce: depositCounter, + Amount: big.NewInt(0).Set(deposit), + SourceTokenBytes: ethToken.Bytes(), + }) + } + } + } +} + +func getMaxMvxPendingBatchID(cfg testConfiguration) uint64 { + if cfg.amountsOnMvxPendingBatches == nil { + return 0 + } + + maxBatchIDFound := uint64(0) + for batchID := range cfg.amountsOnMvxPendingBatches { + if batchID > maxBatchIDFound { + maxBatchIDFound = batchID + } + } + + return maxBatchIDFound +} + +func returnBigIntOrZeroIfNil(value *big.Int) *big.Int { + if value == nil { + return big.NewInt(0) + } + + return big.NewInt(0).Set(value) +} diff --git a/clients/balanceValidator/errors.go b/clients/balanceValidator/errors.go new file mode 100644 index 00000000..9288ca5b --- /dev/null +++ b/clients/balanceValidator/errors.go @@ -0,0 +1,24 @@ +package balanceValidator + +import "errors" + +// ErrNilLogger signals that a nil logger has been provided +var ErrNilLogger = errors.New("nil logger") + +// ErrNilMultiversXClient signals that a nil MultiversX client has been provided +var ErrNilMultiversXClient = errors.New("nil MultiversX client") + +// ErrNilEthereumClient signals that a nil Ethereum client has been provided +var ErrNilEthereumClient = errors.New("nil Ethereum client") + +// ErrInvalidDirection signals that an invalid direction was provided +var ErrInvalidDirection = errors.New("invalid direction") + +// ErrInvalidSetup signals that an invalid setup was provided +var ErrInvalidSetup = errors.New("invalid setup") + +// ErrNegativeAmount signals that a negative amount was provided +var ErrNegativeAmount = errors.New("negative amount") + +// ErrBalanceMismatch signals that the balances are not expected +var ErrBalanceMismatch = errors.New("balance mismatch") diff --git a/clients/balanceValidator/interface.go b/clients/balanceValidator/interface.go new file mode 100644 index 00000000..7e8c0388 --- /dev/null +++ b/clients/balanceValidator/interface.go @@ -0,0 +1,37 @@ +package balanceValidator + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" +) + +// MultiversXClient defines the behavior of the MultiversX client able to communicate with the MultiversX chain +type MultiversXClient interface { + GetPendingBatch(ctx context.Context) (*bridgeCore.TransferBatch, error) + GetBatch(ctx context.Context, batchID uint64) (*bridgeCore.TransferBatch, error) + GetLastExecutedEthBatchID(ctx context.Context) (uint64, error) + GetLastMvxBatchID(ctx context.Context) (uint64, error) + IsMintBurnToken(ctx context.Context, token []byte) (bool, error) + IsNativeToken(ctx context.Context, token []byte) (bool, error) + TotalBalances(ctx context.Context, token []byte) (*big.Int, error) + MintBalances(ctx context.Context, token []byte) (*big.Int, error) + BurnBalances(ctx context.Context, token []byte) (*big.Int, error) + CheckRequiredBalance(ctx context.Context, token []byte, value *big.Int) error + IsInterfaceNil() bool +} + +// EthereumClient defines the behavior of the Ethereum client able to communicate with the Ethereum chain +type EthereumClient interface { + GetBatch(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) + TotalBalances(ctx context.Context, token common.Address) (*big.Int, error) + MintBalances(ctx context.Context, token common.Address) (*big.Int, error) + BurnBalances(ctx context.Context, token common.Address) (*big.Int, error) + MintBurnTokens(ctx context.Context, token common.Address) (bool, error) + NativeTokens(ctx context.Context, token common.Address) (bool, error) + CheckRequiredBalance(ctx context.Context, erc20Address common.Address, value *big.Int) error + WasExecuted(ctx context.Context, mvxBatchID uint64) (bool, error) + IsInterfaceNil() bool +} diff --git a/clients/batchValidator/batchValidator.go b/clients/batchValidator/batchValidator.go deleted file mode 100644 index 38a0f931..00000000 --- a/clients/batchValidator/batchValidator.go +++ /dev/null @@ -1,145 +0,0 @@ -package batchValidatorManagement - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net/http" - "time" - - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/clients/chain" - logger "github.com/multiversx/mx-chain-logger-go" -) - -const minRequestTime = time.Millisecond -const logPath = "BatchValidator" - -// ArgsBatchValidator is the DTO used for the creating a new batch validator instance -type ArgsBatchValidator struct { - SourceChain chain.Chain - DestinationChain chain.Chain - RequestURL string - RequestTime time.Duration -} - -type batchValidator struct { - requestURL string - requestTime time.Duration - log logger.Logger - httpClient HTTPClient -} - -// NewBatchValidator returns a new batch validator instance -func NewBatchValidator(args ArgsBatchValidator) (*batchValidator, error) { - err := checkArgs(args) - if err != nil { - return nil, err - } - - bv := &batchValidator{ - requestURL: fmt.Sprintf("%s/%s/%s", args.RequestURL, args.SourceChain.ToLower(), args.DestinationChain.ToLower()), - requestTime: args.RequestTime, - httpClient: http.DefaultClient, - } - bv.log = logger.GetOrCreate(logPath) - return bv, nil -} - -func checkArgs(args ArgsBatchValidator) error { - switch args.SourceChain { - case chain.Ethereum, chain.Bsc, chain.Polygon, chain.MultiversX: - default: - return fmt.Errorf("%w: %q", clients.ErrInvalidValue, args.SourceChain) - } - switch args.DestinationChain { - case chain.Ethereum, chain.Bsc, chain.Polygon, chain.MultiversX: - default: - return fmt.Errorf("%w: %q", clients.ErrInvalidValue, args.DestinationChain) - } - if args.RequestTime < minRequestTime { - return fmt.Errorf("%w in checkArgs for value RequestTime", clients.ErrInvalidValue) - } - - return nil -} - -// ValidateBatch checks whether the given batch is the same also on miscroservice side -func (bv *batchValidator) ValidateBatch(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - body, err := json.Marshal(batch) - if err != nil { - return false, fmt.Errorf("%w during request marshal", err) - } - - responseAsBytes, err := bv.doRequest(ctx, body) - if err != nil { - return false, fmt.Errorf("%w while executing request", err) - } - if len(responseAsBytes) == 0 { - return false, errors.New("empty response") - } - - response := µserviceResponse{} - err = json.Unmarshal(responseAsBytes, response) - if err != nil { - return false, fmt.Errorf("%w during response unmarshal", err) - } - - return response.Valid, nil -} - -func (bv *batchValidator) doRequest(ctx context.Context, batch []byte) ([]byte, error) { - requestContext, cancel := context.WithTimeout(ctx, bv.requestTime) - defer cancel() - - responseAsBytes, err := bv.doRequestReturningBytes(batch, requestContext) - if err != nil { - return nil, err - } - - return responseAsBytes, nil -} - -func (bv *batchValidator) doRequestReturningBytes(batch []byte, ctx context.Context) ([]byte, error) { - request, err := http.NewRequestWithContext(ctx, http.MethodPost, bv.requestURL, bytes.NewBuffer(batch)) - request.Header.Set("Content-Type", "application/json") - if err != nil { - return nil, err - } - - response, err := bv.httpClient.Do(request) - if err != nil { - return nil, err - } - if response.StatusCode == http.StatusBadRequest && response.Body != http.NoBody { - data, _ := ioutil.ReadAll(response.Body) - badResponse := µserviceBadRequestBody{} - err = json.Unmarshal(data, badResponse) - if err != nil { - return nil, fmt.Errorf("%w during bad response unmarshal", err) - } - return nil, fmt.Errorf("got status %s: %s", response.Status, badResponse.Message) - } - if response.StatusCode != http.StatusOK { - return nil, fmt.Errorf("got status %s", response.Status) - } - - defer func() { - _ = response.Body.Close() - }() - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - - return body, nil -} - -// IsInterfaceNil returns true if there is no value under the interface -func (bv *batchValidator) IsInterfaceNil() bool { - return bv == nil -} diff --git a/clients/batchValidator/batchValidator_test.go b/clients/batchValidator/batchValidator_test.go deleted file mode 100644 index 7a785cf9..00000000 --- a/clients/batchValidator/batchValidator_test.go +++ /dev/null @@ -1,287 +0,0 @@ -package batchValidatorManagement - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "math/big" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" - - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/clients/chain" - "github.com/multiversx/mx-bridge-eth-go/testsCommon" - "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func createMockArgsBatchValidator() ArgsBatchValidator { - return ArgsBatchValidator{ - SourceChain: chain.Ethereum, - DestinationChain: chain.MultiversX, - RequestURL: "", - RequestTime: time.Second, - } -} - -func TestNewBatchValidator(t *testing.T) { - t.Parallel() - - t.Run("invalid SourceChain", func(t *testing.T) { - args := createMockArgsBatchValidator() - args.SourceChain = "" - - bv, err := NewBatchValidator(args) - assert.True(t, check.IfNil(bv)) - assert.True(t, errors.Is(err, clients.ErrInvalidValue)) - }) - t.Run("invalid DestinationChain", func(t *testing.T) { - args := createMockArgsBatchValidator() - args.DestinationChain = "" - - bv, err := NewBatchValidator(args) - assert.True(t, check.IfNil(bv)) - assert.True(t, errors.Is(err, clients.ErrInvalidValue)) - }) - t.Run("invalid request time", func(t *testing.T) { - args := createMockArgsBatchValidator() - args.RequestTime = time.Duration(minRequestTime.Nanoseconds() - 1) - - bv, err := NewBatchValidator(args) - assert.True(t, check.IfNil(bv)) - assert.True(t, errors.Is(err, clients.ErrInvalidValue)) - assert.True(t, strings.Contains(err.Error(), "checkArgs for value RequestTime")) - }) - t.Run("should work", func(t *testing.T) { - args := createMockArgsBatchValidator() - - bv, err := NewBatchValidator(args) - assert.False(t, check.IfNil(bv)) - assert.Nil(t, err) - }) -} - -func TestBatchValidator_ValidateBatch(t *testing.T) { - t.Parallel() - - largeValue, ok := big.NewInt(0).SetString("1000000000000000000000", 10) // 1000 units (10^18 denominated) - require.True(t, ok) - batch := &clients.TransferBatch{ - ID: 1, - Deposits: []*clients.DepositTransfer{ - { - Nonce: 1, - DisplayableTo: "to1", - DisplayableFrom: "from1", - DisplayableToken: "token1", - Amount: big.NewInt(0).Add(largeValue, big.NewInt(1)), - }, - { - Nonce: 2, - DisplayableTo: "to2", - DisplayableFrom: "from2", - DisplayableToken: "token2", - Amount: big.NewInt(0).Add(largeValue, big.NewInt(2)), - }, - }, - Statuses: []byte{0x3, 0x4}, - } - expectedJsonString := `{"batchId":1,"deposits":[{"nonce":1,"to":"to1","from":"from1","token":"token1","amount":1000000000000000000001},{"nonce":2,"to":"to2","from":"from2","token":"token2","amount":1000000000000000000002}],"statuses":"AwQ="}` - - t.Run("server errors with Bad Request, but no reason", func(t *testing.T) { - t.Parallel() - - args := createMockArgsBatchValidator() - responseHandler := &testsCommon.HTTPHandlerStub{ - ServeHTTPCalled: func(writer http.ResponseWriter, request *http.Request) { - expectedURL := fmt.Sprintf("/%s/%s", args.SourceChain.ToLower(), args.DestinationChain.ToLower()) - require.Equal(t, expectedURL, request.URL.String()) - - writer.WriteHeader(http.StatusBadRequest) - }, - } - - server := httptest.NewServer(responseHandler) - defer server.Close() - - args.RequestURL = server.URL - bv, _ := NewBatchValidator(args) - - isValid, err := bv.ValidateBatch(context.Background(), batch) - assert.False(t, isValid) - assert.NotNil(t, err) - assert.Equal(t, "got status 400 Bad Request while executing request", err.Error()) - }) - t.Run("server errors with Bad Request and reason", func(t *testing.T) { - t.Parallel() - - args := createMockArgsBatchValidator() - bodyJson := microserviceBadRequestBody{ - StatusCode: 400, - Message: "different number of swaps. given: 2, stored: 3", - Error: "Bad Request", - } - responseHandler := &testsCommon.HTTPHandlerStub{ - ServeHTTPCalled: func(writer http.ResponseWriter, request *http.Request) { - expectedURL := fmt.Sprintf("/%s/%s", args.SourceChain.ToLower(), args.DestinationChain.ToLower()) - require.Equal(t, expectedURL, request.URL.String()) - - writer.WriteHeader(http.StatusBadRequest) - body, _ := json.Marshal(bodyJson) - _, _ = writer.Write(body) - }, - } - - server := httptest.NewServer(responseHandler) - defer server.Close() - - args.RequestURL = server.URL - bv, _ := NewBatchValidator(args) - - isValid, err := bv.ValidateBatch(context.Background(), batch) - assert.False(t, isValid) - assert.NotNil(t, err) - assert.Equal(t, fmt.Sprintf("got status 400 Bad Request: %s while executing request", bodyJson.Message), err.Error()) - }) - t.Run("server errors with other than Bad Request", func(t *testing.T) { - t.Parallel() - - args := createMockArgsBatchValidator() - responseHandler := &testsCommon.HTTPHandlerStub{ - ServeHTTPCalled: func(writer http.ResponseWriter, request *http.Request) { - expectedURL := fmt.Sprintf("/%s/%s", args.SourceChain.ToLower(), args.DestinationChain.ToLower()) - require.Equal(t, expectedURL, request.URL.String()) - - writer.WriteHeader(http.StatusForbidden) - }, - } - - server := httptest.NewServer(responseHandler) - defer server.Close() - - args.RequestURL = server.URL - bv, _ := NewBatchValidator(args) - - isValid, err := bv.ValidateBatch(context.Background(), batch) - assert.False(t, isValid) - assert.NotNil(t, err) - assert.Equal(t, "got status 403 Forbidden while executing request", err.Error()) - }) - t.Run("empty response", func(t *testing.T) { - t.Parallel() - - args := createMockArgsBatchValidator() - responseHandler := &testsCommon.HTTPHandlerStub{ - ServeHTTPCalled: func(writer http.ResponseWriter, request *http.Request) { - expectedURL := fmt.Sprintf("/%s/%s", args.SourceChain.ToLower(), args.DestinationChain.ToLower()) - require.Equal(t, expectedURL, request.URL.String()) - - writer.WriteHeader(http.StatusOK) - _, _ = writer.Write(nil) - }, - } - - server := httptest.NewServer(responseHandler) - defer server.Close() - - args.RequestURL = server.URL - bv, _ := NewBatchValidator(args) - - isValid, err := bv.ValidateBatch(context.Background(), batch) - assert.False(t, isValid) - assert.NotNil(t, err) - assert.Equal(t, "empty response", err.Error()) - }) - t.Run("improper response", func(t *testing.T) { - t.Parallel() - - args := createMockArgsBatchValidator() - responseHandler := &testsCommon.HTTPHandlerStub{ - ServeHTTPCalled: func(writer http.ResponseWriter, request *http.Request) { - expectedURL := fmt.Sprintf("/%s/%s", args.SourceChain.ToLower(), args.DestinationChain.ToLower()) - require.Equal(t, expectedURL, request.URL.String()) - - writer.WriteHeader(http.StatusOK) - _, _ = writer.Write([]byte("garbage")) - }, - } - - server := httptest.NewServer(responseHandler) - defer server.Close() - - args.RequestURL = server.URL - bv, _ := NewBatchValidator(args) - - isValid, err := bv.ValidateBatch(context.Background(), batch) - assert.False(t, isValid) - assert.NotNil(t, err) - assert.True(t, strings.Contains(err.Error(), "value during response unmarshal")) - }) - t.Run("context deadline exceeded", func(t *testing.T) { - t.Parallel() - - args := createMockArgsBatchValidator() - responseHandler := &testsCommon.HTTPHandlerStub{ - ServeHTTPCalled: func(writer http.ResponseWriter, request *http.Request) { - expectedURL := fmt.Sprintf("/%s/%s", args.SourceChain.ToLower(), args.DestinationChain.ToLower()) - require.Equal(t, expectedURL, request.URL.String()) - }, - } - - server := httptest.NewServer(responseHandler) - defer server.Close() - - args.RequestURL = server.URL - bv, _ := NewBatchValidator(args) - - ctx, cancel := context.WithCancel(context.Background()) - cancel() - - isValid, err := bv.ValidateBatch(ctx, batch) - assert.False(t, isValid) - assert.NotNil(t, err) - assert.True(t, strings.Contains(err.Error(), "context canceled while executing request")) - }) - t.Run("should work", func(t *testing.T) { - t.Parallel() - - args := createMockArgsBatchValidator() - responseHandler := &testsCommon.HTTPHandlerStub{ - ServeHTTPCalled: func(writer http.ResponseWriter, request *http.Request) { - expectedURL := fmt.Sprintf("/%s/%s", args.SourceChain.ToLower(), args.DestinationChain.ToLower()) - require.Equal(t, expectedURL, request.URL.String()) - - defer func() { - _ = request.Body.Close() - }() - - body, err := ioutil.ReadAll(request.Body) - require.Nil(t, err) - require.Equal(t, expectedJsonString, string(body)) - - resp := µserviceResponse{ - Valid: true, - } - writer.WriteHeader(http.StatusOK) - respBytes, _ := json.Marshal(resp) - _, _ = writer.Write(respBytes) - }, - } - - server := httptest.NewServer(responseHandler) - defer server.Close() - - args.RequestURL = server.URL - bv, _ := NewBatchValidator(args) - - isValid, err := bv.ValidateBatch(context.Background(), batch) - assert.True(t, isValid) - assert.Nil(t, err) - }) -} diff --git a/clients/batchValidator/disabled/disabledBatchValidator.go b/clients/batchValidator/disabled/disabledBatchValidator.go deleted file mode 100644 index ce67a6c8..00000000 --- a/clients/batchValidator/disabled/disabledBatchValidator.go +++ /dev/null @@ -1,24 +0,0 @@ -package disabled - -import ( - "context" - - "github.com/multiversx/mx-bridge-eth-go/clients" -) - -type disabledBatchValidator struct{} - -// NewDisabledBatchValidator will return a disabled batch validator instance -func NewDisabledBatchValidator() *disabledBatchValidator { - return &disabledBatchValidator{} -} - -// ValidateBatch returns true,nil and will result in skipping batch validation -func (dbv *disabledBatchValidator) ValidateBatch(_ context.Context, _ *clients.TransferBatch) (bool, error) { - return true, nil -} - -// IsInterfaceNil returns true if there is no value under the interface -func (dbv *disabledBatchValidator) IsInterfaceNil() bool { - return dbv == nil -} diff --git a/clients/batchValidator/disabled/disabledBatchValidator_test.go b/clients/batchValidator/disabled/disabledBatchValidator_test.go deleted file mode 100644 index ed571a36..00000000 --- a/clients/batchValidator/disabled/disabledBatchValidator_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package disabled - -import ( - "context" - "testing" - - "github.com/multiversx/mx-chain-core-go/core/check" - "github.com/stretchr/testify/assert" -) - -func TestNewDisabledBatchValidator(t *testing.T) { - dbv := NewDisabledBatchValidator() - - assert.False(t, check.IfNil(dbv)) - - isValid, err := dbv.ValidateBatch(context.Background(), nil) - assert.True(t, isValid) - assert.Nil(t, err) -} diff --git a/clients/batchValidator/factory/batchValidatorManagementFactory.go b/clients/batchValidator/factory/batchValidatorManagementFactory.go deleted file mode 100644 index 0281ad4b..00000000 --- a/clients/batchValidator/factory/batchValidatorManagementFactory.go +++ /dev/null @@ -1,15 +0,0 @@ -package factory - -import ( - "github.com/multiversx/mx-bridge-eth-go/clients" - batchValidatorManagement "github.com/multiversx/mx-bridge-eth-go/clients/batchValidator" - "github.com/multiversx/mx-bridge-eth-go/clients/batchValidator/disabled" -) - -// CreateBatchValidator generates an implementation of BatchValidator -func CreateBatchValidator(args batchValidatorManagement.ArgsBatchValidator, enabled bool) (clients.BatchValidator, error) { - if enabled { - return batchValidatorManagement.NewBatchValidator(args) - } - return disabled.NewDisabledBatchValidator(), nil -} diff --git a/clients/batchValidator/interface.go b/clients/batchValidator/interface.go deleted file mode 100644 index c78fb3fe..00000000 --- a/clients/batchValidator/interface.go +++ /dev/null @@ -1,8 +0,0 @@ -package batchValidatorManagement - -import "net/http" - -// HTTPClient is the interface we expect to call in order to do the HTTP requests -type HTTPClient interface { - Do(req *http.Request) (*http.Response, error) -} diff --git a/clients/batchValidator/types.go b/clients/batchValidator/types.go deleted file mode 100644 index 98a04ac4..00000000 --- a/clients/batchValidator/types.go +++ /dev/null @@ -1,36 +0,0 @@ -package batchValidatorManagement - -import ( - "encoding/json" - "fmt" -) - -type microserviceResponse struct { - Valid bool `json:"valid"` -} - -// String will convert the microservice response to a string -func (msr *microserviceResponse) String() string { - data, err := json.Marshal(msr) - if err != nil { - return fmt.Sprintf("microserviceResponse errored with %s", err.Error()) - } - - return string(data) -} - -type microserviceBadRequestBody struct { - StatusCode int `json:"statusCode"` - Message string `json:"message"` - Error string `json:"error"` -} - -// String will convert the microservice bad response to a string -func (msr *microserviceBadRequestBody) String() string { - data, err := json.Marshal(msr) - if err != nil { - return fmt.Sprintf("microserviceResponse errored with %s", err.Error()) - } - - return string(data) -} diff --git a/clients/batchValidator/types_test.go b/clients/batchValidator/types_test.go deleted file mode 100644 index a7a970df..00000000 --- a/clients/batchValidator/types_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package batchValidatorManagement - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMicroserviceResponse_String(t *testing.T) { - t.Parallel() - - response := µserviceResponse{ - Valid: true, - } - expectedTrueString := `{"valid":true}` - assert.Equal(t, expectedTrueString, response.String()) - - response.Valid = false - expectedFalseString := `{"valid":false}` - assert.Equal(t, expectedFalseString, response.String()) -} - -func TestMicroserviceBadRequestBody_String(t *testing.T) { - t.Parallel() - - response := µserviceBadRequestBody{ - StatusCode: 400, - Message: "Cannot read properties of undefined (reading 'length')", - Error: "Bad Request", - } - expectedString := `{"statusCode":400,"message":"Cannot read properties of undefined (reading 'length')","error":"Bad Request"}` - assert.Equal(t, expectedString, response.String()) - -} diff --git a/clients/constants.go b/clients/constants.go deleted file mode 100644 index d03f2b2f..00000000 --- a/clients/constants.go +++ /dev/null @@ -1,8 +0,0 @@ -package clients - -const ( - // Executed is the Executed with success status value - Executed = byte(3) - // Rejected is the Rejected status value - Rejected = byte(4) -) diff --git a/clients/errors.go b/clients/errors.go index dbcf1b97..c4652e8c 100644 --- a/clients/errors.go +++ b/clients/errors.go @@ -29,4 +29,13 @@ var ( // ErrMultisigContractPaused signals that the multisig contract is paused ErrMultisigContractPaused = errors.New("multisig contract paused") + + // ErrNoBatchAvailable signals that the batch is not available + ErrNoBatchAvailable = errors.New("no batch available") + + // ErrNoPendingBatchAvailable signals that no pending batch is available + ErrNoPendingBatchAvailable = errors.New("no pending batch available") + + // ErrNilCryptoHandler signals that a nil crypto handler was provided + ErrNilCryptoHandler = errors.New("nil crypto handler") ) diff --git a/clients/ethereum/client.go b/clients/ethereum/client.go index 9062c3f3..fc45358f 100644 --- a/clients/ethereum/client.go +++ b/clients/ethereum/client.go @@ -2,67 +2,64 @@ package ethereum import ( "context" - "crypto/ecdsa" "fmt" "math/big" "sync" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" chainCore "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" ) const ( - messagePrefix = "\u0019Ethereum Signed Message:\n32" - minQuorumValue = uint64(1) - minAllowedDelta = 1 + messagePrefix = "\u0019Ethereum Signed Message:\n32" + minQuorumValue = uint64(1) + minClientAvailabilityAllowDelta = 1 ) -type argListsBatch struct { - tokens []common.Address - recipients []common.Address - amounts []*big.Int - nonces []*big.Int -} - // ArgsEthereumClient is the DTO used in the ethereum's client constructor type ArgsEthereumClient struct { - ClientWrapper ClientWrapper - Erc20ContractsHandler Erc20ContractsHolder - Log chainCore.Logger - AddressConverter core.AddressConverter - Broadcaster Broadcaster - PrivateKey *ecdsa.PrivateKey - TokensMapper TokensMapper - SignatureHolder SignaturesHolder - SafeContractAddress common.Address - GasHandler GasHandler - TransferGasLimitBase uint64 - TransferGasLimitForEach uint64 - AllowDelta uint64 + ClientWrapper ClientWrapper + Erc20ContractsHandler Erc20ContractsHolder + Log chainCore.Logger + AddressConverter core.AddressConverter + Broadcaster Broadcaster + CryptoHandler CryptoHandler + TokensMapper TokensMapper + SignatureHolder SignaturesHolder + SafeContractAddress common.Address + GasHandler GasHandler + TransferGasLimitBase uint64 + TransferGasLimitForEach uint64 + ClientAvailabilityAllowDelta uint64 + EventsBlockRangeFrom int64 + EventsBlockRangeTo int64 } type client struct { - clientWrapper ClientWrapper - erc20ContractsHandler Erc20ContractsHolder - log chainCore.Logger - addressConverter core.AddressConverter - broadcaster Broadcaster - privateKey *ecdsa.PrivateKey - publicKey *ecdsa.PublicKey - tokensMapper TokensMapper - signatureHolder SignaturesHolder - safeContractAddress common.Address - gasHandler GasHandler - transferGasLimitBase uint64 - transferGasLimitForEach uint64 - allowDelta uint64 + clientWrapper ClientWrapper + erc20ContractsHandler Erc20ContractsHolder + log chainCore.Logger + addressConverter core.AddressConverter + broadcaster Broadcaster + cryptoHandler CryptoHandler + tokensMapper TokensMapper + signatureHolder SignaturesHolder + safeContractAddress common.Address + gasHandler GasHandler + transferGasLimitBase uint64 + transferGasLimitForEach uint64 + clientAvailabilityAllowDelta uint64 + eventsBlockRangeFrom int64 + eventsBlockRangeTo int64 lastBlockNumber uint64 retriesAvailabilityCheck uint64 @@ -76,31 +73,26 @@ func NewEthereumClient(args ArgsEthereumClient) (*client, error) { return nil, err } - publicKey := args.PrivateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return nil, errPublicKeyCast - } - c := &client{ - clientWrapper: args.ClientWrapper, - erc20ContractsHandler: args.Erc20ContractsHandler, - log: args.Log, - addressConverter: args.AddressConverter, - broadcaster: args.Broadcaster, - privateKey: args.PrivateKey, - publicKey: publicKeyECDSA, - tokensMapper: args.TokensMapper, - signatureHolder: args.SignatureHolder, - safeContractAddress: args.SafeContractAddress, - gasHandler: args.GasHandler, - transferGasLimitBase: args.TransferGasLimitBase, - transferGasLimitForEach: args.TransferGasLimitForEach, - allowDelta: args.AllowDelta, + clientWrapper: args.ClientWrapper, + erc20ContractsHandler: args.Erc20ContractsHandler, + log: args.Log, + addressConverter: args.AddressConverter, + broadcaster: args.Broadcaster, + cryptoHandler: args.CryptoHandler, + tokensMapper: args.TokensMapper, + signatureHolder: args.SignatureHolder, + safeContractAddress: args.SafeContractAddress, + gasHandler: args.GasHandler, + transferGasLimitBase: args.TransferGasLimitBase, + transferGasLimitForEach: args.TransferGasLimitForEach, + clientAvailabilityAllowDelta: args.ClientAvailabilityAllowDelta, + eventsBlockRangeFrom: args.EventsBlockRangeFrom, + eventsBlockRangeTo: args.EventsBlockRangeTo, } c.log.Info("NewEthereumClient", - "relayer address", crypto.PubkeyToAddress(*publicKeyECDSA), + "relayer address", c.cryptoHandler.GetAddress(), "safe contract address", c.safeContractAddress.String()) return c, err @@ -122,8 +114,8 @@ func checkArgs(args ArgsEthereumClient) error { if check.IfNil(args.Broadcaster) { return errNilBroadcaster } - if args.PrivateKey == nil { - return clients.ErrNilPrivateKey + if check.IfNil(args.CryptoHandler) { + return clients.ErrNilCryptoHandler } if check.IfNil(args.TokensMapper) { return clients.ErrNilTokensMapper @@ -140,33 +132,38 @@ func checkArgs(args ArgsEthereumClient) error { if args.TransferGasLimitForEach == 0 { return errInvalidGasLimit } - if args.AllowDelta < minAllowedDelta { + if args.ClientAvailabilityAllowDelta < minClientAvailabilityAllowDelta { return fmt.Errorf("%w for args.AllowedDelta, got: %d, minimum: %d", - clients.ErrInvalidValue, args.AllowDelta, minAllowedDelta) + clients.ErrInvalidValue, args.ClientAvailabilityAllowDelta, minClientAvailabilityAllowDelta) + } + if args.EventsBlockRangeFrom > args.EventsBlockRangeTo { + return fmt.Errorf("%w, args.EventsBlockRangeFrom: %d, args.EventsBlockRangeTo: %d", + clients.ErrInvalidValue, args.EventsBlockRangeFrom, args.EventsBlockRangeTo) } return nil } // GetBatch returns the batch (if existing) from the Ethereum contract by providing the nonce -func (c *client) GetBatch(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) { +func (c *client) GetBatch(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { c.log.Info("Getting batch", "nonce", nonce) nonceAsBigInt := big.NewInt(0).SetUint64(nonce) - batch, err := c.clientWrapper.GetBatch(ctx, nonceAsBigInt) + batch, isFinalBatch, err := c.clientWrapper.GetBatch(ctx, nonceAsBigInt) if err != nil { - return nil, err + return nil, false, err } - deposits, err := c.clientWrapper.GetBatchDeposits(ctx, nonceAsBigInt) + deposits, areFinalDeposits, err := c.clientWrapper.GetBatchDeposits(ctx, nonceAsBigInt) if err != nil { - return nil, err + return nil, false, err } if int(batch.DepositsCount) != len(deposits) { - return nil, fmt.Errorf("%w, batch.DepositsCount: %d, fetched deposits len: %d", + return nil, false, fmt.Errorf("%w, batch.DepositsCount: %d, fetched deposits len: %d", errDepositsAndBatchDepositsCountDiffer, batch.DepositsCount, len(deposits)) } - transferBatch := &clients.TransferBatch{ - ID: batch.Nonce.Uint64(), - Deposits: make([]*clients.DepositTransfer, 0, batch.DepositsCount), + transferBatch := &bridgeCore.TransferBatch{ + ID: batch.Nonce.Uint64(), + BlockNumber: batch.BlockNumber, + Deposits: make([]*bridgeCore.DepositTransfer, 0, batch.DepositsCount), } cachedTokens := make(map[string][]byte) for i := range deposits { @@ -175,25 +172,25 @@ func (c *client) GetBatch(ctx context.Context, nonce uint64) (*clients.TransferB fromBytes := deposit.Depositor[:] tokenBytes := deposit.TokenAddress[:] - depositTransfer := &clients.DepositTransfer{ + depositTransfer := &bridgeCore.DepositTransfer{ Nonce: deposit.Nonce.Uint64(), ToBytes: toBytes, - DisplayableTo: c.addressConverter.ToBech32String(toBytes), + DisplayableTo: c.addressConverter.ToBech32StringSilent(toBytes), FromBytes: fromBytes, DisplayableFrom: c.addressConverter.ToHexString(fromBytes), - TokenBytes: tokenBytes, + SourceTokenBytes: tokenBytes, DisplayableToken: c.addressConverter.ToHexString(tokenBytes), Amount: big.NewInt(0).Set(deposit.Amount), } storedConvertedTokenBytes, exists := cachedTokens[depositTransfer.DisplayableToken] if !exists { - depositTransfer.ConvertedTokenBytes, err = c.tokensMapper.ConvertToken(ctx, depositTransfer.TokenBytes) + depositTransfer.DestinationTokenBytes, err = c.tokensMapper.ConvertToken(ctx, depositTransfer.SourceTokenBytes) if err != nil { - return nil, err + return nil, false, err } - cachedTokens[depositTransfer.DisplayableToken] = depositTransfer.ConvertedTokenBytes + cachedTokens[depositTransfer.DisplayableToken] = depositTransfer.DestinationTokenBytes } else { - depositTransfer.ConvertedTokenBytes = storedConvertedTokenBytes + depositTransfer.DestinationTokenBytes = storedConvertedTokenBytes } transferBatch.Deposits = append(transferBatch.Deposits, depositTransfer) @@ -201,17 +198,55 @@ func (c *client) GetBatch(ctx context.Context, nonce uint64) (*clients.TransferB transferBatch.Statuses = make([]byte, len(transferBatch.Deposits)) - return transferBatch, nil + return transferBatch, isFinalBatch && areFinalDeposits, nil +} + +// GetBatchSCMetadata returns the emitted logs in a batch that hold metadata for SC execution on MVX +func (c *client) GetBatchSCMetadata(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + scExecAbi, err := contract.ERC20SafeMetaData.GetAbi() + if err != nil { + return nil, err + } + + query := ethereum.FilterQuery{ + Addresses: []common.Address{c.safeContractAddress}, + Topics: [][]common.Hash{ + {scExecAbi.Events["ERC20SCDeposit"].ID}, + {common.BytesToHash(new(big.Int).SetUint64(nonce).Bytes())}, + }, + FromBlock: big.NewInt(blockNumber + c.eventsBlockRangeFrom), + ToBlock: big.NewInt(blockNumber + c.eventsBlockRangeTo), + } + + logs, err := c.clientWrapper.FilterLogs(ctx, query) + if err != nil { + return nil, err + } + + depositEvents := make([]*contract.ERC20SafeERC20SCDeposit, 0) + for _, vLog := range logs { + event := new(contract.ERC20SafeERC20SCDeposit) + err = scExecAbi.UnpackIntoInterface(event, "ERC20SCDeposit", vLog.Data) + if err != nil { + return nil, err + } + + // Add this manually since UnpackIntoInterface only unpacks non-indexed arguments + event.BatchId = big.NewInt(0).SetUint64(nonce) + depositEvents = append(depositEvents, event) + } + + return depositEvents, nil } -// WasExecuted returns true if the batch ID was executed -func (c *client) WasExecuted(ctx context.Context, batchID uint64) (bool, error) { - return c.clientWrapper.WasBatchExecuted(ctx, big.NewInt(0).SetUint64(batchID)) +// WasExecuted returns true if the MultiversX batch ID was executed +func (c *client) WasExecuted(ctx context.Context, mvxBatchID uint64) (bool, error) { + return c.clientWrapper.WasBatchExecuted(ctx, big.NewInt(0).SetUint64(mvxBatchID)) } // BroadcastSignatureForMessageHash will send the signature for the provided message hash func (c *client) BroadcastSignatureForMessageHash(msgHash common.Hash) { - signature, err := crypto.Sign(msgHash.Bytes(), c.privateKey) + signature, err := c.cryptoHandler.Sign(msgHash) if err != nil { c.log.Error("error generating signature", "msh hash", msgHash, "error", err) return @@ -221,7 +256,12 @@ func (c *client) BroadcastSignatureForMessageHash(msgHash common.Hash) { } // GenerateMessageHash will generate the message hash based on the provided batch -func (c *client) GenerateMessageHash(batch *clients.TransferBatch) (common.Hash, error) { +func (c *client) GenerateMessageHash(batch *batchProcessor.ArgListsBatch, batchId uint64) (common.Hash, error) { + return GenerateMessageHash(batch, batchId) +} + +// GenerateMessageHash will generate the message hash based on the provided batch +func GenerateMessageHash(batch *batchProcessor.ArgListsBatch, batchId uint64) (common.Hash, error) { if batch == nil { return common.Hash{}, clients.ErrNilBatch } @@ -231,12 +271,7 @@ func (c *client) GenerateMessageHash(batch *clients.TransferBatch) (common.Hash, return common.Hash{}, err } - argLists, err := c.extractList(batch) - if err != nil { - return common.Hash{}, err - } - - pack, err := args.Pack(argLists.recipients, argLists.tokens, argLists.amounts, argLists.nonces, big.NewInt(0).SetUint64(batch.ID), "ExecuteBatchedTransfer") + pack, err := args.Pack(batch.Recipients, batch.EthTokens, batch.Amounts, batch.Nonces, big.NewInt(0).SetUint64(batchId), "ExecuteBatchedTransfer") if err != nil { return common.Hash{}, err } @@ -276,34 +311,15 @@ func generateTransferArgs() (abi.Arguments, error) { }, nil } -func (c *client) extractList(batch *clients.TransferBatch) (argListsBatch, error) { - arg := argListsBatch{} - - for _, dt := range batch.Deposits { - recipient := common.BytesToAddress(dt.ToBytes) - arg.recipients = append(arg.recipients, recipient) - - token := common.BytesToAddress(dt.ConvertedTokenBytes) - arg.tokens = append(arg.tokens, token) - - amount := big.NewInt(0).Set(dt.Amount) - arg.amounts = append(arg.amounts, amount) - - nonce := big.NewInt(0).SetUint64(dt.Nonce) - arg.nonces = append(arg.nonces, nonce) - } - - return arg, nil -} - // ExecuteTransfer will initiate and send the transaction from the transfer batch struct func (c *client) ExecuteTransfer( ctx context.Context, msgHash common.Hash, - batch *clients.TransferBatch, + argLists *batchProcessor.ArgListsBatch, + batchId uint64, quorum int, ) (string, error) { - if batch == nil { + if argLists == nil { return "", clients.ErrNilBatch } @@ -315,11 +331,7 @@ func (c *client) ExecuteTransfer( return "", fmt.Errorf("%w in client.ExecuteTransfer", clients.ErrMultisigContractPaused) } - c.log.Info("executing transfer " + batch.String()) - - fromAddress := crypto.PubkeyToAddress(*c.publicKey) - - nonce, err := c.getNonce(ctx, fromAddress) + nonce, err := c.getNonce(ctx, c.cryptoHandler.GetAddress()) if err != nil { return "", err } @@ -329,7 +341,7 @@ func (c *client) ExecuteTransfer( return "", err } - auth, err := bind.NewKeyedTransactorWithChainID(c.privateKey, chainId) + auth, err := c.cryptoHandler.CreateKeyedTransactor(chainId) if err != nil { return "", err } @@ -341,7 +353,7 @@ func (c *client) ExecuteTransfer( auth.Nonce = big.NewInt(nonce) auth.Value = big.NewInt(0) - auth.GasLimit = c.transferGasLimitBase + uint64(len(batch.Deposits))*c.transferGasLimitForEach + auth.GasLimit = c.transferGasLimitBase + uint64(len(argLists.EthTokens))*c.transferGasLimitForEach auth.Context = ctx auth.GasPrice = gasPrice @@ -355,16 +367,6 @@ func (c *client) ExecuteTransfer( signatures = signatures[:quorum] } - argLists, err := c.extractList(batch) - if err != nil { - return "", err - } - - err = c.checkAvailableTokens(ctx, argLists.tokens, argLists.amounts) - if err != nil { - return "", err - } - minimumForFee := big.NewInt(int64(auth.GasLimit)) minimumForFee.Mul(minimumForFee, auth.GasPrice) err = c.checkRelayerFundsForFee(ctx, minimumForFee) @@ -372,8 +374,8 @@ func (c *client) ExecuteTransfer( return "", err } - batchID := big.NewInt(0).SetUint64(batch.ID) - tx, err := c.clientWrapper.ExecuteTransfer(auth, argLists.tokens, argLists.recipients, argLists.amounts, argLists.nonces, batchID, signatures) + batchID := big.NewInt(0).SetUint64(batchId) + tx, err := c.clientWrapper.ExecuteTransfer(auth, argLists.EthTokens, argLists.Recipients, argLists.Amounts, argLists.Nonces, batchID, signatures) if err != nil { return "", err } @@ -391,7 +393,7 @@ func (c *client) CheckClientAvailability(ctx context.Context) error { currentBlock, err := c.clientWrapper.BlockNumber(ctx) if err != nil { - c.setStatusForAvailabilityCheck(ethmultiversx.Unavailable, err.Error(), currentBlock) + c.setStatusForAvailabilityCheck(bridgeCore.Unavailable, err.Error(), currentBlock) return err } @@ -404,14 +406,14 @@ func (c *client) CheckClientAvailability(ctx context.Context) error { // if we reached this point we will need to increment the retries counter defer c.incrementRetriesAvailabilityCheck() - if c.retriesAvailabilityCheck > c.allowDelta { + if c.retriesAvailabilityCheck > c.clientAvailabilityAllowDelta { message := fmt.Sprintf("block %d fetched for %d times in a row", currentBlock, c.retriesAvailabilityCheck) - c.setStatusForAvailabilityCheck(ethmultiversx.Unavailable, message, currentBlock) + c.setStatusForAvailabilityCheck(bridgeCore.Unavailable, message, currentBlock) return nil } - c.setStatusForAvailabilityCheck(ethmultiversx.Available, "", currentBlock) + c.setStatusForAvailabilityCheck(bridgeCore.Available, "", currentBlock) return nil } @@ -420,60 +422,74 @@ func (c *client) incrementRetriesAvailabilityCheck() { c.retriesAvailabilityCheck++ } -func (c *client) setStatusForAvailabilityCheck(status ethmultiversx.ClientStatus, message string, nonce uint64) { +func (c *client) setStatusForAvailabilityCheck(status bridgeCore.ClientStatus, message string, nonce uint64) { c.clientWrapper.SetStringMetric(core.MetricMultiversXClientStatus, status.String()) c.clientWrapper.SetStringMetric(core.MetricLastMultiversXClientError, message) c.clientWrapper.SetIntMetric(core.MetricLastBlockNonce, int(nonce)) } -func (c *client) checkAvailableTokens(ctx context.Context, tokens []common.Address, amounts []*big.Int) error { - transfers := c.getCumulatedTransfers(tokens, amounts) +// CheckRequiredBalance will check if the safe has enough balance for the transfer +func (c *client) CheckRequiredBalance(ctx context.Context, erc20Address common.Address, value *big.Int) error { + isMintBurn, err := c.MintBurnTokens(ctx, erc20Address) + if err != nil { + return err + } - return c.checkCumulatedTransfers(ctx, transfers) -} + if isMintBurn { + return nil + } -func (c *client) getCumulatedTransfers(tokens []common.Address, amounts []*big.Int) map[common.Address]*big.Int { - transfers := make(map[common.Address]*big.Int) - for i, token := range tokens { - existing, found := transfers[token] - if !found { - existing = big.NewInt(0) - transfers[token] = existing - } + existingBalance, err := c.erc20ContractsHandler.BalanceOf(ctx, erc20Address, c.safeContractAddress) + if err != nil { + return fmt.Errorf("%w for address %s for ERC20 token %s", err, c.safeContractAddress.String(), erc20Address.String()) + } - existing.Add(existing, amounts[i]) + if value.Cmp(existingBalance) > 0 { + return fmt.Errorf("%w, existing: %s, required: %s for ERC20 token %s and address %s", + errInsufficientErc20Balance, existingBalance.String(), value.String(), erc20Address.String(), c.safeContractAddress.String()) } - return transfers + c.log.Debug("checked ERC20 balance", + "ERC20 token", erc20Address.String(), + "address", c.safeContractAddress.String(), + "existing balance", existingBalance.String(), + "needed", value.String()) + + return nil } -func (c *client) checkCumulatedTransfers(ctx context.Context, transfers map[common.Address]*big.Int) error { - for erc20Address, value := range transfers { - existingBalance, err := c.erc20ContractsHandler.BalanceOf(ctx, erc20Address, c.safeContractAddress) - if err != nil { - return fmt.Errorf("%w for address %s for ERC20 token %s", err, c.safeContractAddress.String(), erc20Address.String()) - } +// TotalBalances returns the total balance of the given token +func (c *client) TotalBalances(ctx context.Context, token common.Address) (*big.Int, error) { + return c.clientWrapper.TotalBalances(ctx, token) +} - if value.Cmp(existingBalance) > 0 { - return fmt.Errorf("%w, existing: %s, required: %s for ERC20 token %s and address %s", - errInsufficientErc20Balance, existingBalance.String(), value.String(), erc20Address.String(), c.safeContractAddress.String()) - } +// MintBalances returns the mint balance of the given token +func (c *client) MintBalances(ctx context.Context, token common.Address) (*big.Int, error) { + return c.clientWrapper.MintBalances(ctx, token) +} - c.log.Debug("checked ERC20 balance", - "ERC20 token", erc20Address.String(), - "address", c.safeContractAddress.String(), - "existing balance", existingBalance.String(), - "needed", value.String()) - } +// BurnBalances returns the burn balance of the given token +func (c *client) BurnBalances(ctx context.Context, token common.Address) (*big.Int, error) { + return c.clientWrapper.BurnBalances(ctx, token) +} - return nil +// MintBurnTokens returns true if the token is mintBurn token +func (c *client) MintBurnTokens(ctx context.Context, token common.Address) (bool, error) { + return c.clientWrapper.MintBurnTokens(ctx, token) } -func (c *client) checkRelayerFundsForFee(ctx context.Context, transferFee *big.Int) error { +// NativeTokens returns true if the token is native +func (c *client) NativeTokens(ctx context.Context, token common.Address) (bool, error) { + return c.clientWrapper.NativeTokens(ctx, token) +} - ethereumRelayerAddress := crypto.PubkeyToAddress(*c.publicKey) +// WhitelistedTokens returns true if the token is whitelisted +func (c *client) WhitelistedTokens(ctx context.Context, token common.Address) (bool, error) { + return c.clientWrapper.WhitelistedTokens(ctx, token) +} - existingBalance, err := c.clientWrapper.BalanceAt(ctx, ethereumRelayerAddress, nil) +func (c *client) checkRelayerFundsForFee(ctx context.Context, transferFee *big.Int) error { + existingBalance, err := c.clientWrapper.BalanceAt(ctx, c.cryptoHandler.GetAddress(), nil) if err != nil { return err } @@ -503,7 +519,15 @@ func (c *client) getNonce(ctx context.Context, fromAddress common.Address) (int6 // GetTransactionsStatuses will return the transactions statuses from the batch func (c *client) GetTransactionsStatuses(ctx context.Context, batchId uint64) ([]byte, error) { - return c.clientWrapper.GetStatusesAfterExecution(ctx, big.NewInt(0).SetUint64(batchId)) + buff, isFinal, err := c.clientWrapper.GetStatusesAfterExecution(ctx, big.NewInt(0).SetUint64(batchId)) + if err != nil { + return nil, err + } + if !isFinal { + return nil, errStatusIsNotFinal + } + + return buff, nil } // GetQuorumSize returns the size of the quorum diff --git a/clients/ethereum/client_test.go b/clients/ethereum/client_test.go index 0e7aa2e4..a13922c7 100644 --- a/clients/ethereum/client_test.go +++ b/clients/ethereum/client_test.go @@ -9,20 +9,21 @@ import ( "strings" "testing" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" "github.com/multiversx/mx-bridge-eth-go/clients" "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" "github.com/multiversx/mx-bridge-eth-go/core/converters" "github.com/multiversx/mx-bridge-eth-go/testsCommon" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/multiversx/mx-chain-core-go/core/check" logger "github.com/multiversx/mx-chain-logger-go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var expectedAmounts = []*big.Int{big.NewInt(20), big.NewInt(40)} @@ -31,8 +32,6 @@ var expectedRecipients = []common.Address{common.BytesToAddress([]byte("to1")), var expectedNonces = []*big.Int{big.NewInt(10), big.NewInt(30)} func createMockEthereumClientArgs() ArgsEthereumClient { - sk, _ := crypto.HexToECDSA("9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb1") - addressConverter, err := converters.NewAddressConverter() if err != nil { panic(err) @@ -44,46 +43,48 @@ func createMockEthereumClientArgs() ArgsEthereumClient { Log: logger.GetOrCreate("test"), AddressConverter: addressConverter, Broadcaster: &testsCommon.BroadcasterStub{}, - PrivateKey: sk, + CryptoHandler: &bridgeTests.CryptoHandlerStub{}, TokensMapper: &bridgeTests.TokensMapperStub{ ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { return append([]byte("ERC20"), sourceBytes...), nil }, }, - SignatureHolder: &testsCommon.SignaturesHolderStub{}, - SafeContractAddress: testsCommon.CreateRandomEthereumAddress(), - GasHandler: &testsCommon.GasHandlerStub{}, - TransferGasLimitBase: 50, - TransferGasLimitForEach: 20, - AllowDelta: 5, + SignatureHolder: &testsCommon.SignaturesHolderStub{}, + SafeContractAddress: testsCommon.CreateRandomEthereumAddress(), + GasHandler: &testsCommon.GasHandlerStub{}, + TransferGasLimitBase: 50, + TransferGasLimitForEach: 20, + ClientAvailabilityAllowDelta: 5, + EventsBlockRangeFrom: -100, + EventsBlockRangeTo: 400, } } -func createMockTransferBatch() *clients.TransferBatch { - return &clients.TransferBatch{ +func createMockTransferBatch() *bridgeCore.TransferBatch { + return &bridgeCore.TransferBatch{ ID: 332, - Deposits: []*clients.DepositTransfer{ + Deposits: []*bridgeCore.DepositTransfer{ { - Nonce: 10, - ToBytes: []byte("to1"), - DisplayableTo: "to1", - FromBytes: []byte("from1"), - DisplayableFrom: "from1", - TokenBytes: []byte("token1"), - DisplayableToken: "token1", - Amount: big.NewInt(20), - ConvertedTokenBytes: []byte("ERC20token1"), + Nonce: 10, + ToBytes: []byte("to1"), + DisplayableTo: "to1", + FromBytes: []byte("from1"), + DisplayableFrom: "from1", + SourceTokenBytes: []byte("source token1"), + DisplayableToken: "token1", + Amount: big.NewInt(20), + DestinationTokenBytes: []byte("ERC20token1"), }, { - Nonce: 30, - ToBytes: []byte("to2"), - DisplayableTo: "to2", - FromBytes: []byte("from2"), - DisplayableFrom: "from2", - TokenBytes: []byte("token2"), - DisplayableToken: "token2", - Amount: big.NewInt(40), - ConvertedTokenBytes: []byte("ERC20token2"), + Nonce: 30, + ToBytes: []byte("to2"), + DisplayableTo: "to2", + FromBytes: []byte("from2"), + DisplayableFrom: "from2", + SourceTokenBytes: []byte("source token2"), + DisplayableToken: "token2", + Amount: big.NewInt(40), + DestinationTokenBytes: []byte("ERC20token2"), }, }, Statuses: make([]byte, 2), @@ -133,12 +134,12 @@ func TestNewEthereumClient(t *testing.T) { assert.Equal(t, errNilBroadcaster, err) assert.True(t, check.IfNil(c)) }) - t.Run("nil private key", func(t *testing.T) { + t.Run("nil crypto handler", func(t *testing.T) { args := createMockEthereumClientArgs() - args.PrivateKey = nil + args.CryptoHandler = nil c, err := NewEthereumClient(args) - assert.Equal(t, clients.ErrNilPrivateKey, err) + assert.Equal(t, clients.ErrNilCryptoHandler, err) assert.True(t, check.IfNil(c)) }) t.Run("nil tokens mapper", func(t *testing.T) { @@ -181,11 +182,11 @@ func TestNewEthereumClient(t *testing.T) { assert.Equal(t, errInvalidGasLimit, err) assert.True(t, check.IfNil(c)) }) - t.Run("invalid AllowDelta should error", func(t *testing.T) { + t.Run("invalid ClientAvailabilityAllowDelta should error", func(t *testing.T) { t.Parallel() args := createMockEthereumClientArgs() - args.AllowDelta = 0 + args.ClientAvailabilityAllowDelta = 0 c, err := NewEthereumClient(args) @@ -193,6 +194,20 @@ func TestNewEthereumClient(t *testing.T) { assert.True(t, errors.Is(err, clients.ErrInvalidValue)) assert.True(t, strings.Contains(err.Error(), "for args.AllowedDelta")) }) + t.Run("invalid events block range from should error", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + args.EventsBlockRangeFrom = 100 + args.EventsBlockRangeTo = 50 + + c, err := NewEthereumClient(args) + + assert.True(t, check.IfNil(c)) + assert.True(t, errors.Is(err, clients.ErrInvalidValue)) + assert.True(t, strings.Contains(err.Error(), "args.EventsBlockRangeFrom")) + assert.True(t, strings.Contains(err.Error(), "args.EventsBlockRangeTo")) + }) t.Run("should work", func(t *testing.T) { args := createMockEthereumClientArgs() c, err := NewEthereumClient(args) @@ -211,67 +226,71 @@ func TestClient_GetBatch(t *testing.T) { t.Run("error while getting batch", func(t *testing.T) { c.clientWrapper = &bridgeTests.EthereumClientWrapperStub{ - GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { - return contract.Batch{}, expectedErr + GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { + return contract.Batch{}, false, expectedErr }, } - batch, err := c.GetBatch(context.Background(), 1) + batch, isFinal, err := c.GetBatch(context.Background(), 1) assert.Nil(t, batch) assert.Equal(t, expectedErr, err) + assert.False(t, isFinal) }) t.Run("error while getting deposits", func(t *testing.T) { c.clientWrapper = &bridgeTests.EthereumClientWrapperStub{ - GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { + GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { return contract.Batch{ Nonce: batchNonce, DepositsCount: 2, - }, nil + }, true, nil }, - GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { - return nil, expectedErr + GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { + return nil, false, expectedErr }, } - batch, err := c.GetBatch(context.Background(), 1) + batch, isFinal, err := c.GetBatch(context.Background(), 1) assert.Nil(t, batch) assert.Equal(t, expectedErr, err) + assert.False(t, isFinal) }) t.Run("deposits mismatch - with 0", func(t *testing.T) { c.clientWrapper = &bridgeTests.EthereumClientWrapperStub{ - GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { + GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { return contract.Batch{ Nonce: batchNonce, DepositsCount: 2, - }, nil + }, true, nil }, - GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { - return make([]contract.Deposit, 0), nil + GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { + return make([]contract.Deposit, 0), true, nil }, } - batch, err := c.GetBatch(context.Background(), 1) + batch, isFinal, err := c.GetBatch(context.Background(), 1) assert.Nil(t, batch) assert.True(t, errors.Is(err, errDepositsAndBatchDepositsCountDiffer)) assert.True(t, strings.Contains(err.Error(), "batch.DepositsCount: 2, fetched deposits len: 0")) + assert.False(t, isFinal) }) t.Run("deposits mismatch - with non zero value", func(t *testing.T) { c.clientWrapper = &bridgeTests.EthereumClientWrapperStub{ - GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { + GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { return contract.Batch{ Nonce: batchNonce, DepositsCount: 2, - }, nil + }, true, nil }, - GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { + GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { return []contract.Deposit{ { Nonce: big.NewInt(22), }, - }, nil + }, true, nil }, } - batch, err := c.GetBatch(context.Background(), 1) + batch, isFinal, err := c.GetBatch(context.Background(), 1) assert.Nil(t, batch) assert.True(t, errors.Is(err, errDepositsAndBatchDepositsCountDiffer)) assert.True(t, strings.Contains(err.Error(), "batch.DepositsCount: 2, fetched deposits len: 1")) + assert.False(t, isFinal) }) t.Run("returns batch should work", func(t *testing.T) { from1 := testsCommon.CreateRandomEthereumAddress() @@ -283,15 +302,15 @@ func TestClient_GetBatch(t *testing.T) { recipient2 := testsCommon.CreateRandomMultiversXAddress() c.clientWrapper = &bridgeTests.EthereumClientWrapperStub{ - GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { + GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { return contract.Batch{ Nonce: big.NewInt(112243), BlockNumber: 0, LastUpdatedBlockNumber: 0, DepositsCount: 2, - }, nil + }, true, nil }, - GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { + GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { return []contract.Deposit{ { Nonce: big.NewInt(10), @@ -307,44 +326,120 @@ func TestClient_GetBatch(t *testing.T) { Depositor: from2, Recipient: recipient2.AddressSlice(), }, - }, nil + }, true, nil }, } - expectedBatch := &clients.TransferBatch{ + bech32Recipient1Address, _ := recipient1.AddressAsBech32String() + bech32Recipient2Address, _ := recipient2.AddressAsBech32String() + expectedBatch := &bridgeCore.TransferBatch{ ID: 112243, - Deposits: []*clients.DepositTransfer{ + Deposits: []*bridgeCore.DepositTransfer{ { - Nonce: 10, - ToBytes: recipient1.AddressBytes(), - DisplayableTo: recipient1.AddressAsBech32String(), - FromBytes: from1[:], - DisplayableFrom: hex.EncodeToString(from1[:]), - TokenBytes: token1[:], - DisplayableToken: hex.EncodeToString(token1[:]), - Amount: big.NewInt(20), - ConvertedTokenBytes: append([]byte("ERC20"), token1[:]...), + Nonce: 10, + ToBytes: recipient1.AddressBytes(), + DisplayableTo: bech32Recipient1Address, + FromBytes: from1[:], + DisplayableFrom: hex.EncodeToString(from1[:]), + SourceTokenBytes: token1[:], + DisplayableToken: hex.EncodeToString(token1[:]), + Amount: big.NewInt(20), + DestinationTokenBytes: append([]byte("ERC20"), token1[:]...), }, { - Nonce: 30, - ToBytes: recipient2.AddressBytes(), - DisplayableTo: recipient2.AddressAsBech32String(), - FromBytes: from2[:], - DisplayableFrom: hex.EncodeToString(from2[:]), - TokenBytes: token2[:], - DisplayableToken: hex.EncodeToString(token2[:]), - Amount: big.NewInt(40), - ConvertedTokenBytes: append([]byte("ERC20"), token2[:]...), + Nonce: 30, + ToBytes: recipient2.AddressBytes(), + DisplayableTo: bech32Recipient2Address, + FromBytes: from2[:], + DisplayableFrom: hex.EncodeToString(from2[:]), + SourceTokenBytes: token2[:], + DisplayableToken: hex.EncodeToString(token2[:]), + Amount: big.NewInt(40), + DestinationTokenBytes: append([]byte("ERC20"), token2[:]...), }, }, Statuses: make([]byte, 2), } - batch, err := c.GetBatch(context.Background(), 1) + batch, isFinal, err := c.GetBatch(context.Background(), 1) assert.Equal(t, expectedBatch, batch) assert.Nil(t, err) + assert.True(t, isFinal) }) + t.Run("returns non final batch should work", func(t *testing.T) { + from1 := testsCommon.CreateRandomEthereumAddress() + token1 := testsCommon.CreateRandomEthereumAddress() + recipient1 := testsCommon.CreateRandomMultiversXAddress() + + from2 := testsCommon.CreateRandomEthereumAddress() + token2 := testsCommon.CreateRandomEthereumAddress() + recipient2 := testsCommon.CreateRandomMultiversXAddress() + c.clientWrapper = &bridgeTests.EthereumClientWrapperStub{ + GetBatchCalled: func(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { + return contract.Batch{ + Nonce: big.NewInt(112243), + BlockNumber: 0, + LastUpdatedBlockNumber: 0, + DepositsCount: 2, + }, false, nil + }, + GetBatchDepositsCalled: func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { + return []contract.Deposit{ + { + Nonce: big.NewInt(10), + TokenAddress: token1, + Amount: big.NewInt(20), + Depositor: from1, + Recipient: recipient1.AddressSlice(), + }, + { + Nonce: big.NewInt(30), + TokenAddress: token2, + Amount: big.NewInt(40), + Depositor: from2, + Recipient: recipient2.AddressSlice(), + }, + }, false, nil + }, + } + + bech32Recipient1Address, _ := recipient1.AddressAsBech32String() + bech32Recipient2Address, _ := recipient2.AddressAsBech32String() + expectedBatch := &bridgeCore.TransferBatch{ + ID: 112243, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: 10, + ToBytes: recipient1.AddressBytes(), + DisplayableTo: bech32Recipient1Address, + FromBytes: from1[:], + DisplayableFrom: hex.EncodeToString(from1[:]), + SourceTokenBytes: token1[:], + DisplayableToken: hex.EncodeToString(token1[:]), + Amount: big.NewInt(20), + DestinationTokenBytes: append([]byte("ERC20"), token1[:]...), + }, + { + Nonce: 30, + ToBytes: recipient2.AddressBytes(), + DisplayableTo: bech32Recipient2Address, + FromBytes: from2[:], + DisplayableFrom: hex.EncodeToString(from2[:]), + SourceTokenBytes: token2[:], + DisplayableToken: hex.EncodeToString(token2[:]), + Amount: big.NewInt(40), + DestinationTokenBytes: append([]byte("ERC20"), token2[:]...), + }, + }, + Statuses: make([]byte, 2), + } + + batch, isFinal, err := c.GetBatch(context.Background(), 1) + assert.Equal(t, expectedBatch, batch) + assert.Nil(t, err) + assert.False(t, isFinal) + }) } func TestClient_GenerateMessageHash(t *testing.T) { @@ -355,20 +450,20 @@ func TestClient_GenerateMessageHash(t *testing.T) { t.Run("nil batch should error", func(t *testing.T) { c, _ := NewEthereumClient(args) - h, err := c.GenerateMessageHash(nil) + h, err := c.GenerateMessageHash(nil, 0) assert.Equal(t, common.Hash{}, h) assert.True(t, errors.Is(err, clients.ErrNilBatch)) }) t.Run("should work", func(t *testing.T) { c, _ := NewEthereumClient(args) - argLists, _ := c.extractList(batch) - assert.Equal(t, expectedAmounts, argLists.amounts) - assert.Equal(t, expectedTokens, argLists.tokens) - assert.Equal(t, expectedRecipients, argLists.recipients) - assert.Equal(t, expectedNonces, argLists.nonces) + argLists := batchProcessor.ExtractListMvxToEth(batch) + assert.Equal(t, expectedAmounts, argLists.Amounts) + assert.Equal(t, expectedTokens, argLists.EthTokens) + assert.Equal(t, expectedRecipients, argLists.Recipients) + assert.Equal(t, expectedNonces, argLists.Nonces) - h, err := c.GenerateMessageHash(batch) + h, err := c.GenerateMessageHash(argLists, batch.ID) assert.Nil(t, err) assert.Equal(t, "c68190e0a3b8d7c6bd966272a11d618ceddc4b38662b0a1610621f4d30ec07ca", hex.EncodeToString(h.Bytes())) }) @@ -377,23 +472,54 @@ func TestClient_GenerateMessageHash(t *testing.T) { func TestClient_BroadcastSignatureForMessageHash(t *testing.T) { t.Parallel() - expectedSig := "b556014dd984183e4662dc3204e522a5a92093fd6f64bb2da9c1b66b8d5ad12d774e05728b83c76bf09bb91af93ede4118f59aa949c7d02c86051dd0fa140c9900" - broadcastCalled := false + t.Run("sign failed should not broadcast", func(t *testing.T) { + t.Parallel() - hash := common.HexToHash("c99286352d865e33f1747761cbd440a7906b9bd8a5261cb6909e5ba18dd19b08") - args := createMockEthereumClientArgs() - args.Broadcaster = &testsCommon.BroadcasterStub{ - BroadcastSignatureCalled: func(signature []byte, messageHash []byte) { - assert.Equal(t, hash.Bytes(), messageHash) - assert.Equal(t, expectedSig, hex.EncodeToString(signature)) - broadcastCalled = true - }, - } + expectedError := errors.New("expected error") + hash := common.HexToHash("hash") + args := createMockEthereumClientArgs() + args.Broadcaster = &testsCommon.BroadcasterStub{ + BroadcastSignatureCalled: func(signature []byte, messageHash []byte) { + assert.Fail(t, "should have not called bradcast") + }, + } + args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ + SignCalled: func(msgHash common.Hash) ([]byte, error) { + assert.Equal(t, hash.Bytes(), msgHash.Bytes()) + return nil, expectedError + }, + } - c, _ := NewEthereumClient(args) - c.BroadcastSignatureForMessageHash(hash) + c, _ := NewEthereumClient(args) + c.BroadcastSignatureForMessageHash(hash) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + expectedSig := "expected sig" + broadcastCalled := false + + hash := common.HexToHash("hash") + args := createMockEthereumClientArgs() + args.Broadcaster = &testsCommon.BroadcasterStub{ + BroadcastSignatureCalled: func(signature []byte, messageHash []byte) { + assert.Equal(t, hash.Bytes(), messageHash) + assert.Equal(t, expectedSig, string(signature)) + broadcastCalled = true + }, + } + args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ + SignCalled: func(msgHash common.Hash) ([]byte, error) { + assert.Equal(t, hash.Bytes(), msgHash.Bytes()) + return []byte(expectedSig), nil + }, + } - assert.True(t, broadcastCalled) + c, _ := NewEthereumClient(args) + c.BroadcastSignatureForMessageHash(hash) + + assert.True(t, broadcastCalled) + }) } func TestClient_WasExecuted(t *testing.T) { @@ -419,7 +545,13 @@ func TestClient_ExecuteTransfer(t *testing.T) { t.Parallel() args := createMockEthereumClientArgs() + args.CryptoHandler = &bridgeTests.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return &bind.TransactOpts{}, nil + }, + } batch := createMockTransferBatch() + argLists := batchProcessor.ExtractListMvxToEth(batch) signatures := make([][]byte, 10) for i := range signatures { signatures[i] = []byte(fmt.Sprintf("sig %d", i)) @@ -427,7 +559,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { t.Run("nil batch", func(t *testing.T) { c, _ := NewEthereumClient(args) - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, nil, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, nil, 0, 10) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, clients.ErrNilBatch)) }) @@ -439,7 +571,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { return false, expectedErr }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, expectedErr)) }) @@ -450,7 +582,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { return true, nil }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, clients.ErrMultisigContractPaused)) }) @@ -462,7 +594,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { return 0, expectedErr }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, expectedErr)) }) @@ -474,7 +606,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { return 0, expectedErr }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, expectedErr)) }) @@ -486,7 +618,19 @@ func TestClient_ExecuteTransfer(t *testing.T) { return big.NewInt(0), expectedErr }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) + assert.Equal(t, "", hash) + assert.True(t, errors.Is(err, expectedErr)) + }) + t.Run("create keyed transactor fails", func(t *testing.T) { + expectedErr := errors.New("expected error create keyed transactor") + c, _ := NewEthereumClient(args) + c.cryptoHandler = &bridgeTests.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return nil, expectedErr + }, + } + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, expectedErr)) }) @@ -498,9 +642,9 @@ func TestClient_ExecuteTransfer(t *testing.T) { return nil, expectedErr }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) - assert.True(t, errors.Is(err, expectedErr)) + assert.ErrorIs(t, err, expectedErr) }) t.Run("not enough quorum", func(t *testing.T) { c, _ := NewEthereumClient(args) @@ -509,7 +653,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { return signatures[:9] }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 10) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 10) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, errQuorumNotReached)) assert.True(t, strings.Contains(err.Error(), "num signatures: 9, quorum: 10")) @@ -537,76 +681,22 @@ func TestClient_ExecuteTransfer(t *testing.T) { }, } newBatch := batch.Clone() - newBatch.Deposits = append(newBatch.Deposits, &clients.DepositTransfer{ - Nonce: 40, - ToBytes: []byte("to3"), - DisplayableTo: "to3", - FromBytes: []byte("from3"), - DisplayableFrom: "from3", - TokenBytes: []byte("token1"), - DisplayableToken: "token1", - Amount: big.NewInt(80), - ConvertedTokenBytes: []byte("ERC20token1"), + newBatch.Deposits = append(newBatch.Deposits, &bridgeCore.DepositTransfer{ + Nonce: 40, + ToBytes: []byte("to3"), + DisplayableTo: "to3", + FromBytes: []byte("from3"), + DisplayableFrom: "from3", + SourceTokenBytes: []byte("source token1"), + DisplayableToken: "token1", + Amount: big.NewInt(80), + DestinationTokenBytes: []byte("ERC20token1"), }) - - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, newBatch, 9) + newArgLists := batchProcessor.ExtractListMvxToEth(newBatch) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, newArgLists, newBatch.ID, 9) assert.Equal(t, "", hash) assert.True(t, errors.Is(err, errInsufficientBalance)) }) - t.Run("not enough erc20 balance", func(t *testing.T) { - c, _ := NewEthereumClient(args) - c.signatureHolder = &testsCommon.SignaturesHolderStub{ - SignaturesCalled: func(messageHash []byte) [][]byte { - return signatures[:9] - }, - } - c.erc20ContractsHandler = &bridgeTests.ERC20ContractsHolderStub{ - BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { - assert.Equal(t, c.safeContractAddress, address) - tokenErc20 := common.BytesToAddress([]byte("ERC20token1")) - if erc20Address.String() == tokenErc20.String() { - return big.NewInt(99), nil - } - - return big.NewInt(1000000), nil - }, - } - - newBatch := batch.Clone() - newBatch.Deposits = append(newBatch.Deposits, &clients.DepositTransfer{ - Nonce: 40, - ToBytes: []byte("to3"), - DisplayableTo: "to3", - FromBytes: []byte("from3"), - DisplayableFrom: "from3", - TokenBytes: []byte("token1"), - DisplayableToken: "token1", - Amount: big.NewInt(80), - ConvertedTokenBytes: []byte("ERC20token1"), - }) - - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, newBatch, 9) - assert.Equal(t, "", hash) - assert.True(t, errors.Is(err, errInsufficientErc20Balance)) - }) - t.Run("erc20 balance of errors", func(t *testing.T) { - expectedErr := errors.New("expected error erc20 balance of") - c, _ := NewEthereumClient(args) - c.signatureHolder = &testsCommon.SignaturesHolderStub{ - SignaturesCalled: func(messageHash []byte) [][]byte { - return signatures[:9] - }, - } - c.erc20ContractsHandler = &bridgeTests.ERC20ContractsHolderStub{ - BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { - return nil, expectedErr - }, - } - - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 9) - assert.Equal(t, "", hash) - assert.True(t, errors.Is(err, expectedErr)) - }) t.Run("execute transfer errors", func(t *testing.T) { expectedErr := errors.New("expected error execute transfer") c, _ := NewEthereumClient(args) @@ -626,7 +716,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 9) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 9) assert.Equal(t, "", hash) assert.Equal(t, expectedErr, err) }) @@ -660,7 +750,7 @@ func TestClient_ExecuteTransfer(t *testing.T) { }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 9) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 9) assert.Equal(t, "0xc5b2c658f5fa236c598a6e7fbf7f21413dc42e2a41dd982eb772b30707cba2eb", hash) assert.Nil(t, err) assert.True(t, wasCalled) @@ -695,31 +785,294 @@ func TestClient_ExecuteTransfer(t *testing.T) { }, } - hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, batch, 5) + hash, err := c.ExecuteTransfer(context.Background(), common.Hash{}, argLists, batch.ID, 5) assert.Equal(t, "0xc5b2c658f5fa236c598a6e7fbf7f21413dc42e2a41dd982eb772b30707cba2eb", hash) assert.Nil(t, err) assert.True(t, wasCalled) }) } +func TestClient_CheckRequiredBalance(t *testing.T) { + t.Parallel() + args := createMockEthereumClientArgs() + + tokenErc20 := common.BytesToAddress([]byte("ERC20token1")) + balance := big.NewInt(1000000) + t.Run("not enough erc20 balance", func(t *testing.T) { + c, _ := NewEthereumClient(args) + c.erc20ContractsHandler = &bridgeTests.ERC20ContractsHolderStub{ + BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { + assert.Equal(t, c.safeContractAddress, address) + + return balance, nil + }, + } + err := c.CheckRequiredBalance(context.Background(), tokenErc20, big.NewInt(0).Add(balance, big.NewInt(1))) + assert.True(t, errors.Is(err, errInsufficientErc20Balance)) + }) + t.Run("erc20 balance of errors", func(t *testing.T) { + expectedErr := errors.New("expected error erc20 balance of") + c, _ := NewEthereumClient(args) + c.erc20ContractsHandler = &bridgeTests.ERC20ContractsHolderStub{ + BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { + return nil, expectedErr + }, + } + + err := c.CheckRequiredBalance(context.Background(), tokenErc20, balance) + assert.True(t, errors.Is(err, expectedErr)) + }) + t.Run("should work", func(t *testing.T) { + c, _ := NewEthereumClient(args) + c.erc20ContractsHandler = &bridgeTests.ERC20ContractsHolderStub{ + BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { + assert.Equal(t, c.safeContractAddress, address) + + return balance, nil + }, + } + err := c.CheckRequiredBalance(context.Background(), tokenErc20, balance) + assert.Nil(t, err) + }) +} + +func TestClient_TotalBalances(t *testing.T) { + t.Parallel() + + t.Run("error while getting total balances", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + TotalBalancesCalled: func(ctx context.Context, token common.Address) (*big.Int, error) { + return nil, expectedErr + }, + } + c, _ := NewEthereumClient(args) + + balances, err := c.TotalBalances(context.Background(), common.Address{}) + assert.Nil(t, balances) + assert.True(t, errors.Is(err, expectedErr)) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + providedBalance := big.NewInt(100) + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + TotalBalancesCalled: func(ctx context.Context, token common.Address) (*big.Int, error) { + return providedBalance, nil + }, + } + c, _ := NewEthereumClient(args) + + balances, err := c.TotalBalances(context.Background(), common.Address{}) + assert.Nil(t, err) + assert.Equal(t, providedBalance, balances) + }) +} + +func TestClient_MintBalances(t *testing.T) { + t.Parallel() + + t.Run("error while getting mint balances", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + MintBalancesCalled: func(ctx context.Context, token common.Address) (*big.Int, error) { + return nil, expectedErr + }, + } + c, _ := NewEthereumClient(args) + + balances, err := c.MintBalances(context.Background(), common.Address{}) + assert.Nil(t, balances) + assert.True(t, errors.Is(err, expectedErr)) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + providedBalance := big.NewInt(100) + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + MintBalancesCalled: func(ctx context.Context, token common.Address) (*big.Int, error) { + return providedBalance, nil + }, + } + c, _ := NewEthereumClient(args) + + balances, err := c.MintBalances(context.Background(), common.Address{}) + assert.Nil(t, err) + assert.Equal(t, providedBalance, balances) + }) +} + +func TestClient_BurnBalances(t *testing.T) { + t.Parallel() + + t.Run("error while getting burn balances", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + BurnBalancesCalled: func(ctx context.Context, token common.Address) (*big.Int, error) { + return nil, expectedErr + }, + } + c, _ := NewEthereumClient(args) + + balances, err := c.BurnBalances(context.Background(), common.Address{}) + assert.Nil(t, balances) + assert.True(t, errors.Is(err, expectedErr)) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + providedBalance := big.NewInt(100) + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + BurnBalancesCalled: func(ctx context.Context, token common.Address) (*big.Int, error) { + return providedBalance, nil + }, + } + c, _ := NewEthereumClient(args) + + balances, err := c.BurnBalances(context.Background(), common.Address{}) + assert.Nil(t, err) + assert.Equal(t, providedBalance, balances) + }) +} + +func TestClient_MintBurnTokens(t *testing.T) { + t.Parallel() + + t.Run("error while getting mint burn tokens", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + MintBurnTokensCalled: func(ctx context.Context, token common.Address) (bool, error) { + return false, expectedErr + }, + } + c, _ := NewEthereumClient(args) + + isMintBurn, err := c.MintBurnTokens(context.Background(), common.Address{}) + assert.False(t, isMintBurn) + assert.True(t, errors.Is(err, expectedErr)) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + MintBurnTokensCalled: func(ctx context.Context, token common.Address) (bool, error) { + return true, nil + }, + } + c, _ := NewEthereumClient(args) + + isMintBurn, err := c.MintBurnTokens(context.Background(), common.Address{}) + assert.Nil(t, err) + assert.True(t, isMintBurn) + }) +} + +func TestClient_NativeTokens(t *testing.T) { + t.Parallel() + + t.Run("error while getting native tokens", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + NativeTokensCalled: func(ctx context.Context, token common.Address) (bool, error) { + return false, expectedErr + }, + } + c, _ := NewEthereumClient(args) + + isNative, err := c.NativeTokens(context.Background(), common.Address{}) + assert.False(t, isNative) + assert.True(t, errors.Is(err, expectedErr)) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + NativeTokensCalled: func(ctx context.Context, token common.Address) (bool, error) { + return true, nil + }, + } + c, _ := NewEthereumClient(args) + + isNative, err := c.NativeTokens(context.Background(), common.Address{}) + assert.Nil(t, err) + assert.True(t, isNative) + }) +} + func TestClient_GetTransactionsStatuses(t *testing.T) { t.Parallel() expectedStatuses := []byte{1, 2, 3} expectedBatchID := big.NewInt(2232) - args := createMockEthereumClientArgs() - args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ - GetStatusesAfterExecutionCalled: func(ctx context.Context, batchID *big.Int) ([]byte, error) { - assert.Equal(t, expectedBatchID, batchID) - return expectedStatuses, nil - }, - } + expectedErr := errors.New("expected error") + t.Run("operation error, should error", func(t *testing.T) { + t.Parallel() - c, _ := NewEthereumClient(args) + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + GetStatusesAfterExecutionCalled: func(ctx context.Context, batchID *big.Int) ([]byte, bool, error) { + assert.Equal(t, expectedBatchID, batchID) + return nil, false, expectedErr + }, + } - statuses, err := c.GetTransactionsStatuses(context.Background(), expectedBatchID.Uint64()) - assert.Nil(t, err) - assert.Equal(t, expectedStatuses, statuses) + c, _ := NewEthereumClient(args) + statuses, err := c.GetTransactionsStatuses(context.Background(), expectedBatchID.Uint64()) + assert.Nil(t, statuses) + assert.Equal(t, expectedErr, err) + }) + t.Run("statuses are not final, should error", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + GetStatusesAfterExecutionCalled: func(ctx context.Context, batchID *big.Int) ([]byte, bool, error) { + assert.Equal(t, expectedBatchID, batchID) + return []byte("dummy"), false, nil + }, + } + + c, _ := NewEthereumClient(args) + statuses, err := c.GetTransactionsStatuses(context.Background(), expectedBatchID.Uint64()) + assert.Nil(t, statuses) + assert.Equal(t, errStatusIsNotFinal, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + GetStatusesAfterExecutionCalled: func(ctx context.Context, batchID *big.Int) ([]byte, bool, error) { + assert.Equal(t, expectedBatchID, batchID) + return expectedStatuses, true, nil + }, + } + + c, _ := NewEthereumClient(args) + statuses, err := c.GetTransactionsStatuses(context.Background(), expectedBatchID.Uint64()) + assert.Equal(t, expectedStatuses, statuses) + assert.Nil(t, err) + }) } func TestClient_GetQuorumSize(t *testing.T) { @@ -835,7 +1188,7 @@ func TestClient_CheckClientAvailability(t *testing.T) { for i := 0; i < 10; i++ { err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") } assert.True(t, statusHandler.GetIntMetric(bridgeCore.MetricLastBlockNonce) > 0) }) @@ -846,21 +1199,21 @@ func TestClient_CheckClientAvailability(t *testing.T) { incrementor = 0 // place a random message as to test it is reset - statusHandler.SetStringMetric(bridgeCore.MetricMultiversXClientStatus, ethmultiversx.ClientStatus(3).String()) + statusHandler.SetStringMetric(bridgeCore.MetricMultiversXClientStatus, bridgeCore.ClientStatus(3).String()) statusHandler.SetStringMetric(bridgeCore.MetricLastMultiversXClientError, "random") // this will just increment the retry counter - for i := 0; i < int(args.AllowDelta); i++ { + for i := 0; i < int(args.ClientAvailabilityAllowDelta); i++ { err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") } for i := 0; i < 10; i++ { - message := fmt.Sprintf("block %d fetched for %d times in a row", currentNonce, args.AllowDelta+uint64(i+1)) + message := fmt.Sprintf("block %d fetched for %d times in a row", currentNonce, args.ClientAvailabilityAllowDelta+uint64(i+1)) err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Unavailable, message) + checkStatusHandler(t, statusHandler, bridgeCore.Unavailable, message) } }) t.Run("same current nonce should error after a while and then recovers", func(t *testing.T) { @@ -870,23 +1223,23 @@ func TestClient_CheckClientAvailability(t *testing.T) { incrementor = 0 // this will just increment the retry counter - for i := 0; i < int(args.AllowDelta); i++ { + for i := 0; i < int(args.ClientAvailabilityAllowDelta); i++ { err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") } for i := 0; i < 10; i++ { - message := fmt.Sprintf("block %d fetched for %d times in a row", currentNonce, args.AllowDelta+uint64(i+1)) + message := fmt.Sprintf("block %d fetched for %d times in a row", currentNonce, args.ClientAvailabilityAllowDelta+uint64(i+1)) err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Unavailable, message) + checkStatusHandler(t, statusHandler, bridgeCore.Unavailable, message) } incrementor = 1 err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") }) t.Run("get current nonce errors", func(t *testing.T) { resetClient(c) @@ -898,9 +1251,61 @@ func TestClient_CheckClientAvailability(t *testing.T) { } err := c.CheckClientAvailability(context.Background()) - checkStatusHandler(t, statusHandler, ethmultiversx.Unavailable, expectedErr.Error()) + checkStatusHandler(t, statusHandler, bridgeCore.Unavailable, expectedErr.Error()) + assert.Equal(t, expectedErr, err) + }) +} + +func TestClient_GetBatchSCMetadata(t *testing.T) { + t.Parallel() + + t.Run("returns error on filter logs error", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("filter logs err") + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return nil, expectedErr + }, + } + c, _ := NewEthereumClient(args) + batch, err := c.GetBatchSCMetadata(context.Background(), 0, 0) + assert.Nil(t, batch) assert.Equal(t, expectedErr, err) }) + + t.Run("returns expected logs", func(t *testing.T) { + scExecAbi, _ := contract.ERC20SafeMetaData.GetAbi() + expectedEvent := &contract.ERC20SafeERC20SCDeposit{ + BatchId: big.NewInt(1), + DepositNonce: big.NewInt(2), + CallData: []byte("call_data_to_unpack"), + } + + eventInputs := scExecAbi.Events["ERC20SCDeposit"].Inputs.NonIndexed() + packedArgs, err := eventInputs.Pack(expectedEvent.DepositNonce, expectedEvent.CallData) + require.Nil(t, err) + + args := createMockEthereumClientArgs() + args.ClientWrapper = &bridgeTests.EthereumClientWrapperStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return []types.Log{ + { + Data: packedArgs, + }, + }, nil + }, + } + c, _ := NewEthereumClient(args) + batch, err := c.GetBatchSCMetadata(context.Background(), expectedEvent.BatchId.Uint64(), 500) + + assert.Nil(t, err) + assert.Equal(t, 1, len(batch)) + assert.Equal(t, expectedEvent.BatchId, batch[0].BatchId) + assert.Equal(t, expectedEvent.DepositNonce, batch[0].DepositNonce) + assert.Equal(t, expectedEvent.CallData, batch[0].CallData) + }) } func resetClient(c *client) { @@ -911,7 +1316,7 @@ func resetClient(c *client) { c.clientWrapper.SetStringMetric(bridgeCore.MetricLastMultiversXClientError, "") } -func checkStatusHandler(t *testing.T, statusHandler *testsCommon.StatusHandlerMock, status ethmultiversx.ClientStatus, message string) { +func checkStatusHandler(t *testing.T, statusHandler *testsCommon.StatusHandlerMock, status bridgeCore.ClientStatus, message string) { assert.Equal(t, status.String(), statusHandler.GetStringMetric(bridgeCore.MetricMultiversXClientStatus)) assert.Equal(t, message, statusHandler.GetStringMetric(bridgeCore.MetricLastMultiversXClientError)) } diff --git a/clients/ethereum/contract/Bridge.go b/clients/ethereum/contract/Bridge.go new file mode 100755 index 00000000..ed1a6ddb --- /dev/null +++ b/clients/ethereum/contract/Bridge.go @@ -0,0 +1,1728 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// Batch is an auto generated low-level Go binding around an user-defined struct. +type Batch struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +} + +// Deposit is an auto generated low-level Go binding around an user-defined struct. +type Deposit struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +} + +// BridgeMetaData contains all meta data concerning the Bridge contract. +var BridgeMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminRoleTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isPause\",\"type\":\"bool\"}],\"name\":\"Pause\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"quorum\",\"type\":\"uint256\"}],\"name\":\"QuorumChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerRemoved\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"addRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleBlockCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"crossTransferStatuses\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"createdBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"depositNonces\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"batchNonceMvx\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"executeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"executedBatches\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"bool\",\"name\":\"isBatchFinal\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatchDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"areDepositsFinal\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRelayer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayersCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceMvx\",\"type\":\"uint256\"}],\"name\":\"getStatusesAfterExecution\",\"outputs\":[{\"internalType\":\"enumDepositStatus[]\",\"name\":\"\",\"type\":\"uint8[]\"},{\"internalType\":\"bool\",\"name\":\"isFinal\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"board\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"initialQuorum\",\"type\":\"uint256\"},{\"internalType\":\"contractERC20Safe\",\"name\":\"erc20Safe\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isRelayer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"removeRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchSettleLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchSettleLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorum\",\"type\":\"uint256\"}],\"name\":\"setQuorum\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceMvx\",\"type\":\"uint256\"}],\"name\":\"wasBatchExecuted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// BridgeABI is the input ABI used to generate the binding from. +// Deprecated: Use BridgeMetaData.ABI instead. +var BridgeABI = BridgeMetaData.ABI + +// Bridge is an auto generated Go binding around an Ethereum contract. +type Bridge struct { + BridgeCaller // Read-only binding to the contract + BridgeTransactor // Write-only binding to the contract + BridgeFilterer // Log filterer for contract events +} + +// BridgeCaller is an auto generated read-only Go binding around an Ethereum contract. +type BridgeCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BridgeTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BridgeTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BridgeFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BridgeFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BridgeSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BridgeSession struct { + Contract *Bridge // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BridgeCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BridgeCallerSession struct { + Contract *BridgeCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BridgeTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BridgeTransactorSession struct { + Contract *BridgeTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BridgeRaw is an auto generated low-level Go binding around an Ethereum contract. +type BridgeRaw struct { + Contract *Bridge // Generic contract binding to access the raw methods on +} + +// BridgeCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BridgeCallerRaw struct { + Contract *BridgeCaller // Generic read-only contract binding to access the raw methods on +} + +// BridgeTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BridgeTransactorRaw struct { + Contract *BridgeTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBridge creates a new instance of Bridge, bound to a specific deployed contract. +func NewBridge(address common.Address, backend bind.ContractBackend) (*Bridge, error) { + contract, err := bindBridge(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Bridge{BridgeCaller: BridgeCaller{contract: contract}, BridgeTransactor: BridgeTransactor{contract: contract}, BridgeFilterer: BridgeFilterer{contract: contract}}, nil +} + +// NewBridgeCaller creates a new read-only instance of Bridge, bound to a specific deployed contract. +func NewBridgeCaller(address common.Address, caller bind.ContractCaller) (*BridgeCaller, error) { + contract, err := bindBridge(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BridgeCaller{contract: contract}, nil +} + +// NewBridgeTransactor creates a new write-only instance of Bridge, bound to a specific deployed contract. +func NewBridgeTransactor(address common.Address, transactor bind.ContractTransactor) (*BridgeTransactor, error) { + contract, err := bindBridge(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BridgeTransactor{contract: contract}, nil +} + +// NewBridgeFilterer creates a new log filterer instance of Bridge, bound to a specific deployed contract. +func NewBridgeFilterer(address common.Address, filterer bind.ContractFilterer) (*BridgeFilterer, error) { + contract, err := bindBridge(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BridgeFilterer{contract: contract}, nil +} + +// bindBridge binds a generic wrapper to an already deployed contract. +func bindBridge(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := BridgeMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Bridge *BridgeRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Bridge.Contract.BridgeCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Bridge *BridgeRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.Contract.BridgeTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Bridge *BridgeRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Bridge.Contract.BridgeTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Bridge *BridgeCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Bridge.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Bridge *BridgeTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Bridge *BridgeTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Bridge.Contract.contract.Transact(opts, method, params...) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_Bridge *BridgeCaller) Admin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "admin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_Bridge *BridgeSession) Admin() (common.Address, error) { + return _Bridge.Contract.Admin(&_Bridge.CallOpts) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_Bridge *BridgeCallerSession) Admin() (common.Address, error) { + return _Bridge.Contract.Admin(&_Bridge.CallOpts) +} + +// BatchSettleBlockCount is a free data retrieval call binding the contract method 0x4ab3867f. +// +// Solidity: function batchSettleBlockCount() view returns(uint256) +func (_Bridge *BridgeCaller) BatchSettleBlockCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "batchSettleBlockCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BatchSettleBlockCount is a free data retrieval call binding the contract method 0x4ab3867f. +// +// Solidity: function batchSettleBlockCount() view returns(uint256) +func (_Bridge *BridgeSession) BatchSettleBlockCount() (*big.Int, error) { + return _Bridge.Contract.BatchSettleBlockCount(&_Bridge.CallOpts) +} + +// BatchSettleBlockCount is a free data retrieval call binding the contract method 0x4ab3867f. +// +// Solidity: function batchSettleBlockCount() view returns(uint256) +func (_Bridge *BridgeCallerSession) BatchSettleBlockCount() (*big.Int, error) { + return _Bridge.Contract.BatchSettleBlockCount(&_Bridge.CallOpts) +} + +// CrossTransferStatuses is a free data retrieval call binding the contract method 0xb2c79ca3. +// +// Solidity: function crossTransferStatuses(uint256 ) view returns(uint256 createdBlockNumber) +func (_Bridge *BridgeCaller) CrossTransferStatuses(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "crossTransferStatuses", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// CrossTransferStatuses is a free data retrieval call binding the contract method 0xb2c79ca3. +// +// Solidity: function crossTransferStatuses(uint256 ) view returns(uint256 createdBlockNumber) +func (_Bridge *BridgeSession) CrossTransferStatuses(arg0 *big.Int) (*big.Int, error) { + return _Bridge.Contract.CrossTransferStatuses(&_Bridge.CallOpts, arg0) +} + +// CrossTransferStatuses is a free data retrieval call binding the contract method 0xb2c79ca3. +// +// Solidity: function crossTransferStatuses(uint256 ) view returns(uint256 createdBlockNumber) +func (_Bridge *BridgeCallerSession) CrossTransferStatuses(arg0 *big.Int) (*big.Int, error) { + return _Bridge.Contract.CrossTransferStatuses(&_Bridge.CallOpts, arg0) +} + +// ExecutedBatches is a free data retrieval call binding the contract method 0x7039e21b. +// +// Solidity: function executedBatches(uint256 ) view returns(bool) +func (_Bridge *BridgeCaller) ExecutedBatches(opts *bind.CallOpts, arg0 *big.Int) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "executedBatches", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// ExecutedBatches is a free data retrieval call binding the contract method 0x7039e21b. +// +// Solidity: function executedBatches(uint256 ) view returns(bool) +func (_Bridge *BridgeSession) ExecutedBatches(arg0 *big.Int) (bool, error) { + return _Bridge.Contract.ExecutedBatches(&_Bridge.CallOpts, arg0) +} + +// ExecutedBatches is a free data retrieval call binding the contract method 0x7039e21b. +// +// Solidity: function executedBatches(uint256 ) view returns(bool) +func (_Bridge *BridgeCallerSession) ExecutedBatches(arg0 *big.Int) (bool, error) { + return _Bridge.Contract.ExecutedBatches(&_Bridge.CallOpts, arg0) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16), bool isBatchFinal) +func (_Bridge *BridgeCaller) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (Batch, bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getBatch", batchNonce) + + if err != nil { + return *new(Batch), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(Batch)).(*Batch) + out1 := *abi.ConvertType(out[1], new(bool)).(*bool) + + return out0, out1, err + +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16), bool isBatchFinal) +func (_Bridge *BridgeSession) GetBatch(batchNonce *big.Int) (Batch, bool, error) { + return _Bridge.Contract.GetBatch(&_Bridge.CallOpts, batchNonce) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16), bool isBatchFinal) +func (_Bridge *BridgeCallerSession) GetBatch(batchNonce *big.Int) (Batch, bool, error) { + return _Bridge.Contract.GetBatch(&_Bridge.CallOpts, batchNonce) +} + +// GetBatchDeposits is a free data retrieval call binding the contract method 0x90924da7. +// +// Solidity: function getBatchDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[], bool areDepositsFinal) +func (_Bridge *BridgeCaller) GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]Deposit, bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getBatchDeposits", batchNonce) + + if err != nil { + return *new([]Deposit), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new([]Deposit)).(*[]Deposit) + out1 := *abi.ConvertType(out[1], new(bool)).(*bool) + + return out0, out1, err + +} + +// GetBatchDeposits is a free data retrieval call binding the contract method 0x90924da7. +// +// Solidity: function getBatchDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[], bool areDepositsFinal) +func (_Bridge *BridgeSession) GetBatchDeposits(batchNonce *big.Int) ([]Deposit, bool, error) { + return _Bridge.Contract.GetBatchDeposits(&_Bridge.CallOpts, batchNonce) +} + +// GetBatchDeposits is a free data retrieval call binding the contract method 0x90924da7. +// +// Solidity: function getBatchDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[], bool areDepositsFinal) +func (_Bridge *BridgeCallerSession) GetBatchDeposits(batchNonce *big.Int) ([]Deposit, bool, error) { + return _Bridge.Contract.GetBatchDeposits(&_Bridge.CallOpts, batchNonce) +} + +// GetRelayer is a free data retrieval call binding the contract method 0xbee2e4dd. +// +// Solidity: function getRelayer(uint256 index) view returns(address) +func (_Bridge *BridgeCaller) GetRelayer(opts *bind.CallOpts, index *big.Int) (common.Address, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getRelayer", index) + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetRelayer is a free data retrieval call binding the contract method 0xbee2e4dd. +// +// Solidity: function getRelayer(uint256 index) view returns(address) +func (_Bridge *BridgeSession) GetRelayer(index *big.Int) (common.Address, error) { + return _Bridge.Contract.GetRelayer(&_Bridge.CallOpts, index) +} + +// GetRelayer is a free data retrieval call binding the contract method 0xbee2e4dd. +// +// Solidity: function getRelayer(uint256 index) view returns(address) +func (_Bridge *BridgeCallerSession) GetRelayer(index *big.Int) (common.Address, error) { + return _Bridge.Contract.GetRelayer(&_Bridge.CallOpts, index) +} + +// GetRelayers is a free data retrieval call binding the contract method 0x179ff4b2. +// +// Solidity: function getRelayers() view returns(address[]) +func (_Bridge *BridgeCaller) GetRelayers(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getRelayers") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +// GetRelayers is a free data retrieval call binding the contract method 0x179ff4b2. +// +// Solidity: function getRelayers() view returns(address[]) +func (_Bridge *BridgeSession) GetRelayers() ([]common.Address, error) { + return _Bridge.Contract.GetRelayers(&_Bridge.CallOpts) +} + +// GetRelayers is a free data retrieval call binding the contract method 0x179ff4b2. +// +// Solidity: function getRelayers() view returns(address[]) +func (_Bridge *BridgeCallerSession) GetRelayers() ([]common.Address, error) { + return _Bridge.Contract.GetRelayers(&_Bridge.CallOpts) +} + +// GetRelayersCount is a free data retrieval call binding the contract method 0xd3d9ec01. +// +// Solidity: function getRelayersCount() view returns(uint256) +func (_Bridge *BridgeCaller) GetRelayersCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getRelayersCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetRelayersCount is a free data retrieval call binding the contract method 0xd3d9ec01. +// +// Solidity: function getRelayersCount() view returns(uint256) +func (_Bridge *BridgeSession) GetRelayersCount() (*big.Int, error) { + return _Bridge.Contract.GetRelayersCount(&_Bridge.CallOpts) +} + +// GetRelayersCount is a free data retrieval call binding the contract method 0xd3d9ec01. +// +// Solidity: function getRelayersCount() view returns(uint256) +func (_Bridge *BridgeCallerSession) GetRelayersCount() (*big.Int, error) { + return _Bridge.Contract.GetRelayersCount(&_Bridge.CallOpts) +} + +// GetStatusesAfterExecution is a free data retrieval call binding the contract method 0xdb626c2d. +// +// Solidity: function getStatusesAfterExecution(uint256 batchNonceMvx) view returns(uint8[], bool isFinal) +func (_Bridge *BridgeCaller) GetStatusesAfterExecution(opts *bind.CallOpts, batchNonceMvx *big.Int) ([]uint8, bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "getStatusesAfterExecution", batchNonceMvx) + + if err != nil { + return *new([]uint8), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new([]uint8)).(*[]uint8) + out1 := *abi.ConvertType(out[1], new(bool)).(*bool) + + return out0, out1, err + +} + +// GetStatusesAfterExecution is a free data retrieval call binding the contract method 0xdb626c2d. +// +// Solidity: function getStatusesAfterExecution(uint256 batchNonceMvx) view returns(uint8[], bool isFinal) +func (_Bridge *BridgeSession) GetStatusesAfterExecution(batchNonceMvx *big.Int) ([]uint8, bool, error) { + return _Bridge.Contract.GetStatusesAfterExecution(&_Bridge.CallOpts, batchNonceMvx) +} + +// GetStatusesAfterExecution is a free data retrieval call binding the contract method 0xdb626c2d. +// +// Solidity: function getStatusesAfterExecution(uint256 batchNonceMvx) view returns(uint8[], bool isFinal) +func (_Bridge *BridgeCallerSession) GetStatusesAfterExecution(batchNonceMvx *big.Int) ([]uint8, bool, error) { + return _Bridge.Contract.GetStatusesAfterExecution(&_Bridge.CallOpts, batchNonceMvx) +} + +// IsRelayer is a free data retrieval call binding the contract method 0x541d5548. +// +// Solidity: function isRelayer(address account) view returns(bool) +func (_Bridge *BridgeCaller) IsRelayer(opts *bind.CallOpts, account common.Address) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "isRelayer", account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsRelayer is a free data retrieval call binding the contract method 0x541d5548. +// +// Solidity: function isRelayer(address account) view returns(bool) +func (_Bridge *BridgeSession) IsRelayer(account common.Address) (bool, error) { + return _Bridge.Contract.IsRelayer(&_Bridge.CallOpts, account) +} + +// IsRelayer is a free data retrieval call binding the contract method 0x541d5548. +// +// Solidity: function isRelayer(address account) view returns(bool) +func (_Bridge *BridgeCallerSession) IsRelayer(account common.Address) (bool, error) { + return _Bridge.Contract.IsRelayer(&_Bridge.CallOpts, account) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_Bridge *BridgeCaller) Paused(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "paused") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_Bridge *BridgeSession) Paused() (bool, error) { + return _Bridge.Contract.Paused(&_Bridge.CallOpts) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_Bridge *BridgeCallerSession) Paused() (bool, error) { + return _Bridge.Contract.Paused(&_Bridge.CallOpts) +} + +// Quorum is a free data retrieval call binding the contract method 0x1703a018. +// +// Solidity: function quorum() view returns(uint256) +func (_Bridge *BridgeCaller) Quorum(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "quorum") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Quorum is a free data retrieval call binding the contract method 0x1703a018. +// +// Solidity: function quorum() view returns(uint256) +func (_Bridge *BridgeSession) Quorum() (*big.Int, error) { + return _Bridge.Contract.Quorum(&_Bridge.CallOpts) +} + +// Quorum is a free data retrieval call binding the contract method 0x1703a018. +// +// Solidity: function quorum() view returns(uint256) +func (_Bridge *BridgeCallerSession) Quorum() (*big.Int, error) { + return _Bridge.Contract.Quorum(&_Bridge.CallOpts) +} + +// WasBatchExecuted is a free data retrieval call binding the contract method 0x84aa1ad0. +// +// Solidity: function wasBatchExecuted(uint256 batchNonceMvx) view returns(bool) +func (_Bridge *BridgeCaller) WasBatchExecuted(opts *bind.CallOpts, batchNonceMvx *big.Int) (bool, error) { + var out []interface{} + err := _Bridge.contract.Call(opts, &out, "wasBatchExecuted", batchNonceMvx) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// WasBatchExecuted is a free data retrieval call binding the contract method 0x84aa1ad0. +// +// Solidity: function wasBatchExecuted(uint256 batchNonceMvx) view returns(bool) +func (_Bridge *BridgeSession) WasBatchExecuted(batchNonceMvx *big.Int) (bool, error) { + return _Bridge.Contract.WasBatchExecuted(&_Bridge.CallOpts, batchNonceMvx) +} + +// WasBatchExecuted is a free data retrieval call binding the contract method 0x84aa1ad0. +// +// Solidity: function wasBatchExecuted(uint256 batchNonceMvx) view returns(bool) +func (_Bridge *BridgeCallerSession) WasBatchExecuted(batchNonceMvx *big.Int) (bool, error) { + return _Bridge.Contract.WasBatchExecuted(&_Bridge.CallOpts, batchNonceMvx) +} + +// AddRelayer is a paid mutator transaction binding the contract method 0xdd39f00d. +// +// Solidity: function addRelayer(address account) returns() +func (_Bridge *BridgeTransactor) AddRelayer(opts *bind.TransactOpts, account common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "addRelayer", account) +} + +// AddRelayer is a paid mutator transaction binding the contract method 0xdd39f00d. +// +// Solidity: function addRelayer(address account) returns() +func (_Bridge *BridgeSession) AddRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.AddRelayer(&_Bridge.TransactOpts, account) +} + +// AddRelayer is a paid mutator transaction binding the contract method 0xdd39f00d. +// +// Solidity: function addRelayer(address account) returns() +func (_Bridge *BridgeTransactorSession) AddRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.AddRelayer(&_Bridge.TransactOpts, account) +} + +// ExecuteTransfer is a paid mutator transaction binding the contract method 0x51db0518. +// +// Solidity: function executeTransfer(address[] tokens, address[] recipients, uint256[] amounts, uint256[] depositNonces, uint256 batchNonceMvx, bytes[] signatures) returns() +func (_Bridge *BridgeTransactor) ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonceMvx *big.Int, signatures [][]byte) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "executeTransfer", tokens, recipients, amounts, depositNonces, batchNonceMvx, signatures) +} + +// ExecuteTransfer is a paid mutator transaction binding the contract method 0x51db0518. +// +// Solidity: function executeTransfer(address[] tokens, address[] recipients, uint256[] amounts, uint256[] depositNonces, uint256 batchNonceMvx, bytes[] signatures) returns() +func (_Bridge *BridgeSession) ExecuteTransfer(tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonceMvx *big.Int, signatures [][]byte) (*types.Transaction, error) { + return _Bridge.Contract.ExecuteTransfer(&_Bridge.TransactOpts, tokens, recipients, amounts, depositNonces, batchNonceMvx, signatures) +} + +// ExecuteTransfer is a paid mutator transaction binding the contract method 0x51db0518. +// +// Solidity: function executeTransfer(address[] tokens, address[] recipients, uint256[] amounts, uint256[] depositNonces, uint256 batchNonceMvx, bytes[] signatures) returns() +func (_Bridge *BridgeTransactorSession) ExecuteTransfer(tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonceMvx *big.Int, signatures [][]byte) (*types.Transaction, error) { + return _Bridge.Contract.ExecuteTransfer(&_Bridge.TransactOpts, tokens, recipients, amounts, depositNonces, batchNonceMvx, signatures) +} + +// Initialize is a paid mutator transaction binding the contract method 0x72483bf9. +// +// Solidity: function initialize(address[] board, uint256 initialQuorum, address erc20Safe) returns() +func (_Bridge *BridgeTransactor) Initialize(opts *bind.TransactOpts, board []common.Address, initialQuorum *big.Int, erc20Safe common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "initialize", board, initialQuorum, erc20Safe) +} + +// Initialize is a paid mutator transaction binding the contract method 0x72483bf9. +// +// Solidity: function initialize(address[] board, uint256 initialQuorum, address erc20Safe) returns() +func (_Bridge *BridgeSession) Initialize(board []common.Address, initialQuorum *big.Int, erc20Safe common.Address) (*types.Transaction, error) { + return _Bridge.Contract.Initialize(&_Bridge.TransactOpts, board, initialQuorum, erc20Safe) +} + +// Initialize is a paid mutator transaction binding the contract method 0x72483bf9. +// +// Solidity: function initialize(address[] board, uint256 initialQuorum, address erc20Safe) returns() +func (_Bridge *BridgeTransactorSession) Initialize(board []common.Address, initialQuorum *big.Int, erc20Safe common.Address) (*types.Transaction, error) { + return _Bridge.Contract.Initialize(&_Bridge.TransactOpts, board, initialQuorum, erc20Safe) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_Bridge *BridgeTransactor) Pause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "pause") +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_Bridge *BridgeSession) Pause() (*types.Transaction, error) { + return _Bridge.Contract.Pause(&_Bridge.TransactOpts) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_Bridge *BridgeTransactorSession) Pause() (*types.Transaction, error) { + return _Bridge.Contract.Pause(&_Bridge.TransactOpts) +} + +// RemoveRelayer is a paid mutator transaction binding the contract method 0x60f0a5ac. +// +// Solidity: function removeRelayer(address account) returns() +func (_Bridge *BridgeTransactor) RemoveRelayer(opts *bind.TransactOpts, account common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "removeRelayer", account) +} + +// RemoveRelayer is a paid mutator transaction binding the contract method 0x60f0a5ac. +// +// Solidity: function removeRelayer(address account) returns() +func (_Bridge *BridgeSession) RemoveRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RemoveRelayer(&_Bridge.TransactOpts, account) +} + +// RemoveRelayer is a paid mutator transaction binding the contract method 0x60f0a5ac. +// +// Solidity: function removeRelayer(address account) returns() +func (_Bridge *BridgeTransactorSession) RemoveRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RemoveRelayer(&_Bridge.TransactOpts, account) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_Bridge *BridgeTransactor) RenounceAdmin(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "renounceAdmin") +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_Bridge *BridgeSession) RenounceAdmin() (*types.Transaction, error) { + return _Bridge.Contract.RenounceAdmin(&_Bridge.TransactOpts) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_Bridge *BridgeTransactorSession) RenounceAdmin() (*types.Transaction, error) { + return _Bridge.Contract.RenounceAdmin(&_Bridge.TransactOpts) +} + +// RenounceRelayer is a paid mutator transaction binding the contract method 0x475ed4d0. +// +// Solidity: function renounceRelayer(address account) returns() +func (_Bridge *BridgeTransactor) RenounceRelayer(opts *bind.TransactOpts, account common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "renounceRelayer", account) +} + +// RenounceRelayer is a paid mutator transaction binding the contract method 0x475ed4d0. +// +// Solidity: function renounceRelayer(address account) returns() +func (_Bridge *BridgeSession) RenounceRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RenounceRelayer(&_Bridge.TransactOpts, account) +} + +// RenounceRelayer is a paid mutator transaction binding the contract method 0x475ed4d0. +// +// Solidity: function renounceRelayer(address account) returns() +func (_Bridge *BridgeTransactorSession) RenounceRelayer(account common.Address) (*types.Transaction, error) { + return _Bridge.Contract.RenounceRelayer(&_Bridge.TransactOpts, account) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeTransactor) SetBatchSettleLimit(opts *bind.TransactOpts, newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "setBatchSettleLimit", newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.Contract.SetBatchSettleLimit(&_Bridge.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeTransactorSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.Contract.SetBatchSettleLimit(&_Bridge.TransactOpts, newBatchSettleLimit) +} + +// SetQuorum is a paid mutator transaction binding the contract method 0xc1ba4e59. +// +// Solidity: function setQuorum(uint256 newQuorum) returns() +func (_Bridge *BridgeTransactor) SetQuorum(opts *bind.TransactOpts, newQuorum *big.Int) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "setQuorum", newQuorum) +} + +// SetQuorum is a paid mutator transaction binding the contract method 0xc1ba4e59. +// +// Solidity: function setQuorum(uint256 newQuorum) returns() +func (_Bridge *BridgeSession) SetQuorum(newQuorum *big.Int) (*types.Transaction, error) { + return _Bridge.Contract.SetQuorum(&_Bridge.TransactOpts, newQuorum) +} + +// SetQuorum is a paid mutator transaction binding the contract method 0xc1ba4e59. +// +// Solidity: function setQuorum(uint256 newQuorum) returns() +func (_Bridge *BridgeTransactorSession) SetQuorum(newQuorum *big.Int) (*types.Transaction, error) { + return _Bridge.Contract.SetQuorum(&_Bridge.TransactOpts, newQuorum) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_Bridge *BridgeTransactor) TransferAdmin(opts *bind.TransactOpts, newAdmin common.Address) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "transferAdmin", newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_Bridge *BridgeSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _Bridge.Contract.TransferAdmin(&_Bridge.TransactOpts, newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_Bridge *BridgeTransactorSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _Bridge.Contract.TransferAdmin(&_Bridge.TransactOpts, newAdmin) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_Bridge *BridgeTransactor) Unpause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "unpause") +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_Bridge *BridgeSession) Unpause() (*types.Transaction, error) { + return _Bridge.Contract.Unpause(&_Bridge.TransactOpts) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_Bridge *BridgeTransactorSession) Unpause() (*types.Transaction, error) { + return _Bridge.Contract.Unpause(&_Bridge.TransactOpts) +} + +// BridgeAdminRoleTransferredIterator is returned from FilterAdminRoleTransferred and is used to iterate over the raw logs and unpacked data for AdminRoleTransferred events raised by the Bridge contract. +type BridgeAdminRoleTransferredIterator struct { + Event *BridgeAdminRoleTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BridgeAdminRoleTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BridgeAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BridgeAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BridgeAdminRoleTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeAdminRoleTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeAdminRoleTransferred represents a AdminRoleTransferred event raised by the Bridge contract. +type BridgeAdminRoleTransferred struct { + PreviousAdmin common.Address + NewAdmin common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterAdminRoleTransferred is a free log retrieval operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_Bridge *BridgeFilterer) FilterAdminRoleTransferred(opts *bind.FilterOpts, previousAdmin []common.Address, newAdmin []common.Address) (*BridgeAdminRoleTransferredIterator, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return &BridgeAdminRoleTransferredIterator{contract: _Bridge.contract, event: "AdminRoleTransferred", logs: logs, sub: sub}, nil +} + +// WatchAdminRoleTransferred is a free log subscription operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_Bridge *BridgeFilterer) WatchAdminRoleTransferred(opts *bind.WatchOpts, sink chan<- *BridgeAdminRoleTransferred, previousAdmin []common.Address, newAdmin []common.Address) (event.Subscription, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BridgeAdminRoleTransferred) + if err := _Bridge.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseAdminRoleTransferred is a log parse operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_Bridge *BridgeFilterer) ParseAdminRoleTransferred(log types.Log) (*BridgeAdminRoleTransferred, error) { + event := new(BridgeAdminRoleTransferred) + if err := _Bridge.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgeInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the Bridge contract. +type BridgeInitializedIterator struct { + Event *BridgeInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BridgeInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BridgeInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BridgeInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BridgeInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeInitialized represents a Initialized event raised by the Bridge contract. +type BridgeInitialized struct { + Version uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_Bridge *BridgeFilterer) FilterInitialized(opts *bind.FilterOpts) (*BridgeInitializedIterator, error) { + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &BridgeInitializedIterator{contract: _Bridge.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_Bridge *BridgeFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *BridgeInitialized) (event.Subscription, error) { + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BridgeInitialized) + if err := _Bridge.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_Bridge *BridgeFilterer) ParseInitialized(log types.Log) (*BridgeInitialized, error) { + event := new(BridgeInitialized) + if err := _Bridge.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgePauseIterator is returned from FilterPause and is used to iterate over the raw logs and unpacked data for Pause events raised by the Bridge contract. +type BridgePauseIterator struct { + Event *BridgePause // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BridgePauseIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BridgePause) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BridgePause) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BridgePauseIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgePauseIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgePause represents a Pause event raised by the Bridge contract. +type BridgePause struct { + IsPause bool + Raw types.Log // Blockchain specific contextual infos +} + +// FilterPause is a free log retrieval operation binding the contract event 0x9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304. +// +// Solidity: event Pause(bool isPause) +func (_Bridge *BridgeFilterer) FilterPause(opts *bind.FilterOpts) (*BridgePauseIterator, error) { + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "Pause") + if err != nil { + return nil, err + } + return &BridgePauseIterator{contract: _Bridge.contract, event: "Pause", logs: logs, sub: sub}, nil +} + +// WatchPause is a free log subscription operation binding the contract event 0x9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304. +// +// Solidity: event Pause(bool isPause) +func (_Bridge *BridgeFilterer) WatchPause(opts *bind.WatchOpts, sink chan<- *BridgePause) (event.Subscription, error) { + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "Pause") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BridgePause) + if err := _Bridge.contract.UnpackLog(event, "Pause", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParsePause is a log parse operation binding the contract event 0x9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304. +// +// Solidity: event Pause(bool isPause) +func (_Bridge *BridgeFilterer) ParsePause(log types.Log) (*BridgePause, error) { + event := new(BridgePause) + if err := _Bridge.contract.UnpackLog(event, "Pause", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgeQuorumChangedIterator is returned from FilterQuorumChanged and is used to iterate over the raw logs and unpacked data for QuorumChanged events raised by the Bridge contract. +type BridgeQuorumChangedIterator struct { + Event *BridgeQuorumChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BridgeQuorumChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BridgeQuorumChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BridgeQuorumChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BridgeQuorumChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeQuorumChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeQuorumChanged represents a QuorumChanged event raised by the Bridge contract. +type BridgeQuorumChanged struct { + Quorum *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterQuorumChanged is a free log retrieval operation binding the contract event 0x027863d12a407097e086a48e36475bfc859d0b200b7e6f65b5fd3b218e46632e. +// +// Solidity: event QuorumChanged(uint256 quorum) +func (_Bridge *BridgeFilterer) FilterQuorumChanged(opts *bind.FilterOpts) (*BridgeQuorumChangedIterator, error) { + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "QuorumChanged") + if err != nil { + return nil, err + } + return &BridgeQuorumChangedIterator{contract: _Bridge.contract, event: "QuorumChanged", logs: logs, sub: sub}, nil +} + +// WatchQuorumChanged is a free log subscription operation binding the contract event 0x027863d12a407097e086a48e36475bfc859d0b200b7e6f65b5fd3b218e46632e. +// +// Solidity: event QuorumChanged(uint256 quorum) +func (_Bridge *BridgeFilterer) WatchQuorumChanged(opts *bind.WatchOpts, sink chan<- *BridgeQuorumChanged) (event.Subscription, error) { + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "QuorumChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BridgeQuorumChanged) + if err := _Bridge.contract.UnpackLog(event, "QuorumChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseQuorumChanged is a log parse operation binding the contract event 0x027863d12a407097e086a48e36475bfc859d0b200b7e6f65b5fd3b218e46632e. +// +// Solidity: event QuorumChanged(uint256 quorum) +func (_Bridge *BridgeFilterer) ParseQuorumChanged(log types.Log) (*BridgeQuorumChanged, error) { + event := new(BridgeQuorumChanged) + if err := _Bridge.contract.UnpackLog(event, "QuorumChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgeRelayerAddedIterator is returned from FilterRelayerAdded and is used to iterate over the raw logs and unpacked data for RelayerAdded events raised by the Bridge contract. +type BridgeRelayerAddedIterator struct { + Event *BridgeRelayerAdded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BridgeRelayerAddedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BridgeRelayerAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BridgeRelayerAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BridgeRelayerAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeRelayerAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeRelayerAdded represents a RelayerAdded event raised by the Bridge contract. +type BridgeRelayerAdded struct { + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRelayerAdded is a free log retrieval operation binding the contract event 0xd756b9aee10d6f2c80dc42c5031beb0e0847f6e1d6ba50199bdfc3f0de5cc0cc. +// +// Solidity: event RelayerAdded(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) FilterRelayerAdded(opts *bind.FilterOpts, account []common.Address, sender []common.Address) (*BridgeRelayerAddedIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "RelayerAdded", accountRule, senderRule) + if err != nil { + return nil, err + } + return &BridgeRelayerAddedIterator{contract: _Bridge.contract, event: "RelayerAdded", logs: logs, sub: sub}, nil +} + +// WatchRelayerAdded is a free log subscription operation binding the contract event 0xd756b9aee10d6f2c80dc42c5031beb0e0847f6e1d6ba50199bdfc3f0de5cc0cc. +// +// Solidity: event RelayerAdded(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) WatchRelayerAdded(opts *bind.WatchOpts, sink chan<- *BridgeRelayerAdded, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "RelayerAdded", accountRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BridgeRelayerAdded) + if err := _Bridge.contract.UnpackLog(event, "RelayerAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRelayerAdded is a log parse operation binding the contract event 0xd756b9aee10d6f2c80dc42c5031beb0e0847f6e1d6ba50199bdfc3f0de5cc0cc. +// +// Solidity: event RelayerAdded(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) ParseRelayerAdded(log types.Log) (*BridgeRelayerAdded, error) { + event := new(BridgeRelayerAdded) + if err := _Bridge.contract.UnpackLog(event, "RelayerAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BridgeRelayerRemovedIterator is returned from FilterRelayerRemoved and is used to iterate over the raw logs and unpacked data for RelayerRemoved events raised by the Bridge contract. +type BridgeRelayerRemovedIterator struct { + Event *BridgeRelayerRemoved // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BridgeRelayerRemovedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BridgeRelayerRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BridgeRelayerRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BridgeRelayerRemovedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BridgeRelayerRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BridgeRelayerRemoved represents a RelayerRemoved event raised by the Bridge contract. +type BridgeRelayerRemoved struct { + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRelayerRemoved is a free log retrieval operation binding the contract event 0x0bdcf1d6f29aa87af8131cc81dcbb295fcf98d71cfcdc79cc5d965317bae1d0a. +// +// Solidity: event RelayerRemoved(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) FilterRelayerRemoved(opts *bind.FilterOpts, account []common.Address, sender []common.Address) (*BridgeRelayerRemovedIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.FilterLogs(opts, "RelayerRemoved", accountRule, senderRule) + if err != nil { + return nil, err + } + return &BridgeRelayerRemovedIterator{contract: _Bridge.contract, event: "RelayerRemoved", logs: logs, sub: sub}, nil +} + +// WatchRelayerRemoved is a free log subscription operation binding the contract event 0x0bdcf1d6f29aa87af8131cc81dcbb295fcf98d71cfcdc79cc5d965317bae1d0a. +// +// Solidity: event RelayerRemoved(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) WatchRelayerRemoved(opts *bind.WatchOpts, sink chan<- *BridgeRelayerRemoved, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _Bridge.contract.WatchLogs(opts, "RelayerRemoved", accountRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BridgeRelayerRemoved) + if err := _Bridge.contract.UnpackLog(event, "RelayerRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRelayerRemoved is a log parse operation binding the contract event 0x0bdcf1d6f29aa87af8131cc81dcbb295fcf98d71cfcdc79cc5d965317bae1d0a. +// +// Solidity: event RelayerRemoved(address indexed account, address indexed sender) +func (_Bridge *BridgeFilterer) ParseRelayerRemoved(log types.Log) (*BridgeRelayerRemoved, error) { + event := new(BridgeRelayerRemoved) + if err := _Bridge.contract.UnpackLog(event, "RelayerRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/clients/ethereum/contract/ERC20Safe.go b/clients/ethereum/contract/ERC20Safe.go new file mode 100755 index 00000000..e3777621 --- /dev/null +++ b/clients/ethereum/contract/ERC20Safe.go @@ -0,0 +1,2260 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// ERC20SafeMetaData contains all meta data concerning the ERC20Safe contract. +var ERC20SafeMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AddressInsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedInnerCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminRoleTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousBridge\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"BridgeTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"batchId\",\"type\":\"uint112\"},{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"depositNonce\",\"type\":\"uint112\"}],\"name\":\"ERC20Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint112\",\"name\":\"batchId\",\"type\":\"uint112\"},{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"depositNonce\",\"type\":\"uint112\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"ERC20SCDeposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isPause\",\"type\":\"bool\"}],\"name\":\"Pause\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchBlockLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batchDeposits\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSize\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batches\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchesCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridge\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"burnBalances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientAddress\",\"type\":\"bytes32\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientAddress\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"name\":\"depositWithSCExecution\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositsCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"bool\",\"name\":\"isBatchFinal\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"},{\"internalType\":\"bool\",\"name\":\"areDepositsFinal\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMaxLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMinLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"initSupply\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"mintAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"burnAmount\",\"type\":\"uint256\"}],\"name\":\"initSupplyMintBurn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isAnyBatchInProgress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isTokenWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"mintBalances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"mintBurnTokens\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"nativeTokens\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"recoverLostFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"removeTokenFromWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"resetTotalBalance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchBlockLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchBlockLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchSettleLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchSettleLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"newBatchSize\",\"type\":\"uint16\"}],\"name\":\"setBatchSize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"setBridge\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMaxLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMinLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMaxLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMinLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"totalBalances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minimumAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maximumAmount\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"mintBurn\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"native\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"totalBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"mintBalance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"burnBalance\",\"type\":\"uint256\"}],\"name\":\"whitelistToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"whitelistedTokens\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// ERC20SafeABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC20SafeMetaData.ABI instead. +var ERC20SafeABI = ERC20SafeMetaData.ABI + +// ERC20Safe is an auto generated Go binding around an Ethereum contract. +type ERC20Safe struct { + ERC20SafeCaller // Read-only binding to the contract + ERC20SafeTransactor // Write-only binding to the contract + ERC20SafeFilterer // Log filterer for contract events +} + +// ERC20SafeCaller is an auto generated read-only Go binding around an Ethereum contract. +type ERC20SafeCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC20SafeTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC20SafeFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC20SafeSession struct { + Contract *ERC20Safe // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20SafeCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC20SafeCallerSession struct { + Contract *ERC20SafeCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC20SafeTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC20SafeTransactorSession struct { + Contract *ERC20SafeTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20SafeRaw is an auto generated low-level Go binding around an Ethereum contract. +type ERC20SafeRaw struct { + Contract *ERC20Safe // Generic contract binding to access the raw methods on +} + +// ERC20SafeCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC20SafeCallerRaw struct { + Contract *ERC20SafeCaller // Generic read-only contract binding to access the raw methods on +} + +// ERC20SafeTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC20SafeTransactorRaw struct { + Contract *ERC20SafeTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC20Safe creates a new instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20Safe(address common.Address, backend bind.ContractBackend) (*ERC20Safe, error) { + contract, err := bindERC20Safe(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC20Safe{ERC20SafeCaller: ERC20SafeCaller{contract: contract}, ERC20SafeTransactor: ERC20SafeTransactor{contract: contract}, ERC20SafeFilterer: ERC20SafeFilterer{contract: contract}}, nil +} + +// NewERC20SafeCaller creates a new read-only instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeCaller(address common.Address, caller bind.ContractCaller) (*ERC20SafeCaller, error) { + contract, err := bindERC20Safe(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC20SafeCaller{contract: contract}, nil +} + +// NewERC20SafeTransactor creates a new write-only instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeTransactor(address common.Address, transactor bind.ContractTransactor) (*ERC20SafeTransactor, error) { + contract, err := bindERC20Safe(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC20SafeTransactor{contract: contract}, nil +} + +// NewERC20SafeFilterer creates a new log filterer instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeFilterer(address common.Address, filterer bind.ContractFilterer) (*ERC20SafeFilterer, error) { + contract, err := bindERC20Safe(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC20SafeFilterer{contract: contract}, nil +} + +// bindERC20Safe binds a generic wrapper to an already deployed contract. +func bindERC20Safe(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC20SafeMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC20Safe *ERC20SafeRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20Safe.Contract.ERC20SafeCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC20Safe *ERC20SafeRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.Contract.ERC20SafeTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20Safe *ERC20SafeRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20Safe.Contract.ERC20SafeTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC20Safe *ERC20SafeCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20Safe.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC20Safe *ERC20SafeTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20Safe *ERC20SafeTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20Safe.Contract.contract.Transact(opts, method, params...) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_ERC20Safe *ERC20SafeCaller) Admin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "admin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_ERC20Safe *ERC20SafeSession) Admin() (common.Address, error) { + return _ERC20Safe.Contract.Admin(&_ERC20Safe.CallOpts) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_ERC20Safe *ERC20SafeCallerSession) Admin() (common.Address, error) { + return _ERC20Safe.Contract.Admin(&_ERC20Safe.CallOpts) +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCaller) BatchBlockLimit(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchBlockLimit") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeSession) BatchBlockLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchBlockLimit(&_ERC20Safe.CallOpts) +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCallerSession) BatchBlockLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchBlockLimit(&_ERC20Safe.CallOpts) +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeCaller) BatchDeposits(opts *bind.CallOpts, arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchDeposits", arg0, arg1) + + outstruct := new(struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 + }) + if err != nil { + return *outstruct, err + } + + outstruct.Nonce = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.TokenAddress = *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + outstruct.Amount = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.Depositor = *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + outstruct.Recipient = *abi.ConvertType(out[4], new([32]byte)).(*[32]byte) + outstruct.Status = *abi.ConvertType(out[5], new(uint8)).(*uint8) + + return *outstruct, err + +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeSession) BatchDeposits(arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + return _ERC20Safe.Contract.BatchDeposits(&_ERC20Safe.CallOpts, arg0, arg1) +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeCallerSession) BatchDeposits(arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + return _ERC20Safe.Contract.BatchDeposits(&_ERC20Safe.CallOpts, arg0, arg1) +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCaller) BatchSettleLimit(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchSettleLimit") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeSession) BatchSettleLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchSettleLimit(&_ERC20Safe.CallOpts) +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCallerSession) BatchSettleLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchSettleLimit(&_ERC20Safe.CallOpts) +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeCaller) BatchSize(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchSize") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeSession) BatchSize() (uint16, error) { + return _ERC20Safe.Contract.BatchSize(&_ERC20Safe.CallOpts) +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeCallerSession) BatchSize() (uint16, error) { + return _ERC20Safe.Contract.BatchSize(&_ERC20Safe.CallOpts) +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeCaller) Batches(opts *bind.CallOpts, arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batches", arg0) + + outstruct := new(struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 + }) + if err != nil { + return *outstruct, err + } + + outstruct.Nonce = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(uint64)).(*uint64) + outstruct.LastUpdatedBlockNumber = *abi.ConvertType(out[2], new(uint64)).(*uint64) + outstruct.DepositsCount = *abi.ConvertType(out[3], new(uint16)).(*uint16) + + return *outstruct, err + +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeSession) Batches(arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + return _ERC20Safe.Contract.Batches(&_ERC20Safe.CallOpts, arg0) +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeCallerSession) Batches(arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + return _ERC20Safe.Contract.Batches(&_ERC20Safe.CallOpts, arg0) +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCaller) BatchesCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchesCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeSession) BatchesCount() (uint64, error) { + return _ERC20Safe.Contract.BatchesCount(&_ERC20Safe.CallOpts) +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCallerSession) BatchesCount() (uint64, error) { + return _ERC20Safe.Contract.BatchesCount(&_ERC20Safe.CallOpts) +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeCaller) Bridge(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "bridge") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeSession) Bridge() (common.Address, error) { + return _ERC20Safe.Contract.Bridge(&_ERC20Safe.CallOpts) +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeCallerSession) Bridge() (common.Address, error) { + return _ERC20Safe.Contract.Bridge(&_ERC20Safe.CallOpts) +} + +// BurnBalances is a free data retrieval call binding the contract method 0xcf6682a2. +// +// Solidity: function burnBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) BurnBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "burnBalances", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BurnBalances is a free data retrieval call binding the contract method 0xcf6682a2. +// +// Solidity: function burnBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) BurnBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.BurnBalances(&_ERC20Safe.CallOpts, arg0) +} + +// BurnBalances is a free data retrieval call binding the contract method 0xcf6682a2. +// +// Solidity: function burnBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) BurnBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.BurnBalances(&_ERC20Safe.CallOpts, arg0) +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCaller) DepositsCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "depositsCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeSession) DepositsCount() (uint64, error) { + return _ERC20Safe.Contract.DepositsCount(&_ERC20Safe.CallOpts) +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCallerSession) DepositsCount() (uint64, error) { + return _ERC20Safe.Contract.DepositsCount(&_ERC20Safe.CallOpts) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16), bool isBatchFinal) +func (_ERC20Safe *ERC20SafeCaller) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (Batch, bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getBatch", batchNonce) + + if err != nil { + return *new(Batch), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(Batch)).(*Batch) + out1 := *abi.ConvertType(out[1], new(bool)).(*bool) + + return out0, out1, err + +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16), bool isBatchFinal) +func (_ERC20Safe *ERC20SafeSession) GetBatch(batchNonce *big.Int) (Batch, bool, error) { + return _ERC20Safe.Contract.GetBatch(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16), bool isBatchFinal) +func (_ERC20Safe *ERC20SafeCallerSession) GetBatch(batchNonce *big.Int) (Batch, bool, error) { + return _ERC20Safe.Contract.GetBatch(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[], bool areDepositsFinal) +func (_ERC20Safe *ERC20SafeCaller) GetDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]Deposit, bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getDeposits", batchNonce) + + if err != nil { + return *new([]Deposit), *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new([]Deposit)).(*[]Deposit) + out1 := *abi.ConvertType(out[1], new(bool)).(*bool) + + return out0, out1, err + +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[], bool areDepositsFinal) +func (_ERC20Safe *ERC20SafeSession) GetDeposits(batchNonce *big.Int) ([]Deposit, bool, error) { + return _ERC20Safe.Contract.GetDeposits(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[], bool areDepositsFinal) +func (_ERC20Safe *ERC20SafeCallerSession) GetDeposits(batchNonce *big.Int) ([]Deposit, bool, error) { + return _ERC20Safe.Contract.GetDeposits(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) GetTokenMaxLimit(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getTokenMaxLimit", token) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) GetTokenMaxLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMaxLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) GetTokenMaxLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMaxLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) GetTokenMinLimit(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getTokenMinLimit", token) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) GetTokenMinLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMinLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) GetTokenMinLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMinLimit(&_ERC20Safe.CallOpts, token) +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) IsAnyBatchInProgress(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "isAnyBatchInProgress") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeSession) IsAnyBatchInProgress() (bool, error) { + return _ERC20Safe.Contract.IsAnyBatchInProgress(&_ERC20Safe.CallOpts) +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) IsAnyBatchInProgress() (bool, error) { + return _ERC20Safe.Contract.IsAnyBatchInProgress(&_ERC20Safe.CallOpts) +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) IsTokenWhitelisted(opts *bind.CallOpts, token common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "isTokenWhitelisted", token) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) IsTokenWhitelisted(token common.Address) (bool, error) { + return _ERC20Safe.Contract.IsTokenWhitelisted(&_ERC20Safe.CallOpts, token) +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) IsTokenWhitelisted(token common.Address) (bool, error) { + return _ERC20Safe.Contract.IsTokenWhitelisted(&_ERC20Safe.CallOpts, token) +} + +// MintBalances is a free data retrieval call binding the contract method 0xbc56602f. +// +// Solidity: function mintBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) MintBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "mintBalances", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// MintBalances is a free data retrieval call binding the contract method 0xbc56602f. +// +// Solidity: function mintBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) MintBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.MintBalances(&_ERC20Safe.CallOpts, arg0) +} + +// MintBalances is a free data retrieval call binding the contract method 0xbc56602f. +// +// Solidity: function mintBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) MintBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.MintBalances(&_ERC20Safe.CallOpts, arg0) +} + +// MintBurnTokens is a free data retrieval call binding the contract method 0x90e0cfcb. +// +// Solidity: function mintBurnTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) MintBurnTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "mintBurnTokens", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// MintBurnTokens is a free data retrieval call binding the contract method 0x90e0cfcb. +// +// Solidity: function mintBurnTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) MintBurnTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.MintBurnTokens(&_ERC20Safe.CallOpts, arg0) +} + +// MintBurnTokens is a free data retrieval call binding the contract method 0x90e0cfcb. +// +// Solidity: function mintBurnTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) MintBurnTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.MintBurnTokens(&_ERC20Safe.CallOpts, arg0) +} + +// NativeTokens is a free data retrieval call binding the contract method 0xc86726f6. +// +// Solidity: function nativeTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) NativeTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "nativeTokens", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// NativeTokens is a free data retrieval call binding the contract method 0xc86726f6. +// +// Solidity: function nativeTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) NativeTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.NativeTokens(&_ERC20Safe.CallOpts, arg0) +} + +// NativeTokens is a free data retrieval call binding the contract method 0xc86726f6. +// +// Solidity: function nativeTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) NativeTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.NativeTokens(&_ERC20Safe.CallOpts, arg0) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) Paused(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "paused") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeSession) Paused() (bool, error) { + return _ERC20Safe.Contract.Paused(&_ERC20Safe.CallOpts) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) Paused() (bool, error) { + return _ERC20Safe.Contract.Paused(&_ERC20Safe.CallOpts) +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenMaxLimits(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenMaxLimits", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenMaxLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMaxLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenMaxLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMaxLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenMinLimits(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenMinLimits", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenMinLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMinLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenMinLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMinLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TotalBalances is a free data retrieval call binding the contract method 0xaee9c872. +// +// Solidity: function totalBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TotalBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "totalBalances", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalBalances is a free data retrieval call binding the contract method 0xaee9c872. +// +// Solidity: function totalBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TotalBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TotalBalances(&_ERC20Safe.CallOpts, arg0) +} + +// TotalBalances is a free data retrieval call binding the contract method 0xaee9c872. +// +// Solidity: function totalBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TotalBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TotalBalances(&_ERC20Safe.CallOpts, arg0) +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) WhitelistedTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "whitelistedTokens", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) WhitelistedTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.WhitelistedTokens(&_ERC20Safe.CallOpts, arg0) +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) WhitelistedTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.WhitelistedTokens(&_ERC20Safe.CallOpts, arg0) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeTransactor) Deposit(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "deposit", tokenAddress, amount, recipientAddress) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeSession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.Deposit(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.Deposit(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// DepositWithSCExecution is a paid mutator transaction binding the contract method 0xc859b3fe. +// +// Solidity: function depositWithSCExecution(address tokenAddress, uint256 amount, bytes32 recipientAddress, bytes callData) returns() +func (_ERC20Safe *ERC20SafeTransactor) DepositWithSCExecution(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte, callData []byte) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "depositWithSCExecution", tokenAddress, amount, recipientAddress, callData) +} + +// DepositWithSCExecution is a paid mutator transaction binding the contract method 0xc859b3fe. +// +// Solidity: function depositWithSCExecution(address tokenAddress, uint256 amount, bytes32 recipientAddress, bytes callData) returns() +func (_ERC20Safe *ERC20SafeSession) DepositWithSCExecution(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte, callData []byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.DepositWithSCExecution(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress, callData) +} + +// DepositWithSCExecution is a paid mutator transaction binding the contract method 0xc859b3fe. +// +// Solidity: function depositWithSCExecution(address tokenAddress, uint256 amount, bytes32 recipientAddress, bytes callData) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) DepositWithSCExecution(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte, callData []byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.DepositWithSCExecution(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress, callData) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) InitSupply(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "initSupply", tokenAddress, amount) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) InitSupply(tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupply(&_ERC20Safe.TransactOpts, tokenAddress, amount) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) InitSupply(tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupply(&_ERC20Safe.TransactOpts, tokenAddress, amount) +} + +// InitSupplyMintBurn is a paid mutator transaction binding the contract method 0xe9935b4a. +// +// Solidity: function initSupplyMintBurn(address tokenAddress, uint256 mintAmount, uint256 burnAmount) returns() +func (_ERC20Safe *ERC20SafeTransactor) InitSupplyMintBurn(opts *bind.TransactOpts, tokenAddress common.Address, mintAmount *big.Int, burnAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "initSupplyMintBurn", tokenAddress, mintAmount, burnAmount) +} + +// InitSupplyMintBurn is a paid mutator transaction binding the contract method 0xe9935b4a. +// +// Solidity: function initSupplyMintBurn(address tokenAddress, uint256 mintAmount, uint256 burnAmount) returns() +func (_ERC20Safe *ERC20SafeSession) InitSupplyMintBurn(tokenAddress common.Address, mintAmount *big.Int, burnAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupplyMintBurn(&_ERC20Safe.TransactOpts, tokenAddress, mintAmount, burnAmount) +} + +// InitSupplyMintBurn is a paid mutator transaction binding the contract method 0xe9935b4a. +// +// Solidity: function initSupplyMintBurn(address tokenAddress, uint256 mintAmount, uint256 burnAmount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) InitSupplyMintBurn(tokenAddress common.Address, mintAmount *big.Int, burnAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupplyMintBurn(&_ERC20Safe.TransactOpts, tokenAddress, mintAmount, burnAmount) +} + +// Initialize is a paid mutator transaction binding the contract method 0x8129fc1c. +// +// Solidity: function initialize() returns() +func (_ERC20Safe *ERC20SafeTransactor) Initialize(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "initialize") +} + +// Initialize is a paid mutator transaction binding the contract method 0x8129fc1c. +// +// Solidity: function initialize() returns() +func (_ERC20Safe *ERC20SafeSession) Initialize() (*types.Transaction, error) { + return _ERC20Safe.Contract.Initialize(&_ERC20Safe.TransactOpts) +} + +// Initialize is a paid mutator transaction binding the contract method 0x8129fc1c. +// +// Solidity: function initialize() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Initialize() (*types.Transaction, error) { + return _ERC20Safe.Contract.Initialize(&_ERC20Safe.TransactOpts) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeTransactor) Pause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "pause") +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeSession) Pause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Pause(&_ERC20Safe.TransactOpts) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Pause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Pause(&_ERC20Safe.TransactOpts) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactor) RecoverLostFunds(opts *bind.TransactOpts, tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "recoverLostFunds", tokenAddress) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeSession) RecoverLostFunds(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RecoverLostFunds(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RecoverLostFunds(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RecoverLostFunds(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeTransactor) RemoveTokenFromWhitelist(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "removeTokenFromWhitelist", token) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeSession) RemoveTokenFromWhitelist(token common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RemoveTokenFromWhitelist(&_ERC20Safe.TransactOpts, token) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RemoveTokenFromWhitelist(token common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RemoveTokenFromWhitelist(&_ERC20Safe.TransactOpts, token) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeTransactor) RenounceAdmin(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "renounceAdmin") +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeSession) RenounceAdmin() (*types.Transaction, error) { + return _ERC20Safe.Contract.RenounceAdmin(&_ERC20Safe.TransactOpts) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RenounceAdmin() (*types.Transaction, error) { + return _ERC20Safe.Contract.RenounceAdmin(&_ERC20Safe.TransactOpts) +} + +// ResetTotalBalance is a paid mutator transaction binding the contract method 0xd2763186. +// +// Solidity: function resetTotalBalance(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactor) ResetTotalBalance(opts *bind.TransactOpts, tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "resetTotalBalance", tokenAddress) +} + +// ResetTotalBalance is a paid mutator transaction binding the contract method 0xd2763186. +// +// Solidity: function resetTotalBalance(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeSession) ResetTotalBalance(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.ResetTotalBalance(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// ResetTotalBalance is a paid mutator transaction binding the contract method 0xd2763186. +// +// Solidity: function resetTotalBalance(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) ResetTotalBalance(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.ResetTotalBalance(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchBlockLimit(opts *bind.TransactOpts, newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchBlockLimit", newBatchBlockLimit) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchBlockLimit(newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchBlockLimit(&_ERC20Safe.TransactOpts, newBatchBlockLimit) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchBlockLimit(newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchBlockLimit(&_ERC20Safe.TransactOpts, newBatchBlockLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchSettleLimit(opts *bind.TransactOpts, newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchSettleLimit", newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSettleLimit(&_ERC20Safe.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSettleLimit(&_ERC20Safe.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchSize(opts *bind.TransactOpts, newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchSize", newBatchSize) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchSize(newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSize(&_ERC20Safe.TransactOpts, newBatchSize) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchSize(newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSize(&_ERC20Safe.TransactOpts, newBatchSize) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBridge(opts *bind.TransactOpts, newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBridge", newBridge) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeSession) SetBridge(newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBridge(&_ERC20Safe.TransactOpts, newBridge) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBridge(newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBridge(&_ERC20Safe.TransactOpts, newBridge) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetTokenMaxLimit(opts *bind.TransactOpts, token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setTokenMaxLimit", token, amount) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) SetTokenMaxLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMaxLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetTokenMaxLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMaxLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetTokenMinLimit(opts *bind.TransactOpts, token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setTokenMinLimit", token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) SetTokenMinLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMinLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetTokenMinLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMinLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeTransactor) Transfer(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "transfer", tokenAddress, amount, recipientAddress) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeSession) Transfer(tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.Transfer(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeTransactorSession) Transfer(tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.Transfer(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeTransactor) TransferAdmin(opts *bind.TransactOpts, newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "transferAdmin", newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.TransferAdmin(&_ERC20Safe.TransactOpts, newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.TransferAdmin(&_ERC20Safe.TransactOpts, newAdmin) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeTransactor) Unpause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "unpause") +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeSession) Unpause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Unpause(&_ERC20Safe.TransactOpts) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Unpause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Unpause(&_ERC20Safe.TransactOpts) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x41220439. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount, bool mintBurn, bool native, uint256 totalBalance, uint256 mintBalance, uint256 burnBalance) returns() +func (_ERC20Safe *ERC20SafeTransactor) WhitelistToken(opts *bind.TransactOpts, token common.Address, minimumAmount *big.Int, maximumAmount *big.Int, mintBurn bool, native bool, totalBalance *big.Int, mintBalance *big.Int, burnBalance *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "whitelistToken", token, minimumAmount, maximumAmount, mintBurn, native, totalBalance, mintBalance, burnBalance) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x41220439. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount, bool mintBurn, bool native, uint256 totalBalance, uint256 mintBalance, uint256 burnBalance) returns() +func (_ERC20Safe *ERC20SafeSession) WhitelistToken(token common.Address, minimumAmount *big.Int, maximumAmount *big.Int, mintBurn bool, native bool, totalBalance *big.Int, mintBalance *big.Int, burnBalance *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.WhitelistToken(&_ERC20Safe.TransactOpts, token, minimumAmount, maximumAmount, mintBurn, native, totalBalance, mintBalance, burnBalance) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x41220439. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount, bool mintBurn, bool native, uint256 totalBalance, uint256 mintBalance, uint256 burnBalance) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) WhitelistToken(token common.Address, minimumAmount *big.Int, maximumAmount *big.Int, mintBurn bool, native bool, totalBalance *big.Int, mintBalance *big.Int, burnBalance *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.WhitelistToken(&_ERC20Safe.TransactOpts, token, minimumAmount, maximumAmount, mintBurn, native, totalBalance, mintBalance, burnBalance) +} + +// ERC20SafeAdminRoleTransferredIterator is returned from FilterAdminRoleTransferred and is used to iterate over the raw logs and unpacked data for AdminRoleTransferred events raised by the ERC20Safe contract. +type ERC20SafeAdminRoleTransferredIterator struct { + Event *ERC20SafeAdminRoleTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeAdminRoleTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeAdminRoleTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeAdminRoleTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeAdminRoleTransferred represents a AdminRoleTransferred event raised by the ERC20Safe contract. +type ERC20SafeAdminRoleTransferred struct { + PreviousAdmin common.Address + NewAdmin common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterAdminRoleTransferred is a free log retrieval operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_ERC20Safe *ERC20SafeFilterer) FilterAdminRoleTransferred(opts *bind.FilterOpts, previousAdmin []common.Address, newAdmin []common.Address) (*ERC20SafeAdminRoleTransferredIterator, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return &ERC20SafeAdminRoleTransferredIterator{contract: _ERC20Safe.contract, event: "AdminRoleTransferred", logs: logs, sub: sub}, nil +} + +// WatchAdminRoleTransferred is a free log subscription operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_ERC20Safe *ERC20SafeFilterer) WatchAdminRoleTransferred(opts *bind.WatchOpts, sink chan<- *ERC20SafeAdminRoleTransferred, previousAdmin []common.Address, newAdmin []common.Address) (event.Subscription, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeAdminRoleTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseAdminRoleTransferred is a log parse operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_ERC20Safe *ERC20SafeFilterer) ParseAdminRoleTransferred(log types.Log) (*ERC20SafeAdminRoleTransferred, error) { + event := new(ERC20SafeAdminRoleTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeBridgeTransferredIterator is returned from FilterBridgeTransferred and is used to iterate over the raw logs and unpacked data for BridgeTransferred events raised by the ERC20Safe contract. +type ERC20SafeBridgeTransferredIterator struct { + Event *ERC20SafeBridgeTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeBridgeTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeBridgeTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeBridgeTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeBridgeTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeBridgeTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeBridgeTransferred represents a BridgeTransferred event raised by the ERC20Safe contract. +type ERC20SafeBridgeTransferred struct { + PreviousBridge common.Address + NewBridge common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBridgeTransferred is a free log retrieval operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) FilterBridgeTransferred(opts *bind.FilterOpts, previousBridge []common.Address, newBridge []common.Address) (*ERC20SafeBridgeTransferredIterator, error) { + + var previousBridgeRule []interface{} + for _, previousBridgeItem := range previousBridge { + previousBridgeRule = append(previousBridgeRule, previousBridgeItem) + } + var newBridgeRule []interface{} + for _, newBridgeItem := range newBridge { + newBridgeRule = append(newBridgeRule, newBridgeItem) + } + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "BridgeTransferred", previousBridgeRule, newBridgeRule) + if err != nil { + return nil, err + } + return &ERC20SafeBridgeTransferredIterator{contract: _ERC20Safe.contract, event: "BridgeTransferred", logs: logs, sub: sub}, nil +} + +// WatchBridgeTransferred is a free log subscription operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) WatchBridgeTransferred(opts *bind.WatchOpts, sink chan<- *ERC20SafeBridgeTransferred, previousBridge []common.Address, newBridge []common.Address) (event.Subscription, error) { + + var previousBridgeRule []interface{} + for _, previousBridgeItem := range previousBridge { + previousBridgeRule = append(previousBridgeRule, previousBridgeItem) + } + var newBridgeRule []interface{} + for _, newBridgeItem := range newBridge { + newBridgeRule = append(newBridgeRule, newBridgeItem) + } + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "BridgeTransferred", previousBridgeRule, newBridgeRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeBridgeTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "BridgeTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseBridgeTransferred is a log parse operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) ParseBridgeTransferred(log types.Log) (*ERC20SafeBridgeTransferred, error) { + event := new(ERC20SafeBridgeTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "BridgeTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeERC20DepositIterator is returned from FilterERC20Deposit and is used to iterate over the raw logs and unpacked data for ERC20Deposit events raised by the ERC20Safe contract. +type ERC20SafeERC20DepositIterator struct { + Event *ERC20SafeERC20Deposit // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeERC20DepositIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeERC20Deposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeERC20Deposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeERC20DepositIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeERC20DepositIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeERC20Deposit represents a ERC20Deposit event raised by the ERC20Safe contract. +type ERC20SafeERC20Deposit struct { + BatchId *big.Int + DepositNonce *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterERC20Deposit is a free log retrieval operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 batchId, uint112 depositNonce) +func (_ERC20Safe *ERC20SafeFilterer) FilterERC20Deposit(opts *bind.FilterOpts) (*ERC20SafeERC20DepositIterator, error) { + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "ERC20Deposit") + if err != nil { + return nil, err + } + return &ERC20SafeERC20DepositIterator{contract: _ERC20Safe.contract, event: "ERC20Deposit", logs: logs, sub: sub}, nil +} + +// WatchERC20Deposit is a free log subscription operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 batchId, uint112 depositNonce) +func (_ERC20Safe *ERC20SafeFilterer) WatchERC20Deposit(opts *bind.WatchOpts, sink chan<- *ERC20SafeERC20Deposit) (event.Subscription, error) { + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "ERC20Deposit") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeERC20Deposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20Deposit", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseERC20Deposit is a log parse operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 batchId, uint112 depositNonce) +func (_ERC20Safe *ERC20SafeFilterer) ParseERC20Deposit(log types.Log) (*ERC20SafeERC20Deposit, error) { + event := new(ERC20SafeERC20Deposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20Deposit", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeERC20SCDepositIterator is returned from FilterERC20SCDeposit and is used to iterate over the raw logs and unpacked data for ERC20SCDeposit events raised by the ERC20Safe contract. +type ERC20SafeERC20SCDepositIterator struct { + Event *ERC20SafeERC20SCDeposit // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeERC20SCDepositIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeERC20SCDeposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeERC20SCDeposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeERC20SCDepositIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeERC20SCDepositIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeERC20SCDeposit represents a ERC20SCDeposit event raised by the ERC20Safe contract. +type ERC20SafeERC20SCDeposit struct { + BatchId *big.Int + DepositNonce *big.Int + CallData []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterERC20SCDeposit is a free log retrieval operation binding the contract event 0xce848da21487607afba5c5a500c2ad1002d9b8db97ca5512671309df071113b2. +// +// Solidity: event ERC20SCDeposit(uint112 indexed batchId, uint112 depositNonce, bytes callData) +func (_ERC20Safe *ERC20SafeFilterer) FilterERC20SCDeposit(opts *bind.FilterOpts, batchId []*big.Int) (*ERC20SafeERC20SCDepositIterator, error) { + + var batchIdRule []interface{} + for _, batchIdItem := range batchId { + batchIdRule = append(batchIdRule, batchIdItem) + } + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "ERC20SCDeposit", batchIdRule) + if err != nil { + return nil, err + } + return &ERC20SafeERC20SCDepositIterator{contract: _ERC20Safe.contract, event: "ERC20SCDeposit", logs: logs, sub: sub}, nil +} + +// WatchERC20SCDeposit is a free log subscription operation binding the contract event 0xce848da21487607afba5c5a500c2ad1002d9b8db97ca5512671309df071113b2. +// +// Solidity: event ERC20SCDeposit(uint112 indexed batchId, uint112 depositNonce, bytes callData) +func (_ERC20Safe *ERC20SafeFilterer) WatchERC20SCDeposit(opts *bind.WatchOpts, sink chan<- *ERC20SafeERC20SCDeposit, batchId []*big.Int) (event.Subscription, error) { + + var batchIdRule []interface{} + for _, batchIdItem := range batchId { + batchIdRule = append(batchIdRule, batchIdItem) + } + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "ERC20SCDeposit", batchIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeERC20SCDeposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20SCDeposit", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseERC20SCDeposit is a log parse operation binding the contract event 0xce848da21487607afba5c5a500c2ad1002d9b8db97ca5512671309df071113b2. +// +// Solidity: event ERC20SCDeposit(uint112 indexed batchId, uint112 depositNonce, bytes callData) +func (_ERC20Safe *ERC20SafeFilterer) ParseERC20SCDeposit(log types.Log) (*ERC20SafeERC20SCDeposit, error) { + event := new(ERC20SafeERC20SCDeposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20SCDeposit", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the ERC20Safe contract. +type ERC20SafeInitializedIterator struct { + Event *ERC20SafeInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeInitialized represents a Initialized event raised by the ERC20Safe contract. +type ERC20SafeInitialized struct { + Version uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_ERC20Safe *ERC20SafeFilterer) FilterInitialized(opts *bind.FilterOpts) (*ERC20SafeInitializedIterator, error) { + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &ERC20SafeInitializedIterator{contract: _ERC20Safe.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_ERC20Safe *ERC20SafeFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *ERC20SafeInitialized) (event.Subscription, error) { + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeInitialized) + if err := _ERC20Safe.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_ERC20Safe *ERC20SafeFilterer) ParseInitialized(log types.Log) (*ERC20SafeInitialized, error) { + event := new(ERC20SafeInitialized) + if err := _ERC20Safe.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafePauseIterator is returned from FilterPause and is used to iterate over the raw logs and unpacked data for Pause events raised by the ERC20Safe contract. +type ERC20SafePauseIterator struct { + Event *ERC20SafePause // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafePauseIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafePause) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafePause) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafePauseIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafePauseIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafePause represents a Pause event raised by the ERC20Safe contract. +type ERC20SafePause struct { + IsPause bool + Raw types.Log // Blockchain specific contextual infos +} + +// FilterPause is a free log retrieval operation binding the contract event 0x9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304. +// +// Solidity: event Pause(bool isPause) +func (_ERC20Safe *ERC20SafeFilterer) FilterPause(opts *bind.FilterOpts) (*ERC20SafePauseIterator, error) { + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "Pause") + if err != nil { + return nil, err + } + return &ERC20SafePauseIterator{contract: _ERC20Safe.contract, event: "Pause", logs: logs, sub: sub}, nil +} + +// WatchPause is a free log subscription operation binding the contract event 0x9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304. +// +// Solidity: event Pause(bool isPause) +func (_ERC20Safe *ERC20SafeFilterer) WatchPause(opts *bind.WatchOpts, sink chan<- *ERC20SafePause) (event.Subscription, error) { + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "Pause") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafePause) + if err := _ERC20Safe.contract.UnpackLog(event, "Pause", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParsePause is a log parse operation binding the contract event 0x9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304. +// +// Solidity: event Pause(bool isPause) +func (_ERC20Safe *ERC20SafeFilterer) ParsePause(log types.Log) (*ERC20SafePause, error) { + event := new(ERC20SafePause) + if err := _ERC20Safe.contract.UnpackLog(event, "Pause", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/clients/ethereum/contract/GenericERC20.go b/clients/ethereum/contract/GenericERC20.go new file mode 100755 index 00000000..c03630ae --- /dev/null +++ b/clients/ethereum/contract/GenericERC20.go @@ -0,0 +1,759 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// GenericERC20MetaData contains all meta data concerning the GenericERC20 contract. +var GenericERC20MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenSymbol\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"providedNumDecimals\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", +} + +// GenericERC20ABI is the input ABI used to generate the binding from. +// Deprecated: Use GenericERC20MetaData.ABI instead. +var GenericERC20ABI = GenericERC20MetaData.ABI + +// GenericERC20 is an auto generated Go binding around an Ethereum contract. +type GenericERC20 struct { + GenericERC20Caller // Read-only binding to the contract + GenericERC20Transactor // Write-only binding to the contract + GenericERC20Filterer // Log filterer for contract events +} + +// GenericERC20Caller is an auto generated read-only Go binding around an Ethereum contract. +type GenericERC20Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GenericERC20Transactor is an auto generated write-only Go binding around an Ethereum contract. +type GenericERC20Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GenericERC20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type GenericERC20Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// GenericERC20Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type GenericERC20Session struct { + Contract *GenericERC20 // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GenericERC20CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type GenericERC20CallerSession struct { + Contract *GenericERC20Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// GenericERC20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type GenericERC20TransactorSession struct { + Contract *GenericERC20Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// GenericERC20Raw is an auto generated low-level Go binding around an Ethereum contract. +type GenericERC20Raw struct { + Contract *GenericERC20 // Generic contract binding to access the raw methods on +} + +// GenericERC20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type GenericERC20CallerRaw struct { + Contract *GenericERC20Caller // Generic read-only contract binding to access the raw methods on +} + +// GenericERC20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type GenericERC20TransactorRaw struct { + Contract *GenericERC20Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewGenericERC20 creates a new instance of GenericERC20, bound to a specific deployed contract. +func NewGenericERC20(address common.Address, backend bind.ContractBackend) (*GenericERC20, error) { + contract, err := bindGenericERC20(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &GenericERC20{GenericERC20Caller: GenericERC20Caller{contract: contract}, GenericERC20Transactor: GenericERC20Transactor{contract: contract}, GenericERC20Filterer: GenericERC20Filterer{contract: contract}}, nil +} + +// NewGenericERC20Caller creates a new read-only instance of GenericERC20, bound to a specific deployed contract. +func NewGenericERC20Caller(address common.Address, caller bind.ContractCaller) (*GenericERC20Caller, error) { + contract, err := bindGenericERC20(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &GenericERC20Caller{contract: contract}, nil +} + +// NewGenericERC20Transactor creates a new write-only instance of GenericERC20, bound to a specific deployed contract. +func NewGenericERC20Transactor(address common.Address, transactor bind.ContractTransactor) (*GenericERC20Transactor, error) { + contract, err := bindGenericERC20(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &GenericERC20Transactor{contract: contract}, nil +} + +// NewGenericERC20Filterer creates a new log filterer instance of GenericERC20, bound to a specific deployed contract. +func NewGenericERC20Filterer(address common.Address, filterer bind.ContractFilterer) (*GenericERC20Filterer, error) { + contract, err := bindGenericERC20(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &GenericERC20Filterer{contract: contract}, nil +} + +// bindGenericERC20 binds a generic wrapper to an already deployed contract. +func bindGenericERC20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := GenericERC20MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_GenericERC20 *GenericERC20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _GenericERC20.Contract.GenericERC20Caller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_GenericERC20 *GenericERC20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _GenericERC20.Contract.GenericERC20Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_GenericERC20 *GenericERC20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _GenericERC20.Contract.GenericERC20Transactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_GenericERC20 *GenericERC20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _GenericERC20.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_GenericERC20 *GenericERC20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _GenericERC20.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_GenericERC20 *GenericERC20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _GenericERC20.Contract.contract.Transact(opts, method, params...) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_GenericERC20 *GenericERC20Caller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { + var out []interface{} + err := _GenericERC20.contract.Call(opts, &out, "allowance", owner, spender) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_GenericERC20 *GenericERC20Session) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _GenericERC20.Contract.Allowance(&_GenericERC20.CallOpts, owner, spender) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_GenericERC20 *GenericERC20CallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _GenericERC20.Contract.Allowance(&_GenericERC20.CallOpts, owner, spender) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_GenericERC20 *GenericERC20Caller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { + var out []interface{} + err := _GenericERC20.contract.Call(opts, &out, "balanceOf", account) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_GenericERC20 *GenericERC20Session) BalanceOf(account common.Address) (*big.Int, error) { + return _GenericERC20.Contract.BalanceOf(&_GenericERC20.CallOpts, account) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_GenericERC20 *GenericERC20CallerSession) BalanceOf(account common.Address) (*big.Int, error) { + return _GenericERC20.Contract.BalanceOf(&_GenericERC20.CallOpts, account) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_GenericERC20 *GenericERC20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _GenericERC20.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_GenericERC20 *GenericERC20Session) Decimals() (uint8, error) { + return _GenericERC20.Contract.Decimals(&_GenericERC20.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_GenericERC20 *GenericERC20CallerSession) Decimals() (uint8, error) { + return _GenericERC20.Contract.Decimals(&_GenericERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_GenericERC20 *GenericERC20Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _GenericERC20.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_GenericERC20 *GenericERC20Session) Name() (string, error) { + return _GenericERC20.Contract.Name(&_GenericERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_GenericERC20 *GenericERC20CallerSession) Name() (string, error) { + return _GenericERC20.Contract.Name(&_GenericERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_GenericERC20 *GenericERC20Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _GenericERC20.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_GenericERC20 *GenericERC20Session) Symbol() (string, error) { + return _GenericERC20.Contract.Symbol(&_GenericERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_GenericERC20 *GenericERC20CallerSession) Symbol() (string, error) { + return _GenericERC20.Contract.Symbol(&_GenericERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_GenericERC20 *GenericERC20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _GenericERC20.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_GenericERC20 *GenericERC20Session) TotalSupply() (*big.Int, error) { + return _GenericERC20.Contract.TotalSupply(&_GenericERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_GenericERC20 *GenericERC20CallerSession) TotalSupply() (*big.Int, error) { + return _GenericERC20.Contract.TotalSupply(&_GenericERC20.CallOpts) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.contract.Transact(opts, "approve", spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20Session) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.Approve(&_GenericERC20.TransactOpts, spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20TransactorSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.Approve(&_GenericERC20.TransactOpts, spender, value) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address recipientAddress, uint256 amount) returns() +func (_GenericERC20 *GenericERC20Transactor) Mint(opts *bind.TransactOpts, recipientAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _GenericERC20.contract.Transact(opts, "mint", recipientAddress, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address recipientAddress, uint256 amount) returns() +func (_GenericERC20 *GenericERC20Session) Mint(recipientAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.Mint(&_GenericERC20.TransactOpts, recipientAddress, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address recipientAddress, uint256 amount) returns() +func (_GenericERC20 *GenericERC20TransactorSession) Mint(recipientAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.Mint(&_GenericERC20.TransactOpts, recipientAddress, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20Transactor) Transfer(opts *bind.TransactOpts, to common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.contract.Transact(opts, "transfer", to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20Session) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.Transfer(&_GenericERC20.TransactOpts, to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20TransactorSession) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.Transfer(&_GenericERC20.TransactOpts, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.contract.Transact(opts, "transferFrom", from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20Session) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.TransferFrom(&_GenericERC20.TransactOpts, from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_GenericERC20 *GenericERC20TransactorSession) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _GenericERC20.Contract.TransferFrom(&_GenericERC20.TransactOpts, from, to, value) +} + +// GenericERC20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the GenericERC20 contract. +type GenericERC20ApprovalIterator struct { + Event *GenericERC20Approval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GenericERC20ApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GenericERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GenericERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GenericERC20ApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GenericERC20ApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GenericERC20Approval represents a Approval event raised by the GenericERC20 contract. +type GenericERC20Approval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_GenericERC20 *GenericERC20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*GenericERC20ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _GenericERC20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &GenericERC20ApprovalIterator{contract: _GenericERC20.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_GenericERC20 *GenericERC20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *GenericERC20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _GenericERC20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GenericERC20Approval) + if err := _GenericERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_GenericERC20 *GenericERC20Filterer) ParseApproval(log types.Log) (*GenericERC20Approval, error) { + event := new(GenericERC20Approval) + if err := _GenericERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// GenericERC20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the GenericERC20 contract. +type GenericERC20TransferIterator struct { + Event *GenericERC20Transfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GenericERC20TransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GenericERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GenericERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GenericERC20TransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GenericERC20TransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GenericERC20Transfer represents a Transfer event raised by the GenericERC20 contract. +type GenericERC20Transfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_GenericERC20 *GenericERC20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*GenericERC20TransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _GenericERC20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &GenericERC20TransferIterator{contract: _GenericERC20.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_GenericERC20 *GenericERC20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *GenericERC20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _GenericERC20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GenericERC20Transfer) + if err := _GenericERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_GenericERC20 *GenericERC20Filterer) ParseTransfer(log types.Log) (*GenericERC20Transfer, error) { + event := new(GenericERC20Transfer) + if err := _GenericERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/clients/ethereum/contract/MintBurnERC20.go b/clients/ethereum/contract/MintBurnERC20.go new file mode 100755 index 00000000..53718aed --- /dev/null +++ b/clients/ethereum/contract/MintBurnERC20.go @@ -0,0 +1,1660 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// MintBurnERC20MetaData contains all meta data concerning the MintBurnERC20 contract. +var MintBurnERC20MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"}],\"name\":\"CallerNotMinter\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialization\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotInitializing\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"version\",\"type\":\"uint64\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MINTER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"providedNumDecimals\",\"type\":\"uint8\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", +} + +// MintBurnERC20ABI is the input ABI used to generate the binding from. +// Deprecated: Use MintBurnERC20MetaData.ABI instead. +var MintBurnERC20ABI = MintBurnERC20MetaData.ABI + +// MintBurnERC20 is an auto generated Go binding around an Ethereum contract. +type MintBurnERC20 struct { + MintBurnERC20Caller // Read-only binding to the contract + MintBurnERC20Transactor // Write-only binding to the contract + MintBurnERC20Filterer // Log filterer for contract events +} + +// MintBurnERC20Caller is an auto generated read-only Go binding around an Ethereum contract. +type MintBurnERC20Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MintBurnERC20Transactor is an auto generated write-only Go binding around an Ethereum contract. +type MintBurnERC20Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MintBurnERC20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type MintBurnERC20Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MintBurnERC20Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type MintBurnERC20Session struct { + Contract *MintBurnERC20 // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MintBurnERC20CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type MintBurnERC20CallerSession struct { + Contract *MintBurnERC20Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// MintBurnERC20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type MintBurnERC20TransactorSession struct { + Contract *MintBurnERC20Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MintBurnERC20Raw is an auto generated low-level Go binding around an Ethereum contract. +type MintBurnERC20Raw struct { + Contract *MintBurnERC20 // Generic contract binding to access the raw methods on +} + +// MintBurnERC20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type MintBurnERC20CallerRaw struct { + Contract *MintBurnERC20Caller // Generic read-only contract binding to access the raw methods on +} + +// MintBurnERC20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type MintBurnERC20TransactorRaw struct { + Contract *MintBurnERC20Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewMintBurnERC20 creates a new instance of MintBurnERC20, bound to a specific deployed contract. +func NewMintBurnERC20(address common.Address, backend bind.ContractBackend) (*MintBurnERC20, error) { + contract, err := bindMintBurnERC20(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &MintBurnERC20{MintBurnERC20Caller: MintBurnERC20Caller{contract: contract}, MintBurnERC20Transactor: MintBurnERC20Transactor{contract: contract}, MintBurnERC20Filterer: MintBurnERC20Filterer{contract: contract}}, nil +} + +// NewMintBurnERC20Caller creates a new read-only instance of MintBurnERC20, bound to a specific deployed contract. +func NewMintBurnERC20Caller(address common.Address, caller bind.ContractCaller) (*MintBurnERC20Caller, error) { + contract, err := bindMintBurnERC20(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &MintBurnERC20Caller{contract: contract}, nil +} + +// NewMintBurnERC20Transactor creates a new write-only instance of MintBurnERC20, bound to a specific deployed contract. +func NewMintBurnERC20Transactor(address common.Address, transactor bind.ContractTransactor) (*MintBurnERC20Transactor, error) { + contract, err := bindMintBurnERC20(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &MintBurnERC20Transactor{contract: contract}, nil +} + +// NewMintBurnERC20Filterer creates a new log filterer instance of MintBurnERC20, bound to a specific deployed contract. +func NewMintBurnERC20Filterer(address common.Address, filterer bind.ContractFilterer) (*MintBurnERC20Filterer, error) { + contract, err := bindMintBurnERC20(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &MintBurnERC20Filterer{contract: contract}, nil +} + +// bindMintBurnERC20 binds a generic wrapper to an already deployed contract. +func bindMintBurnERC20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := MintBurnERC20MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_MintBurnERC20 *MintBurnERC20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _MintBurnERC20.Contract.MintBurnERC20Caller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_MintBurnERC20 *MintBurnERC20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _MintBurnERC20.Contract.MintBurnERC20Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_MintBurnERC20 *MintBurnERC20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _MintBurnERC20.Contract.MintBurnERC20Transactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_MintBurnERC20 *MintBurnERC20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _MintBurnERC20.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_MintBurnERC20 *MintBurnERC20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _MintBurnERC20.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_MintBurnERC20 *MintBurnERC20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _MintBurnERC20.Contract.contract.Transact(opts, method, params...) +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20Caller) DEFAULTADMINROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "DEFAULT_ADMIN_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20Session) DEFAULTADMINROLE() ([32]byte, error) { + return _MintBurnERC20.Contract.DEFAULTADMINROLE(&_MintBurnERC20.CallOpts) +} + +// DEFAULTADMINROLE is a free data retrieval call binding the contract method 0xa217fddf. +// +// Solidity: function DEFAULT_ADMIN_ROLE() view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20CallerSession) DEFAULTADMINROLE() ([32]byte, error) { + return _MintBurnERC20.Contract.DEFAULTADMINROLE(&_MintBurnERC20.CallOpts) +} + +// MINTERROLE is a free data retrieval call binding the contract method 0xd5391393. +// +// Solidity: function MINTER_ROLE() view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20Caller) MINTERROLE(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "MINTER_ROLE") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// MINTERROLE is a free data retrieval call binding the contract method 0xd5391393. +// +// Solidity: function MINTER_ROLE() view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20Session) MINTERROLE() ([32]byte, error) { + return _MintBurnERC20.Contract.MINTERROLE(&_MintBurnERC20.CallOpts) +} + +// MINTERROLE is a free data retrieval call binding the contract method 0xd5391393. +// +// Solidity: function MINTER_ROLE() view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20CallerSession) MINTERROLE() ([32]byte, error) { + return _MintBurnERC20.Contract.MINTERROLE(&_MintBurnERC20.CallOpts) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20Caller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "allowance", owner, spender) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20Session) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _MintBurnERC20.Contract.Allowance(&_MintBurnERC20.CallOpts, owner, spender) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20CallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _MintBurnERC20.Contract.Allowance(&_MintBurnERC20.CallOpts, owner, spender) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20Caller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "balanceOf", account) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20Session) BalanceOf(account common.Address) (*big.Int, error) { + return _MintBurnERC20.Contract.BalanceOf(&_MintBurnERC20.CallOpts, account) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20CallerSession) BalanceOf(account common.Address) (*big.Int, error) { + return _MintBurnERC20.Contract.BalanceOf(&_MintBurnERC20.CallOpts, account) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_MintBurnERC20 *MintBurnERC20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_MintBurnERC20 *MintBurnERC20Session) Decimals() (uint8, error) { + return _MintBurnERC20.Contract.Decimals(&_MintBurnERC20.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_MintBurnERC20 *MintBurnERC20CallerSession) Decimals() (uint8, error) { + return _MintBurnERC20.Contract.Decimals(&_MintBurnERC20.CallOpts) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20Caller) GetRoleAdmin(opts *bind.CallOpts, role [32]byte) ([32]byte, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "getRoleAdmin", role) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20Session) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _MintBurnERC20.Contract.GetRoleAdmin(&_MintBurnERC20.CallOpts, role) +} + +// GetRoleAdmin is a free data retrieval call binding the contract method 0x248a9ca3. +// +// Solidity: function getRoleAdmin(bytes32 role) view returns(bytes32) +func (_MintBurnERC20 *MintBurnERC20CallerSession) GetRoleAdmin(role [32]byte) ([32]byte, error) { + return _MintBurnERC20.Contract.GetRoleAdmin(&_MintBurnERC20.CallOpts, role) +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_MintBurnERC20 *MintBurnERC20Caller) HasRole(opts *bind.CallOpts, role [32]byte, account common.Address) (bool, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "hasRole", role, account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_MintBurnERC20 *MintBurnERC20Session) HasRole(role [32]byte, account common.Address) (bool, error) { + return _MintBurnERC20.Contract.HasRole(&_MintBurnERC20.CallOpts, role, account) +} + +// HasRole is a free data retrieval call binding the contract method 0x91d14854. +// +// Solidity: function hasRole(bytes32 role, address account) view returns(bool) +func (_MintBurnERC20 *MintBurnERC20CallerSession) HasRole(role [32]byte, account common.Address) (bool, error) { + return _MintBurnERC20.Contract.HasRole(&_MintBurnERC20.CallOpts, role, account) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_MintBurnERC20 *MintBurnERC20Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_MintBurnERC20 *MintBurnERC20Session) Name() (string, error) { + return _MintBurnERC20.Contract.Name(&_MintBurnERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_MintBurnERC20 *MintBurnERC20CallerSession) Name() (string, error) { + return _MintBurnERC20.Contract.Name(&_MintBurnERC20.CallOpts) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_MintBurnERC20 *MintBurnERC20Caller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_MintBurnERC20 *MintBurnERC20Session) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _MintBurnERC20.Contract.SupportsInterface(&_MintBurnERC20.CallOpts, interfaceId) +} + +// SupportsInterface is a free data retrieval call binding the contract method 0x01ffc9a7. +// +// Solidity: function supportsInterface(bytes4 interfaceId) view returns(bool) +func (_MintBurnERC20 *MintBurnERC20CallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _MintBurnERC20.Contract.SupportsInterface(&_MintBurnERC20.CallOpts, interfaceId) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_MintBurnERC20 *MintBurnERC20Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_MintBurnERC20 *MintBurnERC20Session) Symbol() (string, error) { + return _MintBurnERC20.Contract.Symbol(&_MintBurnERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_MintBurnERC20 *MintBurnERC20CallerSession) Symbol() (string, error) { + return _MintBurnERC20.Contract.Symbol(&_MintBurnERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _MintBurnERC20.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20Session) TotalSupply() (*big.Int, error) { + return _MintBurnERC20.Contract.TotalSupply(&_MintBurnERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_MintBurnERC20 *MintBurnERC20CallerSession) TotalSupply() (*big.Int, error) { + return _MintBurnERC20.Contract.TotalSupply(&_MintBurnERC20.CallOpts) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "approve", spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20Session) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Approve(&_MintBurnERC20.TransactOpts, spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20TransactorSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Approve(&_MintBurnERC20.TransactOpts, spender, value) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 value) returns() +func (_MintBurnERC20 *MintBurnERC20Transactor) Burn(opts *bind.TransactOpts, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "burn", value) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 value) returns() +func (_MintBurnERC20 *MintBurnERC20Session) Burn(value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Burn(&_MintBurnERC20.TransactOpts, value) +} + +// Burn is a paid mutator transaction binding the contract method 0x42966c68. +// +// Solidity: function burn(uint256 value) returns() +func (_MintBurnERC20 *MintBurnERC20TransactorSession) Burn(value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Burn(&_MintBurnERC20.TransactOpts, value) +} + +// BurnFrom is a paid mutator transaction binding the contract method 0x79cc6790. +// +// Solidity: function burnFrom(address account, uint256 value) returns() +func (_MintBurnERC20 *MintBurnERC20Transactor) BurnFrom(opts *bind.TransactOpts, account common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "burnFrom", account, value) +} + +// BurnFrom is a paid mutator transaction binding the contract method 0x79cc6790. +// +// Solidity: function burnFrom(address account, uint256 value) returns() +func (_MintBurnERC20 *MintBurnERC20Session) BurnFrom(account common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.BurnFrom(&_MintBurnERC20.TransactOpts, account, value) +} + +// BurnFrom is a paid mutator transaction binding the contract method 0x79cc6790. +// +// Solidity: function burnFrom(address account, uint256 value) returns() +func (_MintBurnERC20 *MintBurnERC20TransactorSession) BurnFrom(account common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.BurnFrom(&_MintBurnERC20.TransactOpts, account, value) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_MintBurnERC20 *MintBurnERC20Transactor) GrantRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "grantRole", role, account) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_MintBurnERC20 *MintBurnERC20Session) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _MintBurnERC20.Contract.GrantRole(&_MintBurnERC20.TransactOpts, role, account) +} + +// GrantRole is a paid mutator transaction binding the contract method 0x2f2ff15d. +// +// Solidity: function grantRole(bytes32 role, address account) returns() +func (_MintBurnERC20 *MintBurnERC20TransactorSession) GrantRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _MintBurnERC20.Contract.GrantRole(&_MintBurnERC20.TransactOpts, role, account) +} + +// Initialize is a paid mutator transaction binding the contract method 0x1624f6c6. +// +// Solidity: function initialize(string name, string symbol, uint8 providedNumDecimals) returns() +func (_MintBurnERC20 *MintBurnERC20Transactor) Initialize(opts *bind.TransactOpts, name string, symbol string, providedNumDecimals uint8) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "initialize", name, symbol, providedNumDecimals) +} + +// Initialize is a paid mutator transaction binding the contract method 0x1624f6c6. +// +// Solidity: function initialize(string name, string symbol, uint8 providedNumDecimals) returns() +func (_MintBurnERC20 *MintBurnERC20Session) Initialize(name string, symbol string, providedNumDecimals uint8) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Initialize(&_MintBurnERC20.TransactOpts, name, symbol, providedNumDecimals) +} + +// Initialize is a paid mutator transaction binding the contract method 0x1624f6c6. +// +// Solidity: function initialize(string name, string symbol, uint8 providedNumDecimals) returns() +func (_MintBurnERC20 *MintBurnERC20TransactorSession) Initialize(name string, symbol string, providedNumDecimals uint8) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Initialize(&_MintBurnERC20.TransactOpts, name, symbol, providedNumDecimals) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_MintBurnERC20 *MintBurnERC20Transactor) Mint(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "mint", to, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_MintBurnERC20 *MintBurnERC20Session) Mint(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Mint(&_MintBurnERC20.TransactOpts, to, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_MintBurnERC20 *MintBurnERC20TransactorSession) Mint(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Mint(&_MintBurnERC20.TransactOpts, to, amount) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address callerConfirmation) returns() +func (_MintBurnERC20 *MintBurnERC20Transactor) RenounceRole(opts *bind.TransactOpts, role [32]byte, callerConfirmation common.Address) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "renounceRole", role, callerConfirmation) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address callerConfirmation) returns() +func (_MintBurnERC20 *MintBurnERC20Session) RenounceRole(role [32]byte, callerConfirmation common.Address) (*types.Transaction, error) { + return _MintBurnERC20.Contract.RenounceRole(&_MintBurnERC20.TransactOpts, role, callerConfirmation) +} + +// RenounceRole is a paid mutator transaction binding the contract method 0x36568abe. +// +// Solidity: function renounceRole(bytes32 role, address callerConfirmation) returns() +func (_MintBurnERC20 *MintBurnERC20TransactorSession) RenounceRole(role [32]byte, callerConfirmation common.Address) (*types.Transaction, error) { + return _MintBurnERC20.Contract.RenounceRole(&_MintBurnERC20.TransactOpts, role, callerConfirmation) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_MintBurnERC20 *MintBurnERC20Transactor) RevokeRole(opts *bind.TransactOpts, role [32]byte, account common.Address) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "revokeRole", role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_MintBurnERC20 *MintBurnERC20Session) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _MintBurnERC20.Contract.RevokeRole(&_MintBurnERC20.TransactOpts, role, account) +} + +// RevokeRole is a paid mutator transaction binding the contract method 0xd547741f. +// +// Solidity: function revokeRole(bytes32 role, address account) returns() +func (_MintBurnERC20 *MintBurnERC20TransactorSession) RevokeRole(role [32]byte, account common.Address) (*types.Transaction, error) { + return _MintBurnERC20.Contract.RevokeRole(&_MintBurnERC20.TransactOpts, role, account) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20Transactor) Transfer(opts *bind.TransactOpts, to common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "transfer", to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20Session) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Transfer(&_MintBurnERC20.TransactOpts, to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20TransactorSession) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.Transfer(&_MintBurnERC20.TransactOpts, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.contract.Transact(opts, "transferFrom", from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20Session) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.TransferFrom(&_MintBurnERC20.TransactOpts, from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_MintBurnERC20 *MintBurnERC20TransactorSession) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _MintBurnERC20.Contract.TransferFrom(&_MintBurnERC20.TransactOpts, from, to, value) +} + +// MintBurnERC20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the MintBurnERC20 contract. +type MintBurnERC20ApprovalIterator struct { + Event *MintBurnERC20Approval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *MintBurnERC20ApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *MintBurnERC20ApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *MintBurnERC20ApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// MintBurnERC20Approval represents a Approval event raised by the MintBurnERC20 contract. +type MintBurnERC20Approval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_MintBurnERC20 *MintBurnERC20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*MintBurnERC20ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _MintBurnERC20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &MintBurnERC20ApprovalIterator{contract: _MintBurnERC20.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_MintBurnERC20 *MintBurnERC20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *MintBurnERC20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _MintBurnERC20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(MintBurnERC20Approval) + if err := _MintBurnERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_MintBurnERC20 *MintBurnERC20Filterer) ParseApproval(log types.Log) (*MintBurnERC20Approval, error) { + event := new(MintBurnERC20Approval) + if err := _MintBurnERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// MintBurnERC20InitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the MintBurnERC20 contract. +type MintBurnERC20InitializedIterator struct { + Event *MintBurnERC20Initialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *MintBurnERC20InitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20Initialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20Initialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *MintBurnERC20InitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *MintBurnERC20InitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// MintBurnERC20Initialized represents a Initialized event raised by the MintBurnERC20 contract. +type MintBurnERC20Initialized struct { + Version uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_MintBurnERC20 *MintBurnERC20Filterer) FilterInitialized(opts *bind.FilterOpts) (*MintBurnERC20InitializedIterator, error) { + + logs, sub, err := _MintBurnERC20.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &MintBurnERC20InitializedIterator{contract: _MintBurnERC20.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_MintBurnERC20 *MintBurnERC20Filterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *MintBurnERC20Initialized) (event.Subscription, error) { + + logs, sub, err := _MintBurnERC20.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(MintBurnERC20Initialized) + if err := _MintBurnERC20.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_MintBurnERC20 *MintBurnERC20Filterer) ParseInitialized(log types.Log) (*MintBurnERC20Initialized, error) { + event := new(MintBurnERC20Initialized) + if err := _MintBurnERC20.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// MintBurnERC20RoleAdminChangedIterator is returned from FilterRoleAdminChanged and is used to iterate over the raw logs and unpacked data for RoleAdminChanged events raised by the MintBurnERC20 contract. +type MintBurnERC20RoleAdminChangedIterator struct { + Event *MintBurnERC20RoleAdminChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *MintBurnERC20RoleAdminChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20RoleAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20RoleAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *MintBurnERC20RoleAdminChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *MintBurnERC20RoleAdminChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// MintBurnERC20RoleAdminChanged represents a RoleAdminChanged event raised by the MintBurnERC20 contract. +type MintBurnERC20RoleAdminChanged struct { + Role [32]byte + PreviousAdminRole [32]byte + NewAdminRole [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleAdminChanged is a free log retrieval operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_MintBurnERC20 *MintBurnERC20Filterer) FilterRoleAdminChanged(opts *bind.FilterOpts, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (*MintBurnERC20RoleAdminChangedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _MintBurnERC20.contract.FilterLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + if err != nil { + return nil, err + } + return &MintBurnERC20RoleAdminChangedIterator{contract: _MintBurnERC20.contract, event: "RoleAdminChanged", logs: logs, sub: sub}, nil +} + +// WatchRoleAdminChanged is a free log subscription operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_MintBurnERC20 *MintBurnERC20Filterer) WatchRoleAdminChanged(opts *bind.WatchOpts, sink chan<- *MintBurnERC20RoleAdminChanged, role [][32]byte, previousAdminRole [][32]byte, newAdminRole [][32]byte) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var previousAdminRoleRule []interface{} + for _, previousAdminRoleItem := range previousAdminRole { + previousAdminRoleRule = append(previousAdminRoleRule, previousAdminRoleItem) + } + var newAdminRoleRule []interface{} + for _, newAdminRoleItem := range newAdminRole { + newAdminRoleRule = append(newAdminRoleRule, newAdminRoleItem) + } + + logs, sub, err := _MintBurnERC20.contract.WatchLogs(opts, "RoleAdminChanged", roleRule, previousAdminRoleRule, newAdminRoleRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(MintBurnERC20RoleAdminChanged) + if err := _MintBurnERC20.contract.UnpackLog(event, "RoleAdminChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRoleAdminChanged is a log parse operation binding the contract event 0xbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff. +// +// Solidity: event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole) +func (_MintBurnERC20 *MintBurnERC20Filterer) ParseRoleAdminChanged(log types.Log) (*MintBurnERC20RoleAdminChanged, error) { + event := new(MintBurnERC20RoleAdminChanged) + if err := _MintBurnERC20.contract.UnpackLog(event, "RoleAdminChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// MintBurnERC20RoleGrantedIterator is returned from FilterRoleGranted and is used to iterate over the raw logs and unpacked data for RoleGranted events raised by the MintBurnERC20 contract. +type MintBurnERC20RoleGrantedIterator struct { + Event *MintBurnERC20RoleGranted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *MintBurnERC20RoleGrantedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20RoleGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20RoleGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *MintBurnERC20RoleGrantedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *MintBurnERC20RoleGrantedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// MintBurnERC20RoleGranted represents a RoleGranted event raised by the MintBurnERC20 contract. +type MintBurnERC20RoleGranted struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleGranted is a free log retrieval operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_MintBurnERC20 *MintBurnERC20Filterer) FilterRoleGranted(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*MintBurnERC20RoleGrantedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _MintBurnERC20.contract.FilterLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return &MintBurnERC20RoleGrantedIterator{contract: _MintBurnERC20.contract, event: "RoleGranted", logs: logs, sub: sub}, nil +} + +// WatchRoleGranted is a free log subscription operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_MintBurnERC20 *MintBurnERC20Filterer) WatchRoleGranted(opts *bind.WatchOpts, sink chan<- *MintBurnERC20RoleGranted, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _MintBurnERC20.contract.WatchLogs(opts, "RoleGranted", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(MintBurnERC20RoleGranted) + if err := _MintBurnERC20.contract.UnpackLog(event, "RoleGranted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRoleGranted is a log parse operation binding the contract event 0x2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d. +// +// Solidity: event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender) +func (_MintBurnERC20 *MintBurnERC20Filterer) ParseRoleGranted(log types.Log) (*MintBurnERC20RoleGranted, error) { + event := new(MintBurnERC20RoleGranted) + if err := _MintBurnERC20.contract.UnpackLog(event, "RoleGranted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// MintBurnERC20RoleRevokedIterator is returned from FilterRoleRevoked and is used to iterate over the raw logs and unpacked data for RoleRevoked events raised by the MintBurnERC20 contract. +type MintBurnERC20RoleRevokedIterator struct { + Event *MintBurnERC20RoleRevoked // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *MintBurnERC20RoleRevokedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20RoleRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20RoleRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *MintBurnERC20RoleRevokedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *MintBurnERC20RoleRevokedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// MintBurnERC20RoleRevoked represents a RoleRevoked event raised by the MintBurnERC20 contract. +type MintBurnERC20RoleRevoked struct { + Role [32]byte + Account common.Address + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRoleRevoked is a free log retrieval operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_MintBurnERC20 *MintBurnERC20Filterer) FilterRoleRevoked(opts *bind.FilterOpts, role [][32]byte, account []common.Address, sender []common.Address) (*MintBurnERC20RoleRevokedIterator, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _MintBurnERC20.contract.FilterLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return &MintBurnERC20RoleRevokedIterator{contract: _MintBurnERC20.contract, event: "RoleRevoked", logs: logs, sub: sub}, nil +} + +// WatchRoleRevoked is a free log subscription operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_MintBurnERC20 *MintBurnERC20Filterer) WatchRoleRevoked(opts *bind.WatchOpts, sink chan<- *MintBurnERC20RoleRevoked, role [][32]byte, account []common.Address, sender []common.Address) (event.Subscription, error) { + + var roleRule []interface{} + for _, roleItem := range role { + roleRule = append(roleRule, roleItem) + } + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + + logs, sub, err := _MintBurnERC20.contract.WatchLogs(opts, "RoleRevoked", roleRule, accountRule, senderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(MintBurnERC20RoleRevoked) + if err := _MintBurnERC20.contract.UnpackLog(event, "RoleRevoked", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRoleRevoked is a log parse operation binding the contract event 0xf6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b. +// +// Solidity: event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender) +func (_MintBurnERC20 *MintBurnERC20Filterer) ParseRoleRevoked(log types.Log) (*MintBurnERC20RoleRevoked, error) { + event := new(MintBurnERC20RoleRevoked) + if err := _MintBurnERC20.contract.UnpackLog(event, "RoleRevoked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// MintBurnERC20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the MintBurnERC20 contract. +type MintBurnERC20TransferIterator struct { + Event *MintBurnERC20Transfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *MintBurnERC20TransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(MintBurnERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *MintBurnERC20TransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *MintBurnERC20TransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// MintBurnERC20Transfer represents a Transfer event raised by the MintBurnERC20 contract. +type MintBurnERC20Transfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_MintBurnERC20 *MintBurnERC20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*MintBurnERC20TransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _MintBurnERC20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &MintBurnERC20TransferIterator{contract: _MintBurnERC20.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_MintBurnERC20 *MintBurnERC20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *MintBurnERC20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _MintBurnERC20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(MintBurnERC20Transfer) + if err := _MintBurnERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_MintBurnERC20 *MintBurnERC20Filterer) ParseTransfer(log types.Log) (*MintBurnERC20Transfer, error) { + event := new(MintBurnERC20Transfer) + if err := _MintBurnERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/clients/ethereum/contract/genericErc20.go b/clients/ethereum/contract/genericErc20.go deleted file mode 100644 index 1819e764..00000000 --- a/clients/ethereum/contract/genericErc20.go +++ /dev/null @@ -1,800 +0,0 @@ -// Code generated - DO NOT EDIT. -// This file is a generated binding and any manual changes will be lost. - -package contract - -import ( - "errors" - "math/big" - "strings" - - ethereum "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/event" -) - -// Reference imports to suppress errors if they are not otherwise used. -var ( - _ = errors.New - _ = big.NewInt - _ = strings.NewReader - _ = ethereum.NotFound - _ = bind.Bind - _ = common.Big1 - _ = types.BloomLookup - _ = event.NewSubscription -) - -// GenericErc20MetaData contains all meta data concerning the GenericErc20 contract. -var GenericErc20MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenSymbol\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"}],\"name\":\"brrr\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", -} - -// GenericErc20ABI is the input ABI used to generate the binding from. -// Deprecated: Use GenericErc20MetaData.ABI instead. -var GenericErc20ABI = GenericErc20MetaData.ABI - -// GenericErc20 is an auto generated Go binding around an Ethereum contract. -type GenericErc20 struct { - GenericErc20Caller // Read-only binding to the contract - GenericErc20Transactor // Write-only binding to the contract - GenericErc20Filterer // Log filterer for contract events -} - -// GenericErc20Caller is an auto generated read-only Go binding around an Ethereum contract. -type GenericErc20Caller struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// GenericErc20Transactor is an auto generated write-only Go binding around an Ethereum contract. -type GenericErc20Transactor struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// GenericErc20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. -type GenericErc20Filterer struct { - contract *bind.BoundContract // Generic contract wrapper for the low level calls -} - -// GenericErc20Session is an auto generated Go binding around an Ethereum contract, -// with pre-set call and transact options. -type GenericErc20Session struct { - Contract *GenericErc20 // Generic contract binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// GenericErc20CallerSession is an auto generated read-only Go binding around an Ethereum contract, -// with pre-set call options. -type GenericErc20CallerSession struct { - Contract *GenericErc20Caller // Generic contract caller binding to set the session for - CallOpts bind.CallOpts // Call options to use throughout this session -} - -// GenericErc20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, -// with pre-set transact options. -type GenericErc20TransactorSession struct { - Contract *GenericErc20Transactor // Generic contract transactor binding to set the session for - TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session -} - -// GenericErc20Raw is an auto generated low-level Go binding around an Ethereum contract. -type GenericErc20Raw struct { - Contract *GenericErc20 // Generic contract binding to access the raw methods on -} - -// GenericErc20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. -type GenericErc20CallerRaw struct { - Contract *GenericErc20Caller // Generic read-only contract binding to access the raw methods on -} - -// GenericErc20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. -type GenericErc20TransactorRaw struct { - Contract *GenericErc20Transactor // Generic write-only contract binding to access the raw methods on -} - -// NewGenericErc20 creates a new instance of GenericErc20, bound to a specific deployed contract. -func NewGenericErc20(address common.Address, backend bind.ContractBackend) (*GenericErc20, error) { - contract, err := bindGenericErc20(address, backend, backend, backend) - if err != nil { - return nil, err - } - return &GenericErc20{GenericErc20Caller: GenericErc20Caller{contract: contract}, GenericErc20Transactor: GenericErc20Transactor{contract: contract}, GenericErc20Filterer: GenericErc20Filterer{contract: contract}}, nil -} - -// NewGenericErc20Caller creates a new read-only instance of GenericErc20, bound to a specific deployed contract. -func NewGenericErc20Caller(address common.Address, caller bind.ContractCaller) (*GenericErc20Caller, error) { - contract, err := bindGenericErc20(address, caller, nil, nil) - if err != nil { - return nil, err - } - return &GenericErc20Caller{contract: contract}, nil -} - -// NewGenericErc20Transactor creates a new write-only instance of GenericErc20, bound to a specific deployed contract. -func NewGenericErc20Transactor(address common.Address, transactor bind.ContractTransactor) (*GenericErc20Transactor, error) { - contract, err := bindGenericErc20(address, nil, transactor, nil) - if err != nil { - return nil, err - } - return &GenericErc20Transactor{contract: contract}, nil -} - -// NewGenericErc20Filterer creates a new log filterer instance of GenericErc20, bound to a specific deployed contract. -func NewGenericErc20Filterer(address common.Address, filterer bind.ContractFilterer) (*GenericErc20Filterer, error) { - contract, err := bindGenericErc20(address, nil, nil, filterer) - if err != nil { - return nil, err - } - return &GenericErc20Filterer{contract: contract}, nil -} - -// bindGenericErc20 binds a generic wrapper to an already deployed contract. -func bindGenericErc20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(GenericErc20ABI)) - if err != nil { - return nil, err - } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_GenericErc20 *GenericErc20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _GenericErc20.Contract.GenericErc20Caller.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_GenericErc20 *GenericErc20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _GenericErc20.Contract.GenericErc20Transactor.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_GenericErc20 *GenericErc20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _GenericErc20.Contract.GenericErc20Transactor.contract.Transact(opts, method, params...) -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. The result type might be a single field for simple -// returns, a slice of interfaces for anonymous returns and a struct for named -// returns. -func (_GenericErc20 *GenericErc20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { - return _GenericErc20.Contract.contract.Call(opts, result, method, params...) -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (_GenericErc20 *GenericErc20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { - return _GenericErc20.Contract.contract.Transfer(opts) -} - -// Transact invokes the (paid) contract method with params as input values. -func (_GenericErc20 *GenericErc20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { - return _GenericErc20.Contract.contract.Transact(opts, method, params...) -} - -// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. -// -// Solidity: function allowance(address owner, address spender) view returns(uint256) -func (_GenericErc20 *GenericErc20Caller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { - var out []interface{} - err := _GenericErc20.contract.Call(opts, &out, "allowance", owner, spender) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. -// -// Solidity: function allowance(address owner, address spender) view returns(uint256) -func (_GenericErc20 *GenericErc20Session) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { - return _GenericErc20.Contract.Allowance(&_GenericErc20.CallOpts, owner, spender) -} - -// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. -// -// Solidity: function allowance(address owner, address spender) view returns(uint256) -func (_GenericErc20 *GenericErc20CallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { - return _GenericErc20.Contract.Allowance(&_GenericErc20.CallOpts, owner, spender) -} - -// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. -// -// Solidity: function balanceOf(address account) view returns(uint256) -func (_GenericErc20 *GenericErc20Caller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { - var out []interface{} - err := _GenericErc20.contract.Call(opts, &out, "balanceOf", account) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. -// -// Solidity: function balanceOf(address account) view returns(uint256) -func (_GenericErc20 *GenericErc20Session) BalanceOf(account common.Address) (*big.Int, error) { - return _GenericErc20.Contract.BalanceOf(&_GenericErc20.CallOpts, account) -} - -// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. -// -// Solidity: function balanceOf(address account) view returns(uint256) -func (_GenericErc20 *GenericErc20CallerSession) BalanceOf(account common.Address) (*big.Int, error) { - return _GenericErc20.Contract.BalanceOf(&_GenericErc20.CallOpts, account) -} - -// Decimals is a free data retrieval call binding the contract method 0x313ce567. -// -// Solidity: function decimals() view returns(uint8) -func (_GenericErc20 *GenericErc20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { - var out []interface{} - err := _GenericErc20.contract.Call(opts, &out, "decimals") - - if err != nil { - return *new(uint8), err - } - - out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) - - return out0, err - -} - -// Decimals is a free data retrieval call binding the contract method 0x313ce567. -// -// Solidity: function decimals() view returns(uint8) -func (_GenericErc20 *GenericErc20Session) Decimals() (uint8, error) { - return _GenericErc20.Contract.Decimals(&_GenericErc20.CallOpts) -} - -// Decimals is a free data retrieval call binding the contract method 0x313ce567. -// -// Solidity: function decimals() view returns(uint8) -func (_GenericErc20 *GenericErc20CallerSession) Decimals() (uint8, error) { - return _GenericErc20.Contract.Decimals(&_GenericErc20.CallOpts) -} - -// Name is a free data retrieval call binding the contract method 0x06fdde03. -// -// Solidity: function name() view returns(string) -func (_GenericErc20 *GenericErc20Caller) Name(opts *bind.CallOpts) (string, error) { - var out []interface{} - err := _GenericErc20.contract.Call(opts, &out, "name") - - if err != nil { - return *new(string), err - } - - out0 := *abi.ConvertType(out[0], new(string)).(*string) - - return out0, err - -} - -// Name is a free data retrieval call binding the contract method 0x06fdde03. -// -// Solidity: function name() view returns(string) -func (_GenericErc20 *GenericErc20Session) Name() (string, error) { - return _GenericErc20.Contract.Name(&_GenericErc20.CallOpts) -} - -// Name is a free data retrieval call binding the contract method 0x06fdde03. -// -// Solidity: function name() view returns(string) -func (_GenericErc20 *GenericErc20CallerSession) Name() (string, error) { - return _GenericErc20.Contract.Name(&_GenericErc20.CallOpts) -} - -// Symbol is a free data retrieval call binding the contract method 0x95d89b41. -// -// Solidity: function symbol() view returns(string) -func (_GenericErc20 *GenericErc20Caller) Symbol(opts *bind.CallOpts) (string, error) { - var out []interface{} - err := _GenericErc20.contract.Call(opts, &out, "symbol") - - if err != nil { - return *new(string), err - } - - out0 := *abi.ConvertType(out[0], new(string)).(*string) - - return out0, err - -} - -// Symbol is a free data retrieval call binding the contract method 0x95d89b41. -// -// Solidity: function symbol() view returns(string) -func (_GenericErc20 *GenericErc20Session) Symbol() (string, error) { - return _GenericErc20.Contract.Symbol(&_GenericErc20.CallOpts) -} - -// Symbol is a free data retrieval call binding the contract method 0x95d89b41. -// -// Solidity: function symbol() view returns(string) -func (_GenericErc20 *GenericErc20CallerSession) Symbol() (string, error) { - return _GenericErc20.Contract.Symbol(&_GenericErc20.CallOpts) -} - -// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. -// -// Solidity: function totalSupply() view returns(uint256) -func (_GenericErc20 *GenericErc20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _GenericErc20.contract.Call(opts, &out, "totalSupply") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. -// -// Solidity: function totalSupply() view returns(uint256) -func (_GenericErc20 *GenericErc20Session) TotalSupply() (*big.Int, error) { - return _GenericErc20.Contract.TotalSupply(&_GenericErc20.CallOpts) -} - -// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. -// -// Solidity: function totalSupply() view returns(uint256) -func (_GenericErc20 *GenericErc20CallerSession) TotalSupply() (*big.Int, error) { - return _GenericErc20.Contract.TotalSupply(&_GenericErc20.CallOpts) -} - -// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. -// -// Solidity: function approve(address spender, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.contract.Transact(opts, "approve", spender, amount) -} - -// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. -// -// Solidity: function approve(address spender, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20Session) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.Approve(&_GenericErc20.TransactOpts, spender, amount) -} - -// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. -// -// Solidity: function approve(address spender, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20TransactorSession) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.Approve(&_GenericErc20.TransactOpts, spender, amount) -} - -// Brrr is a paid mutator transaction binding the contract method 0x52c4cb14. -// -// Solidity: function brrr(address recipientAddress) returns() -func (_GenericErc20 *GenericErc20Transactor) Brrr(opts *bind.TransactOpts, recipientAddress common.Address) (*types.Transaction, error) { - return _GenericErc20.contract.Transact(opts, "brrr", recipientAddress) -} - -// Brrr is a paid mutator transaction binding the contract method 0x52c4cb14. -// -// Solidity: function brrr(address recipientAddress) returns() -func (_GenericErc20 *GenericErc20Session) Brrr(recipientAddress common.Address) (*types.Transaction, error) { - return _GenericErc20.Contract.Brrr(&_GenericErc20.TransactOpts, recipientAddress) -} - -// Brrr is a paid mutator transaction binding the contract method 0x52c4cb14. -// -// Solidity: function brrr(address recipientAddress) returns() -func (_GenericErc20 *GenericErc20TransactorSession) Brrr(recipientAddress common.Address) (*types.Transaction, error) { - return _GenericErc20.Contract.Brrr(&_GenericErc20.TransactOpts, recipientAddress) -} - -// DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. -// -// Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) -func (_GenericErc20 *GenericErc20Transactor) DecreaseAllowance(opts *bind.TransactOpts, spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { - return _GenericErc20.contract.Transact(opts, "decreaseAllowance", spender, subtractedValue) -} - -// DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. -// -// Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) -func (_GenericErc20 *GenericErc20Session) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.DecreaseAllowance(&_GenericErc20.TransactOpts, spender, subtractedValue) -} - -// DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. -// -// Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) -func (_GenericErc20 *GenericErc20TransactorSession) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.DecreaseAllowance(&_GenericErc20.TransactOpts, spender, subtractedValue) -} - -// IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. -// -// Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) -func (_GenericErc20 *GenericErc20Transactor) IncreaseAllowance(opts *bind.TransactOpts, spender common.Address, addedValue *big.Int) (*types.Transaction, error) { - return _GenericErc20.contract.Transact(opts, "increaseAllowance", spender, addedValue) -} - -// IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. -// -// Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) -func (_GenericErc20 *GenericErc20Session) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.IncreaseAllowance(&_GenericErc20.TransactOpts, spender, addedValue) -} - -// IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. -// -// Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) -func (_GenericErc20 *GenericErc20TransactorSession) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.IncreaseAllowance(&_GenericErc20.TransactOpts, spender, addedValue) -} - -// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. -// -// Solidity: function transfer(address recipient, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20Transactor) Transfer(opts *bind.TransactOpts, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.contract.Transact(opts, "transfer", recipient, amount) -} - -// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. -// -// Solidity: function transfer(address recipient, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20Session) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.Transfer(&_GenericErc20.TransactOpts, recipient, amount) -} - -// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. -// -// Solidity: function transfer(address recipient, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20TransactorSession) Transfer(recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.Transfer(&_GenericErc20.TransactOpts, recipient, amount) -} - -// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. -// -// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20Transactor) TransferFrom(opts *bind.TransactOpts, sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.contract.Transact(opts, "transferFrom", sender, recipient, amount) -} - -// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. -// -// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20Session) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.TransferFrom(&_GenericErc20.TransactOpts, sender, recipient, amount) -} - -// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. -// -// Solidity: function transferFrom(address sender, address recipient, uint256 amount) returns(bool) -func (_GenericErc20 *GenericErc20TransactorSession) TransferFrom(sender common.Address, recipient common.Address, amount *big.Int) (*types.Transaction, error) { - return _GenericErc20.Contract.TransferFrom(&_GenericErc20.TransactOpts, sender, recipient, amount) -} - -// GenericErc20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the GenericErc20 contract. -type GenericErc20ApprovalIterator struct { - Event *GenericErc20Approval // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *GenericErc20ApprovalIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(GenericErc20Approval) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(GenericErc20Approval) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *GenericErc20ApprovalIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *GenericErc20ApprovalIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// GenericErc20Approval represents a Approval event raised by the GenericErc20 contract. -type GenericErc20Approval struct { - Owner common.Address - Spender common.Address - Value *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. -// -// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) -func (_GenericErc20 *GenericErc20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*GenericErc20ApprovalIterator, error) { - - var ownerRule []interface{} - for _, ownerItem := range owner { - ownerRule = append(ownerRule, ownerItem) - } - var spenderRule []interface{} - for _, spenderItem := range spender { - spenderRule = append(spenderRule, spenderItem) - } - - logs, sub, err := _GenericErc20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) - if err != nil { - return nil, err - } - return &GenericErc20ApprovalIterator{contract: _GenericErc20.contract, event: "Approval", logs: logs, sub: sub}, nil -} - -// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. -// -// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) -func (_GenericErc20 *GenericErc20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *GenericErc20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { - - var ownerRule []interface{} - for _, ownerItem := range owner { - ownerRule = append(ownerRule, ownerItem) - } - var spenderRule []interface{} - for _, spenderItem := range spender { - spenderRule = append(spenderRule, spenderItem) - } - - logs, sub, err := _GenericErc20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(GenericErc20Approval) - if err := _GenericErc20.contract.UnpackLog(event, "Approval", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. -// -// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) -func (_GenericErc20 *GenericErc20Filterer) ParseApproval(log types.Log) (*GenericErc20Approval, error) { - event := new(GenericErc20Approval) - if err := _GenericErc20.contract.UnpackLog(event, "Approval", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} - -// GenericErc20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the GenericErc20 contract. -type GenericErc20TransferIterator struct { - Event *GenericErc20Transfer // Event containing the contract specifics and raw log - - contract *bind.BoundContract // Generic contract to use for unpacking event data - event string // Event name to use for unpacking event data - - logs chan types.Log // Log channel receiving the found contract events - sub ethereum.Subscription // Subscription for errors, completion and termination - done bool // Whether the subscription completed delivering logs - fail error // Occurred error to stop iteration -} - -// Next advances the iterator to the subsequent event, returning whether there -// are any more events found. In case of a retrieval or parsing error, false is -// returned and Error() can be queried for the exact failure. -func (it *GenericErc20TransferIterator) Next() bool { - // If the iterator failed, stop iterating - if it.fail != nil { - return false - } - // If the iterator completed, deliver directly whatever's available - if it.done { - select { - case log := <-it.logs: - it.Event = new(GenericErc20Transfer) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - default: - return false - } - } - // Iterator still in progress, wait for either a data or an error event - select { - case log := <-it.logs: - it.Event = new(GenericErc20Transfer) - if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { - it.fail = err - return false - } - it.Event.Raw = log - return true - - case err := <-it.sub.Err(): - it.done = true - it.fail = err - return it.Next() - } -} - -// Error returns any retrieval or parsing error occurred during filtering. -func (it *GenericErc20TransferIterator) Error() error { - return it.fail -} - -// Close terminates the iteration process, releasing any pending underlying -// resources. -func (it *GenericErc20TransferIterator) Close() error { - it.sub.Unsubscribe() - return nil -} - -// GenericErc20Transfer represents a Transfer event raised by the GenericErc20 contract. -type GenericErc20Transfer struct { - From common.Address - To common.Address - Value *big.Int - Raw types.Log // Blockchain specific contextual infos -} - -// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. -// -// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) -func (_GenericErc20 *GenericErc20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*GenericErc20TransferIterator, error) { - - var fromRule []interface{} - for _, fromItem := range from { - fromRule = append(fromRule, fromItem) - } - var toRule []interface{} - for _, toItem := range to { - toRule = append(toRule, toItem) - } - - logs, sub, err := _GenericErc20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) - if err != nil { - return nil, err - } - return &GenericErc20TransferIterator{contract: _GenericErc20.contract, event: "Transfer", logs: logs, sub: sub}, nil -} - -// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. -// -// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) -func (_GenericErc20 *GenericErc20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *GenericErc20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { - - var fromRule []interface{} - for _, fromItem := range from { - fromRule = append(fromRule, fromItem) - } - var toRule []interface{} - for _, toItem := range to { - toRule = append(toRule, toItem) - } - - logs, sub, err := _GenericErc20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) - if err != nil { - return nil, err - } - return event.NewSubscription(func(quit <-chan struct{}) error { - defer sub.Unsubscribe() - for { - select { - case log := <-logs: - // New log arrived, parse the event and forward to the user - event := new(GenericErc20Transfer) - if err := _GenericErc20.contract.UnpackLog(event, "Transfer", log); err != nil { - return err - } - event.Raw = log - - select { - case sink <- event: - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - case err := <-sub.Err(): - return err - case <-quit: - return nil - } - } - }), nil -} - -// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. -// -// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) -func (_GenericErc20 *GenericErc20Filterer) ParseTransfer(log types.Log) (*GenericErc20Transfer, error) { - event := new(GenericErc20Transfer) - if err := _GenericErc20.contract.UnpackLog(event, "Transfer", log); err != nil { - return nil, err - } - event.Raw = log - return event, nil -} diff --git a/clients/ethereum/cryptoHandler.go b/clients/ethereum/cryptoHandler.go new file mode 100644 index 00000000..0cc05199 --- /dev/null +++ b/clients/ethereum/cryptoHandler.go @@ -0,0 +1,63 @@ +package ethereum + +import ( + "crypto/ecdsa" + "math/big" + "os" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/multiversx/mx-bridge-eth-go/core/converters" +) + +type cryptoHandler struct { + privateKey *ecdsa.PrivateKey + publicKey *ecdsa.PublicKey + address common.Address +} + +// NewCryptoHandler creates a new instance of type cryptoHandler able to sign messages and provide the containing public key +func NewCryptoHandler(privateKeyFilename string) (*cryptoHandler, error) { + privateKeyBytes, err := os.ReadFile(privateKeyFilename) + if err != nil { + return nil, err + } + privateKeyString := converters.TrimWhiteSpaceCharacters(string(privateKeyBytes)) + privateKey, err := ethCrypto.HexToECDSA(privateKeyString) + if err != nil { + return nil, err + } + + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, errPublicKeyCast + } + + return &cryptoHandler{ + privateKey: privateKey, + publicKey: publicKeyECDSA, + address: ethCrypto.PubkeyToAddress(*publicKeyECDSA), + }, nil +} + +// Sign signs the provided message hash with the containing private key +func (handler *cryptoHandler) Sign(msgHash common.Hash) ([]byte, error) { + return ethCrypto.Sign(msgHash.Bytes(), handler.privateKey) +} + +// GetAddress returns the corresponding address of the containing public key +func (handler *cryptoHandler) GetAddress() common.Address { + return handler.address +} + +// CreateKeyedTransactor creates a keyed transactor used to create transactions on Ethereum chain +func (handler *cryptoHandler) CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) { + return bind.NewKeyedTransactorWithChainID(handler.privateKey, chainId) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (handler *cryptoHandler) IsInterfaceNil() bool { + return handler == nil +} diff --git a/clients/ethereum/cryptoHandler_test.go b/clients/ethereum/cryptoHandler_test.go new file mode 100644 index 00000000..79d8c99f --- /dev/null +++ b/clients/ethereum/cryptoHandler_test.go @@ -0,0 +1,102 @@ +package ethereum + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +func TestNewCryptoHandler(t *testing.T) { + t.Parallel() + + t.Run("invalid file should error", func(t *testing.T) { + t.Parallel() + + handler, err := NewCryptoHandler("missing file") + assert.Nil(t, handler) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "open missing file: no such file or directory") + }) + t.Run("invalid private key file", func(t *testing.T) { + t.Parallel() + + handler, err := NewCryptoHandler("./testdata/nok-ethereum-key") + assert.Nil(t, handler) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid hex data for private key") + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + handler, err := NewCryptoHandler("./testdata/ok-ethereum-key") + assert.NotNil(t, handler) + assert.Nil(t, err) + }) +} + +func TestCryptoHandler_IsInterfaceNil(t *testing.T) { + t.Parallel() + + var instance *cryptoHandler + assert.True(t, instance.IsInterfaceNil()) + + instance = &cryptoHandler{} + assert.False(t, instance.IsInterfaceNil()) +} + +func TestCryptoHandler_Sign(t *testing.T) { + t.Parallel() + + t.Run("test 1", func(t *testing.T) { + expectedSig := "b556014dd984183e4662dc3204e522a5a92093fd6f64bb2da9c1b66b8d5ad12d774e05728b83c76bf09bb91af93ede4118f59aa949c7d02c86051dd0fa140c9900" + msgHash := common.HexToHash("c99286352d865e33f1747761cbd440a7906b9bd8a5261cb6909e5ba18dd19b08") + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + sig, err := handler.Sign(msgHash) + assert.Nil(t, err) + assert.Equal(t, expectedSig, hex.EncodeToString(sig)) + }) + t.Run("test 2", func(t *testing.T) { + expectedSig := "9abff5ecad356a82855f3ecc816cad5d19315ab812f1affeed7f8020accf01127d4c41ed56ff1b3053b64957a19aa1c6fd7dd1b5aa53065b0df231f517bfe89f01" + msgHash := common.HexToHash("c99286352d865e33f1747761cbd440a7906b9bd8a5261cb6909e5ba18dd19b09") + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + sig, err := handler.Sign(msgHash) + assert.Nil(t, err) + assert.Equal(t, expectedSig, hex.EncodeToString(sig)) + }) +} + +func TestCryptoHandler_GetAddress(t *testing.T) { + t.Parallel() + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + expectedAddress := common.HexToAddress("0x3FE464Ac5aa562F7948322F92020F2b668D543d8") + + assert.Equal(t, expectedAddress, handler.GetAddress()) +} + +func TestCryptoHandler_CreateKeyedTransactor(t *testing.T) { + t.Parallel() + + t.Run("nil chain ID should error", func(t *testing.T) { + t.Parallel() + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + opts, err := handler.CreateKeyedTransactor(nil) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "no chain id specified") + assert.Nil(t, opts) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + handler, _ := NewCryptoHandler("./testdata/ok-ethereum-key") + opts, err := handler.CreateKeyedTransactor(big.NewInt(1)) + assert.Nil(t, err) + assert.NotNil(t, opts) + }) +} diff --git a/clients/ethereum/erc20ContractsHolder.go b/clients/ethereum/erc20ContractsHolder.go index 4df5e27e..3eaf99c1 100644 --- a/clients/ethereum/erc20ContractsHolder.go +++ b/clients/ethereum/erc20ContractsHolder.go @@ -45,14 +45,23 @@ func NewErc20SafeContractsHolder(args ArgsErc20SafeContractsHolder) (*erc20SafeC } // BalanceOf returns the ERC20 balance of the provided address -// if the ERC20 contract does not exists in the map of contract wrappers, it will create and add it first +// if the ERC20 contract does not exist in the map of contract wrappers, it will create and add it first func (h *erc20SafeContractsHolder) BalanceOf(ctx context.Context, erc20Address ethCommon.Address, address ethCommon.Address) (*big.Int, error) { + wrapper, err := h.getOrCreateWrapper(erc20Address) + if err != nil { + return nil, err + } + + return wrapper.BalanceOf(ctx, address) +} + +func (h *erc20SafeContractsHolder) getOrCreateWrapper(erc20Address ethCommon.Address) (erc20ContractWrapper, error) { h.mut.Lock() defer h.mut.Unlock() wrapper, exists := h.contracts[erc20Address] if !exists { - contractInstance, err := contract.NewGenericErc20(erc20Address, h.ethClient) + contractInstance, err := contract.NewGenericERC20(erc20Address, h.ethClient) if err != nil { return nil, fmt.Errorf("%w for %s", err, erc20Address.String()) } @@ -68,7 +77,18 @@ func (h *erc20SafeContractsHolder) BalanceOf(ctx context.Context, erc20Address e h.contracts[erc20Address] = wrapper } - return wrapper.BalanceOf(ctx, address) + return wrapper, nil +} + +// Decimals returns the ERC20 decimals for the current ERC20 contract +// if the ERC20 contract does not exist in the map of contract wrappers, it will create and add it first +func (h *erc20SafeContractsHolder) Decimals(ctx context.Context, erc20Address ethCommon.Address) (uint8, error) { + wrapper, err := h.getOrCreateWrapper(erc20Address) + if err != nil { + return 0, err + } + + return wrapper.Decimals(ctx) } // IsInterfaceNil returns true if there is no value under the interface diff --git a/clients/ethereum/erc20ContractsHolder_test.go b/clients/ethereum/erc20ContractsHolder_test.go index 81ccc011..e1858ce7 100644 --- a/clients/ethereum/erc20ContractsHolder_test.go +++ b/clients/ethereum/erc20ContractsHolder_test.go @@ -53,10 +53,10 @@ func TestNewErc20SafeContractsHolder(t *testing.T) { }) } -func TestBalanceOf(t *testing.T) { +func TestErc20SafeContractsHolder_BalanceOf(t *testing.T) { t.Parallel() - t.Run("address does not exists on map nor blockchain", func(t *testing.T) { + t.Run("address does not exist on map nor blockchain", func(t *testing.T) { expectedError := errors.New("no contract code at given address") args := createMockArgsContractsHolder() args.EthClient = &bridgeTests.ContractBackendStub{ @@ -109,7 +109,7 @@ func TestBalanceOf(t *testing.T) { assert.Equal(t, 0, len(ch.contracts)) result, err := ch.BalanceOf(context.Background(), contractAddress, address1) - // first time the contract does not exists in the map, so it should add it + // first time the contract does not exist in the map, so it should add it assert.Nil(t, err) assert.Equal(t, big.NewInt(returnedBalance), result) assert.Equal(t, 1, len(ch.contracts)) @@ -121,7 +121,7 @@ func TestBalanceOf(t *testing.T) { assert.Equal(t, 1, len(ch.contracts)) }) - t.Run("new contract addres while another contracts already exists", func(t *testing.T) { + t.Run("new contract address while another contracts already exists", func(t *testing.T) { var returnedBalance int64 = 1000 args := createMockArgsContractsHolder() args.EthClient = &bridgeTests.ContractBackendStub{ @@ -160,6 +160,108 @@ func TestBalanceOf(t *testing.T) { }) } +func TestErc20SafeContractsHolder_Decimals(t *testing.T) { + t.Parallel() + + t.Run("address does not exist on map nor blockchain", func(t *testing.T) { + expectedError := errors.New("no contract code at given address") + args := createMockArgsContractsHolder() + args.EthClient = &bridgeTests.ContractBackendStub{ + CallContractCalled: func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return nil, expectedError + }, + } + ch, err := NewErc20SafeContractsHolder(args) + assert.Nil(t, err) + assert.False(t, check.IfNil(ch)) + assert.Equal(t, 0, len(ch.contracts)) + + result, err := ch.Decimals(context.Background(), testsCommon.CreateRandomEthereumAddress()) + assert.Equal(t, expectedError, err) + assert.Zero(t, result) + assert.Equal(t, 1, len(ch.contracts)) + }) + t.Run("address exists only on blockchain", func(t *testing.T) { + returnedDecimals := byte(37) + args := createMockArgsContractsHolder() + args.EthClient = &bridgeTests.ContractBackendStub{ + CallContractCalled: func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return convertByteValueToByteSlice(returnedDecimals), nil + }, + } + ch, err := NewErc20SafeContractsHolder(args) + assert.Nil(t, err) + assert.False(t, check.IfNil(ch)) + assert.Equal(t, 0, len(ch.contracts)) + + result, err := ch.Decimals(context.Background(), testsCommon.CreateRandomEthereumAddress()) + assert.Nil(t, err) + assert.Equal(t, returnedDecimals, result) + assert.Equal(t, 1, len(ch.contracts)) + }) + t.Run("address exists also in contracts map", func(t *testing.T) { + returnedDecimals := byte(38) + args := createMockArgsContractsHolder() + args.EthClient = &bridgeTests.ContractBackendStub{ + CallContractCalled: func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return convertByteValueToByteSlice(returnedDecimals), nil + }, + } + ch, err := NewErc20SafeContractsHolder(args) + contractAddress := testsCommon.CreateRandomEthereumAddress() + assert.Nil(t, err) + assert.False(t, check.IfNil(ch)) + assert.Equal(t, 0, len(ch.contracts)) + + result, err := ch.Decimals(context.Background(), contractAddress) + // first time the contract does not exist in the map, so it should add it + assert.Nil(t, err) + assert.Equal(t, returnedDecimals, result) + assert.Equal(t, 1, len(ch.contracts)) + + result, err = ch.Decimals(context.Background(), contractAddress) + // second time the contract already exists in the map, so it should just use it + assert.Nil(t, err) + assert.Equal(t, returnedDecimals, result) + assert.Equal(t, 1, len(ch.contracts)) + }) + t.Run("new contract address while another contracts already exists", func(t *testing.T) { + returnedDecimals := byte(39) + args := createMockArgsContractsHolder() + args.EthClient = &bridgeTests.ContractBackendStub{ + CallContractCalled: func(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return convertByteValueToByteSlice(returnedDecimals), nil + }, + } + ch, err := NewErc20SafeContractsHolder(args) + contractAddress1 := testsCommon.CreateRandomEthereumAddress() + contractAddress2 := testsCommon.CreateRandomEthereumAddress() + assert.Nil(t, err) + assert.False(t, check.IfNil(ch)) + assert.Equal(t, 0, len(ch.contracts)) + + result, err := ch.Decimals(context.Background(), contractAddress1) + assert.Nil(t, err) + assert.Equal(t, returnedDecimals, result) + assert.Equal(t, 1, len(ch.contracts)) + + result, err = ch.Decimals(context.Background(), contractAddress1) + assert.Nil(t, err) + assert.Equal(t, returnedDecimals, result) + assert.Equal(t, 1, len(ch.contracts)) + + result, err = ch.Decimals(context.Background(), contractAddress2) + assert.Nil(t, err) + assert.Equal(t, returnedDecimals, result) + assert.Equal(t, 2, len(ch.contracts)) + + result, err = ch.Decimals(context.Background(), contractAddress2) + assert.Nil(t, err) + assert.Equal(t, returnedDecimals, result) + assert.Equal(t, 2, len(ch.contracts)) + }) +} + func convertBigToAbiCompatible(number *big.Int) []byte { numberAsBytes := number.Bytes() size := len(numberAsBytes) @@ -170,3 +272,10 @@ func convertBigToAbiCompatible(number *big.Int) []byte { } return bs } + +func convertByteValueToByteSlice(value byte) []byte { + result := make([]byte, 32) + result[len(result)-1] = value + + return result +} diff --git a/clients/ethereum/errors.go b/clients/ethereum/errors.go index ea6468bc..002d341f 100644 --- a/clients/ethereum/errors.go +++ b/clients/ethereum/errors.go @@ -15,4 +15,5 @@ var ( errInvalidGasLimit = errors.New("invalid gas limit") errNilEthClient = errors.New("nil eth client") errDepositsAndBatchDepositsCountDiffer = errors.New("deposits and batch.DepositsCount differs") + errStatusIsNotFinal = errors.New("status is not final") ) diff --git a/clients/ethereum/interface.go b/clients/ethereum/interface.go index ae1d5aa9..ed96a62b 100644 --- a/clients/ethereum/interface.go +++ b/clients/ethereum/interface.go @@ -4,6 +4,7 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -14,8 +15,8 @@ import ( // ClientWrapper represents the Ethereum client wrapper that the ethereum client can rely on type ClientWrapper interface { core.StatusHandler - GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) - GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) + GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) + GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) GetRelayers(ctx context.Context) ([]common.Address, error) WasBatchExecuted(ctx context.Context, batchNonce *big.Int) (bool, error) ChainID(ctx context.Context) (*big.Int, error) @@ -25,14 +26,22 @@ type ClientWrapper interface { recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) Quorum(ctx context.Context) (*big.Int, error) - GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, error) + GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, bool, error) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + TotalBalances(ctx context.Context, arg0 common.Address) (*big.Int, error) + MintBalances(ctx context.Context, arg0 common.Address) (*big.Int, error) + BurnBalances(ctx context.Context, arg0 common.Address) (*big.Int, error) + MintBurnTokens(ctx context.Context, arg0 common.Address) (bool, error) + NativeTokens(ctx context.Context, arg0 common.Address) (bool, error) + WhitelistedTokens(ctx context.Context, arg0 common.Address) (bool, error) IsPaused(ctx context.Context) (bool, error) + FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } // Erc20ContractsHolder defines the Ethereum ERC20 contract operations type Erc20ContractsHolder interface { BalanceOf(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) + Decimals(ctx context.Context, erc20Address common.Address) (uint8, error) IsInterfaceNil() bool } @@ -63,5 +72,14 @@ type SignaturesHolder interface { type erc20ContractWrapper interface { BalanceOf(ctx context.Context, account common.Address) (*big.Int, error) + Decimals(ctx context.Context) (uint8, error) + IsInterfaceNil() bool +} + +// CryptoHandler defines the operations for a component that expose some crypto primitives +type CryptoHandler interface { + Sign(msgHash common.Hash) ([]byte, error) + GetAddress() common.Address + CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) IsInterfaceNil() bool } diff --git a/clients/ethereum/testdata/nok-ethereum-key b/clients/ethereum/testdata/nok-ethereum-key new file mode 100644 index 00000000..3c506c9c --- /dev/null +++ b/clients/ethereum/testdata/nok-ethereum-key @@ -0,0 +1 @@ +9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb diff --git a/clients/ethereum/testdata/ok-ethereum-key b/clients/ethereum/testdata/ok-ethereum-key new file mode 100644 index 00000000..5675c4b6 --- /dev/null +++ b/clients/ethereum/testdata/ok-ethereum-key @@ -0,0 +1 @@ +9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb1 diff --git a/clients/ethereum/wrappers/erc20ContractWrapper.go b/clients/ethereum/wrappers/erc20ContractWrapper.go index 9ea12776..ef4f55b2 100644 --- a/clients/ethereum/wrappers/erc20ContractWrapper.go +++ b/clients/ethereum/wrappers/erc20ContractWrapper.go @@ -43,6 +43,12 @@ func (wrapper *erc20ContractWrapper) BalanceOf(ctx context.Context, account comm return wrapper.erc20Contract.BalanceOf(&bind.CallOpts{Context: ctx}, account) } +// Decimals returns the ERC20 set decimals for the token +func (wrapper *erc20ContractWrapper) Decimals(ctx context.Context) (uint8, error) { + wrapper.statusHandler.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.erc20Contract.Decimals(&bind.CallOpts{Context: ctx}) +} + // IsInterfaceNil returns true if there is no value under the interface func (wrapper *erc20ContractWrapper) IsInterfaceNil() bool { return wrapper == nil diff --git a/clients/ethereum/wrappers/erc20ContractWrapper_test.go b/clients/ethereum/wrappers/erc20ContractWrapper_test.go index c51f8e4f..9d28fbea 100644 --- a/clients/ethereum/wrappers/erc20ContractWrapper_test.go +++ b/clients/ethereum/wrappers/erc20ContractWrapper_test.go @@ -69,3 +69,22 @@ func TestErc20ContractWrapper_BalanceOf(t *testing.T) { assert.True(t, handlerCalled) assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) } + +func TestErc20ContractWrapper_Decimals(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsErc20ContractWrapper() + handlerCalled := false + args.Erc20Contract = &interactors.GenericErc20ContractStub{ + DecimalsCalled: func() (uint8, error) { + handlerCalled = true + return 37, nil + }, + } + wrapper, _ := NewErc20ContractWrapper(args) + decimals, err := wrapper.Decimals(context.TODO()) + assert.Nil(t, err) + assert.Equal(t, byte(37), decimals) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} diff --git a/clients/ethereum/wrappers/errors.go b/clients/ethereum/wrappers/errors.go index 8efee102..6a5cd7b3 100644 --- a/clients/ethereum/wrappers/errors.go +++ b/clients/ethereum/wrappers/errors.go @@ -6,4 +6,5 @@ var ( errNilErc20Contract = errors.New("nil ERC20 contract") errNilBlockchainClient = errors.New("nil blockchain client") errNilMultiSigContract = errors.New("nil multi sig contract") + errNilSafeContract = errors.New("nil safe contract") ) diff --git a/clients/ethereum/wrappers/ethereumChainWrapper.go b/clients/ethereum/wrappers/ethereumChainWrapper.go index 599bf3ac..f5ca77e4 100644 --- a/clients/ethereum/wrappers/ethereumChainWrapper.go +++ b/clients/ethereum/wrappers/ethereumChainWrapper.go @@ -4,6 +4,7 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -17,12 +18,14 @@ import ( type ArgsEthereumChainWrapper struct { StatusHandler core.StatusHandler MultiSigContract multiSigContract + SafeContract safeContract BlockchainClient blockchainClient } type ethereumChainWrapper struct { core.StatusHandler multiSigContract multiSigContract + safeContract safeContract blockchainClient blockchainClient } @@ -36,6 +39,7 @@ func NewEthereumChainWrapper(args ArgsEthereumChainWrapper) (*ethereumChainWrapp return ðereumChainWrapper{ StatusHandler: args.StatusHandler, multiSigContract: args.MultiSigContract, + safeContract: args.SafeContract, blockchainClient: args.BlockchainClient, }, nil } @@ -47,6 +51,9 @@ func checkArgs(args ArgsEthereumChainWrapper) error { if check.IfNilReflect(args.MultiSigContract) { return errNilMultiSigContract } + if check.IfNilReflect(args.SafeContract) { + return errNilSafeContract + } if check.IfNilReflect(args.BlockchainClient) { return errNilBlockchainClient } @@ -55,13 +62,13 @@ func checkArgs(args ArgsEthereumChainWrapper) error { } // GetBatch returns the batch of transactions by providing the batch nonce -func (wrapper *ethereumChainWrapper) GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { +func (wrapper *ethereumChainWrapper) GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) return wrapper.multiSigContract.GetBatch(&bind.CallOpts{Context: ctx}, batchNonce) } // GetBatchDeposits returns the transactions of a batch by providing the batch nonce -func (wrapper *ethereumChainWrapper) GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { +func (wrapper *ethereumChainWrapper) GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) return wrapper.multiSigContract.GetBatchDeposits(&bind.CallOpts{Context: ctx}, batchNonce) } @@ -84,6 +91,12 @@ func (wrapper *ethereumChainWrapper) ChainID(ctx context.Context) (*big.Int, err return wrapper.blockchainClient.ChainID(ctx) } +// FilterLogs executes a query and returns matching logs and events +func (wrapper *ethereumChainWrapper) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.FilterLogs(ctx, q) +} + // BlockNumber returns the current ethereum block number func (wrapper *ethereumChainWrapper) BlockNumber(ctx context.Context) (uint64, error) { wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) @@ -116,7 +129,7 @@ func (wrapper *ethereumChainWrapper) Quorum(ctx context.Context) (*big.Int, erro } // GetStatusesAfterExecution returns the statuses of the last executed transfer -func (wrapper *ethereumChainWrapper) GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, error) { +func (wrapper *ethereumChainWrapper) GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, bool, error) { wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) return wrapper.multiSigContract.GetStatusesAfterExecution(&bind.CallOpts{Context: ctx}, batchID) } @@ -128,6 +141,42 @@ func (wrapper *ethereumChainWrapper) BalanceAt(ctx context.Context, account comm return wrapper.blockchainClient.BalanceAt(ctx, account, blockNumber) } +// TotalBalances returns the total balance of the given token +func (wrapper *ethereumChainWrapper) TotalBalances(ctx context.Context, token common.Address) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.safeContract.TotalBalances(&bind.CallOpts{Context: ctx}, token) +} + +// MintBalances returns the mint balance of the given token +func (wrapper *ethereumChainWrapper) MintBalances(ctx context.Context, token common.Address) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.safeContract.MintBalances(&bind.CallOpts{Context: ctx}, token) +} + +// BurnBalances returns the burn balance of the given token +func (wrapper *ethereumChainWrapper) BurnBalances(ctx context.Context, token common.Address) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.safeContract.BurnBalances(&bind.CallOpts{Context: ctx}, token) +} + +// MintBurnTokens returns true if the token is a mintBurn token +func (wrapper *ethereumChainWrapper) MintBurnTokens(ctx context.Context, token common.Address) (bool, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.safeContract.MintBurnTokens(&bind.CallOpts{Context: ctx}, token) +} + +// NativeTokens returns true if the token is a native token +func (wrapper *ethereumChainWrapper) NativeTokens(ctx context.Context, token common.Address) (bool, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.safeContract.NativeTokens(&bind.CallOpts{Context: ctx}, token) +} + +// WhitelistedTokens returns true if the token is a native token +func (wrapper *ethereumChainWrapper) WhitelistedTokens(ctx context.Context, token common.Address) (bool, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.safeContract.WhitelistedTokens(&bind.CallOpts{Context: ctx}, token) +} + // IsPaused returns true if the multisig contract is paused func (wrapper *ethereumChainWrapper) IsPaused(ctx context.Context) (bool, error) { return wrapper.multiSigContract.Paused(&bind.CallOpts{Context: ctx}) diff --git a/clients/ethereum/wrappers/ethereumChainWrapper_test.go b/clients/ethereum/wrappers/ethereumChainWrapper_test.go index 9473e933..0e64fde1 100644 --- a/clients/ethereum/wrappers/ethereumChainWrapper_test.go +++ b/clients/ethereum/wrappers/ethereumChainWrapper_test.go @@ -4,8 +4,10 @@ import ( "context" "errors" "math/big" + "reflect" "testing" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -24,6 +26,7 @@ func createMockArgsEthereumChainWrapper() (ArgsEthereumChainWrapper, *testsCommo return ArgsEthereumChainWrapper{ MultiSigContract: &bridgeTests.MultiSigContractStub{}, + SafeContract: &bridgeTests.SafeContractStub{}, BlockchainClient: &interactors.BlockchainClientStub{}, StatusHandler: statusHandler, }, statusHandler @@ -62,6 +65,16 @@ func TestNewMultiSigContractWrapper(t *testing.T) { assert.True(t, check.IfNil(wrapper)) assert.Equal(t, errNilMultiSigContract, err) }) + t.Run("nil sc exec contract", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + args.MultiSigContract = nil + + wrapper, err := NewEthereumChainWrapper(args) + assert.True(t, check.IfNil(wrapper)) + assert.Equal(t, errNilMultiSigContract, err) + }) t.Run("should work", func(t *testing.T) { t.Parallel() @@ -80,17 +93,18 @@ func TestEthClientWrapper_GetBatch(t *testing.T) { handlerCalled := false providedBatchID := big.NewInt(223) args.MultiSigContract = &bridgeTests.MultiSigContractStub{ - GetBatchCalled: func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) { + GetBatchCalled: func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, bool, error) { handlerCalled = true assert.Equal(t, providedBatchID, batchNonce) - return contract.Batch{}, nil + return contract.Batch{}, false, nil }, } wrapper, _ := NewEthereumChainWrapper(args) - batch, err := wrapper.GetBatch(context.Background(), providedBatchID) + batch, isFinal, err := wrapper.GetBatch(context.Background(), providedBatchID) assert.Nil(t, err) assert.Equal(t, contract.Batch{}, batch) assert.True(t, handlerCalled) + assert.False(t, isFinal) assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) } @@ -263,16 +277,17 @@ func TestEthClientWrapper_GetStatusesAfterExecution(t *testing.T) { args, statusHandler := createMockArgsEthereumChainWrapper() handlerCalled := false args.MultiSigContract = &bridgeTests.MultiSigContractStub{ - GetStatusesAfterExecutionCalled: func(opts *bind.CallOpts, batchNonceMultiversXETH *big.Int) ([]uint8, error) { + GetStatusesAfterExecutionCalled: func(opts *bind.CallOpts, batchNonceMultiversXETH *big.Int) ([]uint8, bool, error) { handlerCalled = true - return nil, nil + return nil, true, nil }, } wrapper, _ := NewEthereumChainWrapper(args) - statuses, err := wrapper.GetStatusesAfterExecution(context.Background(), nil) + statuses, isFinal, err := wrapper.GetStatusesAfterExecution(context.Background(), nil) assert.Nil(t, err) assert.Nil(t, statuses) assert.True(t, handlerCalled) + assert.True(t, isFinal) assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) } @@ -294,3 +309,45 @@ func TestEthereumChainWrapper_IsPaused(t *testing.T) { assert.True(t, result) assert.True(t, handlerCalled) } + +func TestEthereumChainWrapper_FilterLogs(t *testing.T) { + t.Parallel() + + expectedError := errors.New("expected error") + args, _ := createMockArgsEthereumChainWrapper() + + t.Run("returns error", func(t *testing.T) { + args.BlockchainClient = &interactors.BlockchainClientStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return nil, expectedError + }, + } + + wrapper, _ := NewEthereumChainWrapper(args) + + logs, err := wrapper.FilterLogs(context.Background(), ethereum.FilterQuery{}) + + assert.Nil(t, logs) + assert.Equal(t, expectedError, err) + }) + + t.Run("returns expected logs", func(t *testing.T) { + expectedLogs := []types.Log{ + { + Index: 1, + }, + } + args.BlockchainClient = &interactors.BlockchainClientStub{ + FilterLogsCalled: func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + return expectedLogs, nil + }, + } + + wrapper, _ := NewEthereumChainWrapper(args) + logs, err := wrapper.FilterLogs(context.Background(), ethereum.FilterQuery{}) + + assert.Nil(t, err) + assert.True(t, reflect.DeepEqual(expectedLogs, logs)) + }) + +} diff --git a/clients/ethereum/wrappers/interface.go b/clients/ethereum/wrappers/interface.go index 6537ed82..f6e09953 100644 --- a/clients/ethereum/wrappers/interface.go +++ b/clients/ethereum/wrappers/interface.go @@ -4,6 +4,7 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -12,22 +13,33 @@ import ( type genericErc20Contract interface { BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) + Decimals(opts *bind.CallOpts) (uint8, error) } type multiSigContract interface { - GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) - GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) + GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, bool, error) + GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, bool, error) GetRelayers(opts *bind.CallOpts) ([]common.Address, error) WasBatchExecuted(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) Quorum(opts *bind.CallOpts) (*big.Int, error) - GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) + GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, bool, error) Paused(opts *bind.CallOpts) (bool, error) } +type safeContract interface { + TotalBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) + MintBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) + BurnBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) + MintBurnTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) + NativeTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) + WhitelistedTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) +} + type blockchainClient interface { BlockNumber(ctx context.Context) (uint64, error) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) ChainID(ctx context.Context) (*big.Int, error) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } diff --git a/clients/gasManagement/disabled/disabledGasStation.go b/clients/gasManagement/disabled/disabledGasStation.go index b5e1b654..f091cb56 100644 --- a/clients/gasManagement/disabled/disabledGasStation.go +++ b/clients/gasManagement/disabled/disabledGasStation.go @@ -2,12 +2,19 @@ package disabled import "math/big" +const defaultDisabledGasPrice = 1000 + // DisabledGasStation implementation in case no gasStation is used type DisabledGasStation struct{} -// GetCurrentGasPrice returns nil,nil and will cause the gas price to be determined automatically +// GetCurrentGasPrice returns a default value func (dgs *DisabledGasStation) GetCurrentGasPrice() (*big.Int, error) { - return big.NewInt(0), nil + return big.NewInt(defaultDisabledGasPrice), nil +} + +// Close returns nil and does nothing +func (dgs *DisabledGasStation) Close() error { + return nil } // IsInterfaceNil returns true if there is no value under the interface diff --git a/clients/gasManagement/disabled/disabledGasStation_test.go b/clients/gasManagement/disabled/disabledGasStation_test.go index 4888a3c8..21a2e026 100644 --- a/clients/gasManagement/disabled/disabledGasStation_test.go +++ b/clients/gasManagement/disabled/disabledGasStation_test.go @@ -14,6 +14,9 @@ func TestNewDisabledGasStation(t *testing.T) { assert.False(t, check.IfNil(dgs)) gasPrice, err := dgs.GetCurrentGasPrice() - assert.Equal(t, big.NewInt(0), gasPrice) + assert.Equal(t, big.NewInt(defaultDisabledGasPrice), gasPrice) + assert.Nil(t, err) + + err = dgs.Close() assert.Nil(t, err) } diff --git a/clients/gasManagement/gasStation.go b/clients/gasManagement/gasStation.go index 03cfb9f7..170548e9 100644 --- a/clients/gasManagement/gasStation.go +++ b/clients/gasManagement/gasStation.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "math/big" "net/http" "sync" @@ -199,7 +199,7 @@ func (gs *gasStation) doRequestReturningBytes(ctx context.Context) ([]byte, erro _ = response.Body.Close() }() - body, err := ioutil.ReadAll(response.Body) + body, err := io.ReadAll(response.Body) if err != nil { return nil, err } diff --git a/clients/gasManagement/gasStation_test.go b/clients/gasManagement/gasStation_test.go index e0d0f9f7..47594fb3 100644 --- a/clients/gasManagement/gasStation_test.go +++ b/clients/gasManagement/gasStation_test.go @@ -102,7 +102,10 @@ func TestGasStation_CloseWhileDoingRequest(t *testing.T) { t.Parallel() args := createMockArgsGasStation() + // synchronize the process loop & the testing go routine with an unbuffered channel + chanOk := make(chan struct{}) httpServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + <-chanOk // simulating that the operation takes a lot of time time.Sleep(time.Second * 3) @@ -117,7 +120,8 @@ func TestGasStation_CloseWhileDoingRequest(t *testing.T) { gs, err := NewGasStation(args) require.Nil(t, err) - time.Sleep(time.Second) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) _ = gs.Close() @@ -130,7 +134,10 @@ func TestGasStation_InvalidJsonResponse(t *testing.T) { t.Parallel() args := createMockArgsGasStation() + // synchronize the process loop & the testing go routine with an unbuffered channel + chanNok := make(chan struct{}) httpServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + <-chanNok rw.WriteHeader(http.StatusOK) _, _ = rw.Write([]byte("invalid json response")) })) @@ -141,7 +148,8 @@ func TestGasStation_InvalidJsonResponse(t *testing.T) { gs, err := NewGasStation(args) require.Nil(t, err) - time.Sleep(time.Second * 2) + chanNok <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) _ = gs.Close() @@ -158,7 +166,10 @@ func TestGasStation_GoodResponseShouldSave(t *testing.T) { gsResponse := createMockGasStationResponse() args := createMockArgsGasStation() + // synchronize the process loop & the testing go routine with an unbuffered channel + chanOk := make(chan struct{}) httpServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + <-chanOk rw.WriteHeader(http.StatusOK) resp, _ := json.Marshal(&gsResponse) @@ -171,7 +182,8 @@ func TestGasStation_GoodResponseShouldSave(t *testing.T) { gs, err := NewGasStation(args) require.Nil(t, err) - time.Sleep(time.Second * 2) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) _ = gs.Close() @@ -180,7 +192,6 @@ func TestGasStation_GoodResponseShouldSave(t *testing.T) { var expectedPrice = -1 _, err = fmt.Sscanf(gsResponse.Result.SafeGasPrice, "%d", &expectedPrice) require.Nil(t, err) - assert.NotEqual(t, expectedPrice, -1) assert.Equal(t, gs.GetLatestGasPrice(), expectedPrice) } @@ -190,17 +201,22 @@ func TestGasStation_RetryMechanism_FailsFirstRequests(t *testing.T) { args := createMockArgsGasStation() args.RequestRetryDelay = time.Second args.RequestPollingInterval = 2 * time.Second - numCalled := 0 + args.MaximumFetchRetries = 3 + + // synchronize the process loop & the testing go routine with unbuffered channels + chanOk := make(chan struct{}) + chanNok := make(chan struct{}) gsResponse := createMockGasStationResponse() httpServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) - if numCalled <= args.MaximumFetchRetries { - _, _ = rw.Write([]byte("invalid json response")) - } else { + + select { + case <-chanOk: resp, _ := json.Marshal(&gsResponse) _, _ = rw.Write(resp) + case <-chanNok: + _, _ = rw.Write([]byte("invalid json response")) } - numCalled++ })) defer httpServer.Close() @@ -208,56 +224,35 @@ func TestGasStation_RetryMechanism_FailsFirstRequests(t *testing.T) { gs, err := NewGasStation(args) require.Nil(t, err) - time.Sleep(args.RequestRetryDelay + 1) - assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), -1) - assert.Equal(t, numCalled, 1) - assert.Equal(t, gs.fetchRetries, 1) - gasPrice, err := gs.GetCurrentGasPrice() - assert.Equal(t, big.NewInt(0), gasPrice) - assert.Equal(t, ErrLatestGasPricesWereNotFetched, err) - - time.Sleep(args.RequestRetryDelay + 1) - assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), -1) - assert.Equal(t, numCalled, 2) - assert.Equal(t, gs.fetchRetries, 2) - gasPrice, err = gs.GetCurrentGasPrice() - assert.Equal(t, big.NewInt(0), gasPrice) - assert.Equal(t, ErrLatestGasPricesWereNotFetched, err) - time.Sleep(args.RequestRetryDelay + 1) + chanNok <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), -1) - assert.Equal(t, numCalled, 3) - assert.Equal(t, gs.fetchRetries, 3) - gasPrice, err = gs.GetCurrentGasPrice() + assert.Equal(t, -1, gs.GetLatestGasPrice()) + gasPrice, err := gs.GetCurrentGasPrice() assert.Equal(t, big.NewInt(0), gasPrice) assert.Equal(t, ErrLatestGasPricesWereNotFetched, err) - time.Sleep(args.RequestRetryDelay + 1) + chanNok <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), -1) - assert.Equal(t, numCalled, 4) - assert.Equal(t, gs.fetchRetries, 0) + assert.Equal(t, -1, gs.GetLatestGasPrice()) gasPrice, err = gs.GetCurrentGasPrice() assert.Equal(t, big.NewInt(0), gasPrice) assert.Equal(t, ErrLatestGasPricesWereNotFetched, err) - time.Sleep(args.RequestRetryDelay + 1) + chanNok <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), -1) - assert.Equal(t, numCalled, 4) - assert.Equal(t, gs.fetchRetries, 0) + assert.Equal(t, -1, gs.GetLatestGasPrice()) gasPrice, err = gs.GetCurrentGasPrice() assert.Equal(t, big.NewInt(0), gasPrice) assert.Equal(t, ErrLatestGasPricesWereNotFetched, err) - time.Sleep(args.RequestRetryDelay + 1) + chanOk <- struct{}{} // response is now ok + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), 81) - assert.Equal(t, numCalled, 5) - assert.Equal(t, gs.fetchRetries, 0) + assert.Equal(t, 81, gs.GetLatestGasPrice()) gasPrice, err = gs.GetCurrentGasPrice() assert.Equal(t, big.NewInt(int64(gs.GetLatestGasPrice()*args.GasPriceMultiplier)), gasPrice) assert.Nil(t, err) @@ -267,23 +262,26 @@ func TestGasStation_RetryMechanism_FailsFirstRequests(t *testing.T) { assert.False(t, gs.loopStatus.IsSet()) } -func TestGasStation_RetryMechanism_IntermitentFails(t *testing.T) { +func TestGasStation_RetryMechanism_IntermittentFails(t *testing.T) { t.Parallel() args := createMockArgsGasStation() args.RequestRetryDelay = time.Second args.RequestPollingInterval = 2 * time.Second - numCalled := 0 + + // synchronize the process loop & the testing go routine with unbuffered channels + chanOk := make(chan struct{}) + chanNok := make(chan struct{}) gsResponse := createMockGasStationResponse() httpServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - rw.WriteHeader(http.StatusOK) - if numCalled != 0 && numCalled%3 == 0 { - _, _ = rw.Write([]byte("invalid json response")) - } else { + fmt.Println("http server go routine") + select { + case <-chanOk: resp, _ := json.Marshal(&gsResponse) _, _ = rw.Write(resp) + case <-chanNok: + _, _ = rw.Write([]byte("invalid json response")) } - numCalled++ })) defer httpServer.Close() @@ -292,23 +290,21 @@ func TestGasStation_RetryMechanism_IntermitentFails(t *testing.T) { gs, err := NewGasStation(args) require.Nil(t, err) - time.Sleep(args.RequestPollingInterval*3 + args.RequestRetryDelay + 1) - assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), 81) - assert.Equal(t, numCalled, 4) - assert.Equal(t, gs.fetchRetries, 1) - gasPrice, err := gs.GetCurrentGasPrice() - assert.Equal(t, big.NewInt(int64(gs.GetLatestGasPrice()*args.GasPriceMultiplier)), gasPrice) - assert.Nil(t, err) + for i := 0; i < 6; i++ { + shouldFail := i > 0 && i%3 == 0 + if shouldFail { + chanNok <- struct{}{} + } else { + chanOk <- struct{}{} + } + time.Sleep(time.Millisecond * 100) - time.Sleep(args.RequestRetryDelay + 1) - assert.True(t, gs.loopStatus.IsSet()) - assert.Equal(t, gs.GetLatestGasPrice(), 81) - assert.Equal(t, numCalled, 5) - assert.Equal(t, gs.fetchRetries, 0) - gasPrice, err = gs.GetCurrentGasPrice() - assert.Equal(t, big.NewInt(int64(gs.GetLatestGasPrice()*args.GasPriceMultiplier)), gasPrice) - assert.Nil(t, err) + assert.True(t, gs.loopStatus.IsSet()) + assert.Equal(t, 81, gs.GetLatestGasPrice()) + gasPrice, errGet := gs.GetCurrentGasPrice() + assert.Equal(t, big.NewInt(int64(gs.GetLatestGasPrice()*args.GasPriceMultiplier)), gasPrice) + assert.Nil(t, errGet) + } _ = gs.Close() @@ -322,7 +318,10 @@ func TestGasStation_GetCurrentGasPrice(t *testing.T) { gsResponse := createMockGasStationResponse() args := createMockArgsGasStation() gasPriceMultiplier := big.NewInt(int64(args.GasPriceMultiplier)) + // synchronize the process loop & the testing go routine with an unbuffered channel + chanOk := make(chan struct{}) httpServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + <-chanOk rw.WriteHeader(http.StatusOK) resp, _ := json.Marshal(&gsResponse) @@ -335,11 +334,13 @@ func TestGasStation_GetCurrentGasPrice(t *testing.T) { gs, err := NewGasStation(args) require.Nil(t, err) - time.Sleep(time.Millisecond * 1100) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) gs.SetSelector(core.EthFastGasPrice) - time.Sleep(time.Millisecond * 1100) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) price, err := gs.GetCurrentGasPrice() require.Nil(t, err) expectedPrice := -1 @@ -350,7 +351,8 @@ func TestGasStation_GetCurrentGasPrice(t *testing.T) { assert.Equal(t, expected, price) gs.SetSelector(core.EthProposeGasPrice) - time.Sleep(time.Millisecond * 1100) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) price, err = gs.GetCurrentGasPrice() require.Nil(t, err) expectedPrice = -1 @@ -361,7 +363,8 @@ func TestGasStation_GetCurrentGasPrice(t *testing.T) { assert.Equal(t, expected, price) gs.SetSelector(core.EthSafeGasPrice) - time.Sleep(time.Millisecond * 1100) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) price, err = gs.GetCurrentGasPrice() require.Nil(t, err) expectedPrice = -1 @@ -372,7 +375,8 @@ func TestGasStation_GetCurrentGasPrice(t *testing.T) { assert.Equal(t, expected, price) gs.SetSelector("invalid") - time.Sleep(time.Millisecond * 1100) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) price, err = gs.GetCurrentGasPrice() require.True(t, errors.Is(err, ErrLatestGasPricesWereNotFetched)) assert.Equal(t, big.NewInt(0), price) @@ -385,7 +389,10 @@ func TestGasStation_GetCurrentGasPriceExceededMaximum(t *testing.T) { gsResponse := createMockGasStationResponse() gsResponse.Result.SafeGasPrice = "101" args := createMockArgsGasStation() + // synchronize the process loop & the testing go routine with an unbuffered channel + chanOk := make(chan struct{}) httpServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + <-chanOk rw.WriteHeader(http.StatusOK) resp, _ := json.Marshal(&gsResponse) @@ -398,7 +405,8 @@ func TestGasStation_GetCurrentGasPriceExceededMaximum(t *testing.T) { gs, err := NewGasStation(args) require.Nil(t, err) - time.Sleep(time.Second * 2) + chanOk <- struct{}{} + time.Sleep(time.Millisecond * 100) assert.True(t, gs.loopStatus.IsSet()) price, err := gs.GetCurrentGasPrice() diff --git a/clients/interface.go b/clients/interface.go index 8d473235..624829b4 100644 --- a/clients/interface.go +++ b/clients/interface.go @@ -1,18 +1,12 @@ package clients import ( - "context" "math/big" ) // GasHandler defines the component able to fetch the current gas price type GasHandler interface { GetCurrentGasPrice() (*big.Int, error) - IsInterfaceNil() bool -} - -// BatchValidator defines the operations for a component that can verify a batch -type BatchValidator interface { - ValidateBatch(ctx context.Context, batch *TransferBatch) (bool, error) + Close() error IsInterfaceNil() bool } diff --git a/clients/multiversx/client.go b/clients/multiversx/client.go index ab1d1be8..b9fd0847 100644 --- a/clients/multiversx/client.go +++ b/clients/multiversx/client.go @@ -1,6 +1,7 @@ package multiversx import ( + "bytes" "context" "fmt" "math/big" @@ -8,27 +9,27 @@ import ( "sync" "time" - "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" "github.com/multiversx/mx-bridge-eth-go/clients" "github.com/multiversx/mx-bridge-eth-go/config" bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-bridge-eth-go/core/converters" "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data/api" crypto "github.com/multiversx/mx-chain-crypto-go" "github.com/multiversx/mx-chain-crypto-go/signing/ed25519/singlesig" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-sdk-go/builders" "github.com/multiversx/mx-sdk-go/core" "github.com/multiversx/mx-sdk-go/data" - "github.com/multiversx/mx-sdk-go/interactors/nonceHandlerV1" + "github.com/multiversx/mx-sdk-go/interactors/nonceHandlerV2" ) const ( - proposeTransferFuncName = "proposeMultiTransferEsdtBatch" - proposeSetStatusFuncName = "proposeEsdtSafeSetCurrentTransactionBatchStatus" - signFuncName = "sign" - performActionFuncName = "performAction" - minAllowedDelta = 1 + proposeTransferFuncName = "proposeMultiTransferEsdtBatch" + proposeSetStatusFuncName = "proposeEsdtSafeSetCurrentTransactionBatchStatus" + signFuncName = "sign" + performActionFuncName = "performAction" + minClientAvailabilityAllowDelta = 1 multiversXDataGetterLogId = "MultiversXEth-MultiversXDataGetter" ) @@ -40,26 +41,28 @@ type ClientArgs struct { Log logger.Logger RelayerPrivateKey crypto.PrivateKey MultisigContractAddress core.AddressHandler + SafeContractAddress core.AddressHandler IntervalToResendTxsInSeconds uint64 TokensMapper TokensMapper RoleProvider roleProvider StatusHandler bridgeCore.StatusHandler - AllowDelta uint64 + ClientAvailabilityAllowDelta uint64 } // client represents the MultiversX Client implementation type client struct { *mxClientDataGetter - txHandler txHandler - tokensMapper TokensMapper - relayerPublicKey crypto.PublicKey - relayerAddress core.AddressHandler - multisigContractAddress core.AddressHandler - log logger.Logger - gasMapConfig config.MultiversXGasMapConfig - addressPublicKeyConverter bridgeCore.AddressConverter - statusHandler bridgeCore.StatusHandler - allowDelta uint64 + txHandler txHandler + tokensMapper TokensMapper + relayerPublicKey crypto.PublicKey + relayerAddress core.AddressHandler + multisigContractAddress core.AddressHandler + safeContractAddress core.AddressHandler + log logger.Logger + gasMapConfig config.MultiversXGasMapConfig + addressPublicKeyConverter bridgeCore.AddressConverter + statusHandler bridgeCore.StatusHandler + clientAvailabilityAllowDelta uint64 lastNonce uint64 retriesAvailabilityCheck uint64 @@ -73,7 +76,11 @@ func NewClient(args ClientArgs) (*client, error) { return nil, err } - nonceTxsHandler, err := nonceHandlerV1.NewNonceTransactionHandlerV1(args.Proxy, time.Second*time.Duration(args.IntervalToResendTxsInSeconds), true) + argNonceHandler := nonceHandlerV2.ArgsNonceTransactionsHandlerV2{ + Proxy: args.Proxy, + IntervalToResend: time.Second * time.Duration(args.IntervalToResendTxsInSeconds), + } + nonceTxsHandler, err := nonceHandlerV2.NewNonceTransactionHandlerV2(argNonceHandler) if err != nil { return nil, err } @@ -88,6 +95,7 @@ func NewClient(args ClientArgs) (*client, error) { argsMXClientDataGetter := ArgsMXClientDataGetter{ MultisigContractAddress: args.MultisigContractAddress, + SafeContractAddress: args.SafeContractAddress, RelayerAddress: relayerAddress, Proxy: args.Proxy, Log: bridgeCore.NewLoggerWithIdentifier(logger.GetOrCreate(multiversXDataGetterLogId), multiversXDataGetterLogId), @@ -102,31 +110,44 @@ func NewClient(args ClientArgs) (*client, error) { return nil, clients.ErrNilAddressConverter } + bech23MultisigAddress, err := args.MultisigContractAddress.AddressAsBech32String() + if err != nil { + return nil, fmt.Errorf("%w for %x", err, args.MultisigContractAddress.AddressBytes()) + } + + bech23SafeAddress, err := args.SafeContractAddress.AddressAsBech32String() + if err != nil { + return nil, fmt.Errorf("%w for %x", err, args.SafeContractAddress.AddressBytes()) + } + c := &client{ txHandler: &transactionHandler{ proxy: args.Proxy, relayerAddress: relayerAddress, - multisigAddressAsBech32: args.MultisigContractAddress.AddressAsBech32String(), + multisigAddressAsBech32: bech23MultisigAddress, nonceTxHandler: nonceTxsHandler, relayerPrivateKey: args.RelayerPrivateKey, singleSigner: &singlesig.Ed25519Signer{}, roleProvider: args.RoleProvider, }, - mxClientDataGetter: getter, - relayerPublicKey: publicKey, - relayerAddress: relayerAddress, - multisigContractAddress: args.MultisigContractAddress, - log: args.Log, - gasMapConfig: args.GasMapConfig, - addressPublicKeyConverter: addressConverter, - tokensMapper: args.TokensMapper, - statusHandler: args.StatusHandler, - allowDelta: args.AllowDelta, - } - + mxClientDataGetter: getter, + relayerPublicKey: publicKey, + relayerAddress: relayerAddress, + multisigContractAddress: args.MultisigContractAddress, + safeContractAddress: args.SafeContractAddress, + log: args.Log, + gasMapConfig: args.GasMapConfig, + addressPublicKeyConverter: addressConverter, + tokensMapper: args.TokensMapper, + statusHandler: args.StatusHandler, + clientAvailabilityAllowDelta: args.ClientAvailabilityAllowDelta, + } + + bech32RelayerAddress, _ := relayerAddress.AddressAsBech32String() c.log.Info("NewMultiversXClient", - "relayer address", relayerAddress.AddressAsBech32String(), - "safe contract address", args.MultisigContractAddress.AddressAsBech32String()) + "relayer address", bech32RelayerAddress, + "multisig contract address", bech23MultisigAddress, + "safe contract address", bech23SafeAddress) return c, nil } @@ -141,6 +162,9 @@ func checkArgs(args ClientArgs) error { if check.IfNil(args.MultisigContractAddress) { return fmt.Errorf("%w for the MultisigContractAddress argument", errNilAddressHandler) } + if check.IfNil(args.SafeContractAddress) { + return fmt.Errorf("%w for the SafeContractAddress argument", errNilAddressHandler) + } if check.IfNil(args.Log) { return clients.ErrNilLogger } @@ -153,9 +177,9 @@ func checkArgs(args ClientArgs) error { if check.IfNil(args.StatusHandler) { return clients.ErrNilStatusHandler } - if args.AllowDelta < minAllowedDelta { - return fmt.Errorf("%w for args.AllowedDelta, got: %d, minimum: %d", - clients.ErrInvalidValue, args.AllowDelta, minAllowedDelta) + if args.ClientAvailabilityAllowDelta < minClientAvailabilityAllowDelta { + return fmt.Errorf("%w for args.ClientAvailabilityAllowDelta, got: %d, minimum: %d", + clients.ErrInvalidValue, args.ClientAvailabilityAllowDelta, minClientAvailabilityAllowDelta) } err := checkGasMapValues(args.GasMapConfig) if err != nil { @@ -178,8 +202,8 @@ func checkGasMapValues(gasMap config.MultiversXGasMapConfig) error { return nil } -// GetPending returns the pending batch -func (c *client) GetPending(ctx context.Context) (*clients.TransferBatch, error) { +// GetPendingBatch returns the pending batch +func (c *client) GetPendingBatch(ctx context.Context) (*bridgeCore.TransferBatch, error) { c.log.Info("getting pending batch...") responseData, err := c.GetCurrentBatchAsDataBytes(ctx) if err != nil { @@ -187,7 +211,22 @@ func (c *client) GetPending(ctx context.Context) (*clients.TransferBatch, error) } if emptyResponse(responseData) { - return nil, ErrNoPendingBatchAvailable + return nil, clients.ErrNoPendingBatchAvailable + } + + return c.createPendingBatchFromResponse(ctx, responseData) +} + +// GetBatch returns the batch (if existing) +func (c *client) GetBatch(ctx context.Context, batchID uint64) (*bridgeCore.TransferBatch, error) { + c.log.Debug("getting batch", "ID", batchID) + responseData, err := c.GetBatchAsDataBytes(ctx, batchID) + if err != nil { + return nil, err + } + + if emptyResponse(responseData) { + return nil, clients.ErrNoBatchAvailable } return c.createPendingBatchFromResponse(ctx, responseData) @@ -197,7 +236,7 @@ func emptyResponse(response [][]byte) bool { return len(response) == 0 || (len(response) == 1 && len(response[0]) == 0) } -func (c *client) createPendingBatchFromResponse(ctx context.Context, responseData [][]byte) (*clients.TransferBatch, error) { +func (c *client) createPendingBatchFromResponse(ctx context.Context, responseData [][]byte) (*bridgeCore.TransferBatch, error) { numFieldsForTransaction := 6 dataLen := len(responseData) haveCorrectNumberOfArgs := (dataLen-1)%numFieldsForTransaction == 0 && dataLen > 1 @@ -210,7 +249,7 @@ func (c *client) createPendingBatchFromResponse(ctx context.Context, responseDat return nil, fmt.Errorf("%w while parsing batch ID", err) } - batch := &clients.TransferBatch{ + batch := &bridgeCore.TransferBatch{ ID: batchID, } @@ -224,26 +263,26 @@ func (c *client) createPendingBatchFromResponse(ctx context.Context, responseDat } amount := big.NewInt(0).SetBytes(responseData[i+5]) - deposit := &clients.DepositTransfer{ + deposit := &bridgeCore.DepositTransfer{ Nonce: depositNonce, FromBytes: responseData[i+2], - DisplayableFrom: c.addressPublicKeyConverter.ToBech32String(responseData[i+2]), + DisplayableFrom: c.addressPublicKeyConverter.ToBech32StringSilent(responseData[i+2]), ToBytes: responseData[i+3], DisplayableTo: c.addressPublicKeyConverter.ToHexStringWithPrefix(responseData[i+3]), - TokenBytes: responseData[i+4], + SourceTokenBytes: responseData[i+4], DisplayableToken: string(responseData[i+4]), Amount: amount, } storedConvertedTokenBytes, exists := cachedTokens[deposit.DisplayableToken] if !exists { - deposit.ConvertedTokenBytes, err = c.tokensMapper.ConvertToken(ctx, deposit.TokenBytes) + deposit.DestinationTokenBytes, err = c.tokensMapper.ConvertToken(ctx, deposit.SourceTokenBytes) if err != nil { return nil, fmt.Errorf("%w while converting token bytes, transfer index %d", err, transferIndex) } - cachedTokens[deposit.DisplayableToken] = deposit.ConvertedTokenBytes + cachedTokens[deposit.DisplayableToken] = deposit.DestinationTokenBytes } else { - deposit.ConvertedTokenBytes = storedConvertedTokenBytes + deposit.DestinationTokenBytes = storedConvertedTokenBytes } batch.Deposits = append(batch.Deposits, deposit) @@ -262,7 +301,7 @@ func (c *client) createCommonTxDataBuilder(funcName string, id int64) builders.T } // ProposeSetStatus will trigger the proposal of the ESDT safe set current transaction batch status operation -func (c *client) ProposeSetStatus(ctx context.Context, batch *clients.TransferBatch) (string, error) { +func (c *client) ProposeSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { if batch == nil { return "", clients.ErrNilBatch } @@ -280,14 +319,14 @@ func (c *client) ProposeSetStatus(ctx context.Context, batch *clients.TransferBa gasLimit := c.gasMapConfig.ProposeStatusBase + uint64(len(batch.Deposits))*c.gasMapConfig.ProposeStatusForEach hash, err := c.txHandler.SendTransactionReturnHash(ctx, txBuilder, gasLimit) if err == nil { - c.log.Info("proposed set statuses"+batch.String(), "transaction hash", hash) + c.log.Info("proposed set statuses "+batch.String(), "transaction hash", hash) } return hash, err } // ProposeTransfer will trigger the propose transfer operation -func (c *client) ProposeTransfer(ctx context.Context, batch *clients.TransferBatch) (string, error) { +func (c *client) ProposeTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { if batch == nil { return "", clients.ErrNilBatch } @@ -302,15 +341,18 @@ func (c *client) ProposeTransfer(ctx context.Context, batch *clients.TransferBat for _, dt := range batch.Deposits { txBuilder.ArgBytes(dt.FromBytes). ArgBytes(dt.ToBytes). - ArgBytes(dt.ConvertedTokenBytes). + ArgBytes(dt.DestinationTokenBytes). ArgBigInt(dt.Amount). - ArgInt64(int64(dt.Nonce)) + ArgInt64(int64(dt.Nonce)). + ArgBytes(dt.Data) } gasLimit := c.gasMapConfig.ProposeTransferBase + uint64(len(batch.Deposits))*c.gasMapConfig.ProposeTransferForEach + extraGasForScCalls := c.computeExtraGasForSCCallsBasic(batch, false) + gasLimit += extraGasForScCalls hash, err := c.txHandler.SendTransactionReturnHash(ctx, txBuilder, gasLimit) if err == nil { - c.log.Info("proposed transfer"+batch.String(), "transaction hash", hash) + c.log.Info("proposed transfer "+batch.String(), "transaction hash", hash) } return hash, err @@ -334,7 +376,7 @@ func (c *client) Sign(ctx context.Context, actionID uint64) (string, error) { } // PerformAction will trigger the execution of the provided action ID -func (c *client) PerformAction(ctx context.Context, actionID uint64, batch *clients.TransferBatch) (string, error) { +func (c *client) PerformAction(ctx context.Context, actionID uint64, batch *bridgeCore.TransferBatch) (string, error) { if batch == nil { return "", clients.ErrNilBatch } @@ -347,6 +389,7 @@ func (c *client) PerformAction(ctx context.Context, actionID uint64, batch *clie txBuilder := c.createCommonTxDataBuilder(performActionFuncName, int64(actionID)) gasLimit := c.gasMapConfig.PerformActionBase + uint64(len(batch.Statuses))*c.gasMapConfig.PerformActionForEach + gasLimit += c.computeExtraGasForSCCallsBasic(batch, true) hash, err := c.txHandler.SendTransactionReturnHash(ctx, txBuilder, gasLimit) if err == nil { @@ -356,6 +399,25 @@ func (c *client) PerformAction(ctx context.Context, actionID uint64, batch *clie return hash, err } +func (c *client) computeExtraGasForSCCallsBasic(batch *bridgeCore.TransferBatch, performAction bool) uint64 { + gasLimit := uint64(0) + for _, deposit := range batch.Deposits { + if bytes.Equal(deposit.Data, []byte{bridgeCore.MissingDataProtocolMarker}) { + continue + } + + computedLen := 1 // extra argument separator (@) + computedLen += len(deposit.Data) * 2 // the data is hexed, so, double the size + + gasLimit += uint64(computedLen) * c.gasMapConfig.ScCallPerByte + if performAction { + gasLimit += c.gasMapConfig.ScCallPerformForEach + } + } + + return gasLimit +} + func (c *client) checkIsPaused(ctx context.Context) error { isPaused, err := c.IsPaused(ctx) if err != nil { @@ -368,6 +430,69 @@ func (c *client) checkIsPaused(ctx context.Context) error { return nil } +// IsMintBurnToken returns true if the provided token is whitelisted for mint/burn operations +func (c *client) IsMintBurnToken(ctx context.Context, token []byte) (bool, error) { + return c.isMintBurnToken(ctx, token) +} + +// IsNativeToken returns true if the provided token is native +func (c *client) IsNativeToken(ctx context.Context, token []byte) (bool, error) { + return c.isNativeToken(ctx, token) +} + +// TotalBalances returns the total stored tokens +func (c *client) TotalBalances(ctx context.Context, token []byte) (*big.Int, error) { + return c.getTotalBalances(ctx, token) +} + +// MintBalances returns the minted tokens +func (c *client) MintBalances(ctx context.Context, token []byte) (*big.Int, error) { + return c.getMintBalances(ctx, token) +} + +// BurnBalances returns the burned tokens +func (c *client) BurnBalances(ctx context.Context, token []byte) (*big.Int, error) { + return c.getBurnBalances(ctx, token) +} + +// CheckRequiredBalance will check the required balance for the provided token +func (c *client) CheckRequiredBalance(ctx context.Context, token []byte, value *big.Int) error { + isMintBurn, err := c.IsMintBurnToken(ctx, token) + if err != nil { + return err + } + + if isMintBurn { + return nil + } + safeAddress, err := c.safeContractAddress.AddressAsBech32String() + if err != nil { + return fmt.Errorf("%w for safe address %s", err, c.safeContractAddress.AddressBytes()) + } + esdt, err := c.proxy.GetESDTTokenData(ctx, c.safeContractAddress, string(token), api.AccountQueryOptions{}) + if err != nil { + return fmt.Errorf("%w for address %s for ESDT token %s", err, safeAddress, string(token)) + } + + existingBalance, ok := big.NewInt(0).SetString(esdt.Balance, 10) + if !ok { + return fmt.Errorf("%w for ESDT token %s and address %s", errInvalidBalance, string(token), safeAddress) + } + + if value.Cmp(existingBalance) > 0 { + return fmt.Errorf("%w, existing: %s, required: %s for ERC20 token %s and address %s", + errInsufficientESDTBalance, existingBalance.String(), value.String(), string(token), safeAddress) + } + + c.log.Debug("checked ERC20 balance", + "ESDT token", string(token), + "address", safeAddress, + "existing balance", existingBalance.String(), + "needed", value.String()) + + return nil +} + // CheckClientAvailability will check the client availability and will set the metric accordingly func (c *client) CheckClientAvailability(ctx context.Context) error { c.mut.Lock() @@ -375,7 +500,7 @@ func (c *client) CheckClientAvailability(ctx context.Context) error { currentNonce, err := c.GetCurrentNonce(ctx) if err != nil { - c.setStatusForAvailabilityCheck(ethmultiversx.Unavailable, err.Error(), currentNonce) + c.setStatusForAvailabilityCheck(bridgeCore.Unavailable, err.Error(), currentNonce) return err } @@ -388,14 +513,14 @@ func (c *client) CheckClientAvailability(ctx context.Context) error { // if we reached this point we will need to increment the retries counter defer c.incrementRetriesAvailabilityCheck() - if c.retriesAvailabilityCheck > c.allowDelta { + if c.retriesAvailabilityCheck > c.clientAvailabilityAllowDelta { message := fmt.Sprintf("nonce %d fetched for %d times in a row", currentNonce, c.retriesAvailabilityCheck) - c.setStatusForAvailabilityCheck(ethmultiversx.Unavailable, message, currentNonce) + c.setStatusForAvailabilityCheck(bridgeCore.Unavailable, message, currentNonce) return nil } - c.setStatusForAvailabilityCheck(ethmultiversx.Available, "", currentNonce) + c.setStatusForAvailabilityCheck(bridgeCore.Available, "", currentNonce) return nil } @@ -404,7 +529,7 @@ func (c *client) incrementRetriesAvailabilityCheck() { c.retriesAvailabilityCheck++ } -func (c *client) setStatusForAvailabilityCheck(status ethmultiversx.ClientStatus, message string, nonce uint64) { +func (c *client) setStatusForAvailabilityCheck(status bridgeCore.ClientStatus, message string, nonce uint64) { c.statusHandler.SetStringMetric(bridgeCore.MetricMultiversXClientStatus, status.String()) c.statusHandler.SetStringMetric(bridgeCore.MetricLastMultiversXClientError, message) c.statusHandler.SetIntMetric(bridgeCore.MetricLastBlockNonce, int(nonce)) diff --git a/clients/multiversx/client_test.go b/clients/multiversx/client_test.go index de40b64a..9fe43810 100644 --- a/clients/multiversx/client_test.go +++ b/clients/multiversx/client_test.go @@ -10,7 +10,6 @@ import ( "strings" "testing" - "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" "github.com/multiversx/mx-bridge-eth-go/clients" "github.com/multiversx/mx-bridge-eth-go/config" bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" @@ -35,6 +34,7 @@ var pausedBytes = []byte{1} func createMockClientArgs() ClientArgs { privateKey, _ := testKeyGen.PrivateKeyFromByteArray(bytes.Repeat([]byte{1}, 32)) multisigContractAddress, _ := data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf") + safeContractAddress, _ := data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus") return ClientArgs{ GasMapConfig: config.MultiversXGasMapConfig{ @@ -45,20 +45,23 @@ func createMockClientArgs() ClientArgs { ProposeStatusForEach: 50, PerformActionBase: 60, PerformActionForEach: 70, + ScCallPerByte: 80, + ScCallPerformForEach: 90, }, Proxy: &interactors.ProxyStub{}, Log: logger.GetOrCreate("test"), RelayerPrivateKey: privateKey, MultisigContractAddress: multisigContractAddress, + SafeContractAddress: safeContractAddress, IntervalToResendTxsInSeconds: 1, TokensMapper: &bridgeTests.TokensMapperStub{ ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { return append([]byte("converted "), sourceBytes...), nil }, }, - RoleProvider: &roleproviders.MultiversXRoleProviderStub{}, - StatusHandler: &testsCommon.StatusHandlerStub{}, - AllowDelta: 5, + RoleProvider: &roleproviders.MultiversXRoleProviderStub{}, + StatusHandler: &testsCommon.StatusHandlerStub{}, + ClientAvailabilityAllowDelta: 5, } } @@ -123,6 +126,17 @@ func TestNewClient(t *testing.T) { require.True(t, check.IfNil(c)) require.True(t, errors.Is(err, errNilAddressHandler)) }) + t.Run("nil safe contract address should error", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + args.SafeContractAddress = nil + + c, err := NewClient(args) + + require.True(t, check.IfNil(c)) + require.True(t, errors.Is(err, errNilAddressHandler)) + }) t.Run("nil logger should error", func(t *testing.T) { t.Parallel() @@ -191,17 +205,17 @@ func TestNewClient(t *testing.T) { require.True(t, check.IfNil(c)) require.Equal(t, clients.ErrNilStatusHandler, err) }) - t.Run("invalid AllowDelta should error", func(t *testing.T) { + t.Run("invalid ClientAvailabilityAllowDelta should error", func(t *testing.T) { t.Parallel() args := createMockClientArgs() - args.AllowDelta = 0 + args.ClientAvailabilityAllowDelta = 0 c, err := NewClient(args) require.True(t, check.IfNil(c)) require.True(t, errors.Is(err, clients.ErrInvalidValue)) - require.True(t, strings.Contains(err.Error(), "for args.AllowedDelta")) + require.True(t, strings.Contains(err.Error(), "for args.ClientAvailabilityAllowDelta")) }) t.Run("should work", func(t *testing.T) { t.Parallel() @@ -214,7 +228,7 @@ func TestNewClient(t *testing.T) { }) } -func TestClient_GetPending(t *testing.T) { +func TestClient_GetPendingBatch(t *testing.T) { t.Parallel() t.Run("get pending batch failed should error", func(t *testing.T) { @@ -229,7 +243,7 @@ func TestClient_GetPending(t *testing.T) { } c, _ := NewClient(args) - batch, err := c.GetPending(context.Background()) + batch, err := c.GetPendingBatch(context.Background()) assert.Nil(t, batch) assert.Equal(t, expectedErr, err) }) @@ -240,9 +254,9 @@ func TestClient_GetPending(t *testing.T) { args.Proxy = createMockProxy(make([][]byte, 0)) c, _ := NewClient(args) - batch, err := c.GetPending(context.Background()) + batch, err := c.GetPendingBatch(context.Background()) assert.Nil(t, batch) - assert.Equal(t, ErrNoPendingBatchAvailable, err) + assert.Equal(t, clients.ErrNoPendingBatchAvailable, err) }) t.Run("invalid length", func(t *testing.T) { t.Parallel() @@ -252,7 +266,7 @@ func TestClient_GetPending(t *testing.T) { args.Proxy = createMockProxy(buff[:len(buff)-1]) c, _ := NewClient(args) - batch, err := c.GetPending(context.Background()) + batch, err := c.GetPendingBatch(context.Background()) assert.Nil(t, batch) assert.True(t, errors.Is(err, errInvalidNumberOfArguments)) @@ -261,7 +275,7 @@ func TestClient_GetPending(t *testing.T) { args.Proxy = createMockProxy([][]byte{{1}}) c, _ = NewClient(args) - batch, err = c.GetPending(context.Background()) + batch, err = c.GetPendingBatch(context.Background()) assert.Nil(t, batch) assert.True(t, errors.Is(err, errInvalidNumberOfArguments)) @@ -276,7 +290,7 @@ func TestClient_GetPending(t *testing.T) { args.Proxy = createMockProxy(buff) c, _ := NewClient(args) - batch, err := c.GetPending(context.Background()) + batch, err := c.GetPendingBatch(context.Background()) assert.Nil(t, batch) assert.True(t, errors.Is(err, errNotUint64Bytes)) @@ -291,7 +305,7 @@ func TestClient_GetPending(t *testing.T) { args.Proxy = createMockProxy(buff) c, _ := NewClient(args) - batch, err := c.GetPending(context.Background()) + batch, err := c.GetPendingBatch(context.Background()) assert.Nil(t, batch) assert.True(t, errors.Is(err, errNotUint64Bytes)) @@ -311,7 +325,7 @@ func TestClient_GetPending(t *testing.T) { args.Proxy = createMockProxy(buff) c, _ := NewClient(args) - batch, err := c.GetPending(context.Background()) + batch, err := c.GetPendingBatch(context.Background()) assert.Nil(t, batch) assert.True(t, errors.Is(err, expectedErr)) @@ -330,37 +344,37 @@ func TestClient_GetPending(t *testing.T) { tokenBytes1 := bytes.Repeat([]byte{3}, 32) tokenBytes2 := bytes.Repeat([]byte{6}, 32) - expectedBatch := &clients.TransferBatch{ + expectedBatch := &bridgeCore.TransferBatch{ ID: 44562, - Deposits: []*clients.DepositTransfer{ + Deposits: []*bridgeCore.DepositTransfer{ { - Nonce: 5000, - ToBytes: bytes.Repeat([]byte{2}, 20), - DisplayableTo: "0x0202020202020202020202020202020202020202", - FromBytes: bytes.Repeat([]byte{1}, 32), - DisplayableFrom: "erd1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsl6e0p7", - TokenBytes: tokenBytes1, - ConvertedTokenBytes: append([]byte("converted_"), tokenBytes1...), - DisplayableToken: string(tokenBytes1), - Amount: big.NewInt(10000), + Nonce: 5000, + ToBytes: bytes.Repeat([]byte{2}, 20), + DisplayableTo: "0x0202020202020202020202020202020202020202", + FromBytes: bytes.Repeat([]byte{1}, 32), + DisplayableFrom: "erd1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsl6e0p7", + SourceTokenBytes: tokenBytes1, + DestinationTokenBytes: append([]byte("converted_"), tokenBytes1...), + DisplayableToken: string(tokenBytes1), + Amount: big.NewInt(10000), }, { - Nonce: 5001, - ToBytes: bytes.Repeat([]byte{5}, 20), - DisplayableTo: "0x0505050505050505050505050505050505050505", - FromBytes: bytes.Repeat([]byte{4}, 32), - DisplayableFrom: "erd1qszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqxjfvxn", - TokenBytes: tokenBytes2, - ConvertedTokenBytes: append([]byte("converted_"), tokenBytes2...), - DisplayableToken: string(tokenBytes2), - Amount: big.NewInt(20000), + Nonce: 5001, + ToBytes: bytes.Repeat([]byte{5}, 20), + DisplayableTo: "0x0505050505050505050505050505050505050505", + FromBytes: bytes.Repeat([]byte{4}, 32), + DisplayableFrom: "erd1qszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqxjfvxn", + SourceTokenBytes: tokenBytes2, + DestinationTokenBytes: append([]byte("converted_"), tokenBytes2...), + DisplayableToken: string(tokenBytes2), + Amount: big.NewInt(20000), }, }, Statuses: make([]byte, 2), } c, _ := NewClient(args) - batch, err := c.GetPending(context.Background()) + batch, err := c.GetPendingBatch(context.Background()) assert.Nil(t, err) args.Log.Info("expected batch\n" + expectedBatch.String()) @@ -369,7 +383,163 @@ func TestClient_GetPending(t *testing.T) { assert.Equal(t, expectedBatch, batch) assert.Nil(t, err) }) +} + +func TestClient_GetBatch(t *testing.T) { + t.Parallel() + + t.Run("get batch failed should error", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + expectedErr := errors.New("expected error") + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return nil, expectedErr + }, + } + + c, _ := NewClient(args) + batch, err := c.GetBatch(context.Background(), 37) + assert.Nil(t, batch) + assert.Equal(t, expectedErr, err) + }) + t.Run("empty response", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + args.Proxy = createMockProxy(make([][]byte, 0)) + + c, _ := NewClient(args) + batch, err := c.GetBatch(context.Background(), 37) + assert.Nil(t, batch) + assert.Equal(t, clients.ErrNoBatchAvailable, err) + }) + t.Run("invalid length", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + buff := createMockPendingBatchBytes(2) + args.Proxy = createMockProxy(buff[:len(buff)-1]) + + c, _ := NewClient(args) + batch, err := c.GetBatch(context.Background(), 37) + + assert.Nil(t, batch) + assert.True(t, errors.Is(err, errInvalidNumberOfArguments)) + assert.True(t, strings.Contains(err.Error(), "got 12 argument(s)")) + + args.Proxy = createMockProxy([][]byte{{1}}) + c, _ = NewClient(args) + + batch, err = c.GetBatch(context.Background(), 37) + + assert.Nil(t, batch) + assert.True(t, errors.Is(err, errInvalidNumberOfArguments)) + assert.True(t, strings.Contains(err.Error(), "got 1 argument(s)")) + }) + t.Run("invalid batch ID", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + buff := createMockPendingBatchBytes(2) + buff[0] = bytes.Repeat([]byte{1}, 32) + args.Proxy = createMockProxy(buff) + + c, _ := NewClient(args) + batch, err := c.GetBatch(context.Background(), 37) + + assert.Nil(t, batch) + assert.True(t, errors.Is(err, errNotUint64Bytes)) + assert.True(t, strings.Contains(err.Error(), "while parsing batch ID")) + }) + t.Run("invalid deposit nonce", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + buff := createMockPendingBatchBytes(2) + buff[8] = bytes.Repeat([]byte{1}, 32) + args.Proxy = createMockProxy(buff) + + c, _ := NewClient(args) + batch, err := c.GetBatch(context.Background(), 37) + + assert.Nil(t, batch) + assert.True(t, errors.Is(err, errNotUint64Bytes)) + assert.True(t, strings.Contains(err.Error(), "while parsing the deposit nonce, transfer index 1")) + }) + t.Run("tokens mapper errors", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + expectedErr := errors.New("expected error in convert tokens") + args.TokensMapper = &bridgeTests.TokensMapperStub{ + ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { + return nil, expectedErr + }, + } + buff := createMockPendingBatchBytes(2) + args.Proxy = createMockProxy(buff) + + c, _ := NewClient(args) + batch, err := c.GetBatch(context.Background(), 37) + + assert.Nil(t, batch) + assert.True(t, errors.Is(err, expectedErr)) + assert.True(t, strings.Contains(err.Error(), "while converting token bytes, transfer index 0")) + }) + t.Run("should create pending batch", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + args.TokensMapper = &bridgeTests.TokensMapperStub{ + ConvertTokenCalled: func(ctx context.Context, sourceBytes []byte) ([]byte, error) { + return append([]byte("converted_"), sourceBytes...), nil + }, + } + args.Proxy = createMockProxy(createMockPendingBatchBytes(2)) + + tokenBytes1 := bytes.Repeat([]byte{3}, 32) + tokenBytes2 := bytes.Repeat([]byte{6}, 32) + expectedBatch := &bridgeCore.TransferBatch{ + ID: 44562, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: 5000, + ToBytes: bytes.Repeat([]byte{2}, 20), + DisplayableTo: "0x0202020202020202020202020202020202020202", + FromBytes: bytes.Repeat([]byte{1}, 32), + DisplayableFrom: "erd1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsl6e0p7", + SourceTokenBytes: tokenBytes1, + DestinationTokenBytes: append([]byte("converted_"), tokenBytes1...), + DisplayableToken: string(tokenBytes1), + Amount: big.NewInt(10000), + }, + { + Nonce: 5001, + ToBytes: bytes.Repeat([]byte{5}, 20), + DisplayableTo: "0x0505050505050505050505050505050505050505", + FromBytes: bytes.Repeat([]byte{4}, 32), + DisplayableFrom: "erd1qszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqxjfvxn", + SourceTokenBytes: tokenBytes2, + DestinationTokenBytes: append([]byte("converted_"), tokenBytes2...), + DisplayableToken: string(tokenBytes2), + Amount: big.NewInt(20000), + }, + }, + Statuses: make([]byte, 2), + } + + c, _ := NewClient(args) + batch, err := c.GetBatch(context.Background(), 44562) + assert.Nil(t, err) + + args.Log.Info("expected batch\n" + expectedBatch.String()) + args.Log.Info("batch\n" + batch.String()) + assert.Equal(t, expectedBatch, batch) + assert.Nil(t, err) + }) } func TestClient_ProposeSetStatus(t *testing.T) { @@ -397,7 +567,7 @@ func TestClient_ProposeSetStatus(t *testing.T) { } c, _ := NewClient(args) - hash, err := c.ProposeSetStatus(context.Background(), &clients.TransferBatch{}) + hash, err := c.ProposeSetStatus(context.Background(), &bridgeCore.TransferBatch{}) assert.Empty(t, hash) assert.True(t, errors.Is(err, expectedErr)) }) @@ -417,7 +587,7 @@ func TestClient_ProposeSetStatus(t *testing.T) { } c, _ := NewClient(args) - hash, err := c.ProposeSetStatus(context.Background(), &clients.TransferBatch{}) + hash, err := c.ProposeSetStatus(context.Background(), &bridgeCore.TransferBatch{}) assert.Empty(t, hash) assert.True(t, errors.Is(err, clients.ErrMultisigContractPaused)) }) @@ -440,7 +610,7 @@ func TestClient_ProposeSetStatus(t *testing.T) { proposeSetStatusFuncName, hex.EncodeToString(big.NewInt(112233).Bytes()), } - expectedStatus := []byte{clients.Rejected, clients.Executed} + expectedStatus := []byte{bridgeCore.Rejected, bridgeCore.Executed} for _, stat := range expectedStatus { expectedArgs = append(expectedArgs, hex.EncodeToString([]byte{stat})) } @@ -486,7 +656,7 @@ func TestClient_ProposeTransfer(t *testing.T) { } c, _ := NewClient(args) - hash, err := c.ProposeTransfer(context.Background(), &clients.TransferBatch{}) + hash, err := c.ProposeTransfer(context.Background(), &bridgeCore.TransferBatch{}) assert.Empty(t, hash) assert.True(t, errors.Is(err, expectedErr)) }) @@ -506,7 +676,7 @@ func TestClient_ProposeTransfer(t *testing.T) { } c, _ := NewClient(args) - hash, err := c.ProposeTransfer(context.Background(), &clients.TransferBatch{}) + hash, err := c.ProposeTransfer(context.Background(), &bridgeCore.TransferBatch{}) assert.Empty(t, hash) assert.True(t, errors.Is(err, clients.ErrMultisigContractPaused)) }) @@ -532,7 +702,7 @@ func TestClient_ProposeTransfer(t *testing.T) { hex.EncodeToString(big.NewInt(int64(batch.ID)).Bytes()), } for _, dt := range batch.Deposits { - dataStrings = append(dataStrings, depositToStrings(dt)...) + dataStrings = append(dataStrings, depositToString(dt)) } expectedDataField := strings.Join(dataStrings, "@") @@ -545,6 +715,54 @@ func TestClient_ProposeTransfer(t *testing.T) { }, } + hash, err := c.ProposeTransfer(context.Background(), batch) + assert.Nil(t, err) + assert.Equal(t, expectedHash, hash) + assert.True(t, sendWasCalled) + }) + t.Run("should propose transfer with SC call", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + args.Proxy = createMockProxy(make([][]byte, 0)) + expectedHash := "expected hash" + c, _ := NewClient(args) + sendWasCalled := false + batch := createMockBatch() + batch.Deposits[0].Data = bridgeTests.CallDataMock + var err error + batch.Deposits[0].DisplayableData = hex.EncodeToString(batch.Deposits[0].Data) + + c.txHandler = &bridgeTests.TxHandlerStub{ + SendTransactionReturnHashCalled: func(ctx context.Context, builder builders.TxDataBuilder, gasLimit uint64) (string, error) { + sendWasCalled = true + + dataField, errConvert := builder.ToDataString() + assert.Nil(t, errConvert) + + dataStrings := []string{ + proposeTransferFuncName, + hex.EncodeToString(big.NewInt(int64(batch.ID)).Bytes()), + } + extraGas := uint64(0) + for _, dt := range batch.Deposits { + dataStrings = append(dataStrings, depositToString(dt)) + if bytes.Equal(dt.Data, []byte{bridgeCore.MissingDataProtocolMarker}) { + continue + } + extraGas += (uint64(len(dt.Data))*2 + 1) * args.GasMapConfig.ScCallPerByte + } + + expectedDataField := strings.Join(dataStrings, "@") + assert.Equal(t, expectedDataField, dataField) + + expectedGasLimit := c.gasMapConfig.ProposeTransferBase + uint64(len(batch.Deposits))*c.gasMapConfig.ProposeTransferForEach + extraGas + assert.Equal(t, expectedGasLimit, gasLimit) + + return expectedHash, nil + }, + } + hash, err := c.ProposeTransfer(context.Background(), batch) assert.Nil(t, err) assert.Equal(t, expectedHash, hash) @@ -552,14 +770,13 @@ func TestClient_ProposeTransfer(t *testing.T) { }) } -func depositToStrings(dt *clients.DepositTransfer) []string { - result := []string{ - hex.EncodeToString(dt.FromBytes), - hex.EncodeToString(dt.ToBytes), - hex.EncodeToString(dt.ConvertedTokenBytes), - hex.EncodeToString(dt.Amount.Bytes()), - hex.EncodeToString(big.NewInt(int64(dt.Nonce)).Bytes()), - } +func depositToString(dt *bridgeCore.DepositTransfer) string { + result := hex.EncodeToString(dt.FromBytes) + result = result + "@" + hex.EncodeToString(dt.ToBytes) + result = result + "@" + hex.EncodeToString(dt.DestinationTokenBytes) + result = result + "@" + hex.EncodeToString(dt.Amount.Bytes()) + result = result + "@" + hex.EncodeToString(big.NewInt(0).SetUint64(dt.Nonce).Bytes()) + result = result + "@" + hex.EncodeToString(dt.Data) return result } @@ -659,7 +876,7 @@ func TestClient_PerformAction(t *testing.T) { } c, _ := NewClient(args) - hash, err := c.PerformAction(context.Background(), actionID, &clients.TransferBatch{}) + hash, err := c.PerformAction(context.Background(), actionID, &bridgeCore.TransferBatch{}) assert.Empty(t, hash) assert.True(t, errors.Is(err, expectedErr)) }) @@ -679,7 +896,7 @@ func TestClient_PerformAction(t *testing.T) { } c, _ := NewClient(args) - hash, err := c.PerformAction(context.Background(), actionID, &clients.TransferBatch{}) + hash, err := c.PerformAction(context.Background(), actionID, &bridgeCore.TransferBatch{}) assert.Empty(t, hash) assert.True(t, errors.Is(err, clients.ErrMultisigContractPaused)) }) @@ -706,8 +923,57 @@ func TestClient_PerformAction(t *testing.T) { } expectedDataField := strings.Join(dataStrings, "@") assert.Equal(t, expectedDataField, dataField) - expectedGasdLimit := c.gasMapConfig.PerformActionBase + uint64(len(batch.Statuses))*c.gasMapConfig.PerformActionForEach - assert.Equal(t, expectedGasdLimit, gasLimit) + expectedGasLimit := c.gasMapConfig.PerformActionBase + uint64(len(batch.Statuses))*c.gasMapConfig.PerformActionForEach + assert.Equal(t, expectedGasLimit, gasLimit) + + return expectedHash, nil + }, + } + + hash, err := c.PerformAction(context.Background(), actionID, batch) + assert.Nil(t, err) + assert.Equal(t, expectedHash, hash) + assert.True(t, sendWasCalled) + }) + t.Run("should perform action with SC call", func(t *testing.T) { + t.Parallel() + + args := createMockClientArgs() + args.Proxy = createMockProxy(make([][]byte, 0)) + expectedHash := "expected hash" + c, _ := NewClient(args) + sendWasCalled := false + batch := createMockBatch() + batch.Deposits[0].Data = bridgeTests.CallDataMock + var err error + batch.Deposits[0].DisplayableData = hex.EncodeToString(batch.Deposits[0].Data) + + c.txHandler = &bridgeTests.TxHandlerStub{ + SendTransactionReturnHashCalled: func(ctx context.Context, builder builders.TxDataBuilder, gasLimit uint64) (string, error) { + sendWasCalled = true + + dataField, errConvert := builder.ToDataString() + assert.Nil(t, errConvert) + + dataStrings := []string{ + performActionFuncName, + hex.EncodeToString(big.NewInt(int64(actionID)).Bytes()), + } + expectedDataField := strings.Join(dataStrings, "@") + assert.Equal(t, expectedDataField, dataField) + + extraGas := uint64(0) + for _, dt := range batch.Deposits { + if bytes.Equal(dt.Data, []byte{bridgeCore.MissingDataProtocolMarker}) { + continue + } + extraGas += (uint64(len(dt.Data))*2 + 1) * args.GasMapConfig.ScCallPerByte + extraGas += args.GasMapConfig.ScCallPerformForEach + } + + expectedGasLimit := c.gasMapConfig.PerformActionBase + uint64(len(batch.Statuses))*c.gasMapConfig.PerformActionForEach + expectedGasLimit += extraGas + assert.Equal(t, expectedGasLimit, gasLimit) return expectedHash, nil }, @@ -766,7 +1032,7 @@ func TestClient_CheckClientAvailability(t *testing.T) { for i := 0; i < 10; i++ { err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") } assert.True(t, statusHandler.GetIntMetric(bridgeCore.MetricLastBlockNonce) > 0) }) @@ -777,21 +1043,21 @@ func TestClient_CheckClientAvailability(t *testing.T) { incrementor = 0 // place a random message as to test it is reset - statusHandler.SetStringMetric(bridgeCore.MetricMultiversXClientStatus, ethmultiversx.ClientStatus(3).String()) + statusHandler.SetStringMetric(bridgeCore.MetricMultiversXClientStatus, bridgeCore.ClientStatus(3).String()) statusHandler.SetStringMetric(bridgeCore.MetricLastMultiversXClientError, "random") // this will just increment the retry counter - for i := 0; i < int(args.AllowDelta); i++ { + for i := 0; i < int(args.ClientAvailabilityAllowDelta); i++ { err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") } for i := 0; i < 10; i++ { - message := fmt.Sprintf("nonce %d fetched for %d times in a row", currentNonce, args.AllowDelta+uint64(i+1)) + message := fmt.Sprintf("nonce %d fetched for %d times in a row", currentNonce, args.ClientAvailabilityAllowDelta+uint64(i+1)) err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Unavailable, message) + checkStatusHandler(t, statusHandler, bridgeCore.Unavailable, message) } }) t.Run("same current nonce should error after a while and then recovers", func(t *testing.T) { @@ -801,24 +1067,24 @@ func TestClient_CheckClientAvailability(t *testing.T) { incrementor = 0 // this will just increment the retry counter - for i := 0; i < int(args.AllowDelta); i++ { + for i := 0; i < int(args.ClientAvailabilityAllowDelta); i++ { err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") } for i := 0; i < 10; i++ { - message := fmt.Sprintf("nonce %d fetched for %d times in a row", currentNonce, args.AllowDelta+uint64(i+1)) + message := fmt.Sprintf("nonce %d fetched for %d times in a row", currentNonce, args.ClientAvailabilityAllowDelta+uint64(i+1)) err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Unavailable, message) + checkStatusHandler(t, statusHandler, bridgeCore.Unavailable, message) } incrementor = 1 err := c.CheckClientAvailability(context.Background()) assert.Nil(t, err) - checkStatusHandler(t, statusHandler, ethmultiversx.Available, "") + checkStatusHandler(t, statusHandler, bridgeCore.Available, "") }) t.Run("get current nonce errors", func(t *testing.T) { resetClient(c) @@ -832,7 +1098,7 @@ func TestClient_CheckClientAvailability(t *testing.T) { } err := c.CheckClientAvailability(context.Background()) - checkStatusHandler(t, statusHandler, ethmultiversx.Unavailable, expectedErr.Error()) + checkStatusHandler(t, statusHandler, bridgeCore.Unavailable, expectedErr.Error()) assert.Equal(t, expectedErr, err) }) } @@ -846,7 +1112,7 @@ func resetClient(c *client) { c.statusHandler.SetIntMetric(bridgeCore.MetricLastBlockNonce, 0) } -func checkStatusHandler(t *testing.T, statusHandler *testsCommon.StatusHandlerMock, status ethmultiversx.ClientStatus, message string) { +func checkStatusHandler(t *testing.T, statusHandler *testsCommon.StatusHandlerMock, status bridgeCore.ClientStatus, message string) { assert.Equal(t, status.String(), statusHandler.GetStringMetric(bridgeCore.MetricMultiversXClientStatus)) assert.Equal(t, message, statusHandler.GetStringMetric(bridgeCore.MetricLastMultiversXClientError)) } diff --git a/clients/multiversx/errors.go b/clients/multiversx/errors.go index 73aa4a4a..289ef70e 100644 --- a/clients/multiversx/errors.go +++ b/clients/multiversx/errors.go @@ -16,7 +16,6 @@ var ( errNilRoleProvider = errors.New("nil role provider") errRelayerNotWhitelisted = errors.New("relayer not whitelisted") errNilNodeStatusResponse = errors.New("nil node status response") - - // ErrNoPendingBatchAvailable signals that no pending batch is available - ErrNoPendingBatchAvailable = errors.New("no pending batch available") + errInvalidBalance = errors.New("invalid balance") + errInsufficientESDTBalance = errors.New("insufficient ESDT balance") ) diff --git a/clients/multiversx/interface.go b/clients/multiversx/interface.go index 83d96374..5916eb2f 100644 --- a/clients/multiversx/interface.go +++ b/clients/multiversx/interface.go @@ -3,6 +3,7 @@ package multiversx import ( "context" + "github.com/multiversx/mx-chain-core-go/data/api" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-sdk-go/builders" "github.com/multiversx/mx-sdk-go/core" @@ -18,12 +19,15 @@ type Proxy interface { GetAccount(ctx context.Context, address core.AddressHandler) (*data.Account, error) GetNetworkStatus(ctx context.Context, shardID uint32) (*data.NetworkStatus, error) GetShardOfAddress(ctx context.Context, bech32Address string) (uint32, error) + GetESDTTokenData(ctx context.Context, address core.AddressHandler, tokenIdentifier string, queryOptions api.AccountQueryOptions) (*data.ESDTFungibleTokenData, error) + GetTransactionInfoWithResults(ctx context.Context, hash string) (*data.TransactionInfo, error) + ProcessTransactionStatus(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) IsInterfaceNil() bool } // NonceTransactionsHandler represents the interface able to handle the current nonce and the transactions resend mechanism type NonceTransactionsHandler interface { - GetNonce(ctx context.Context, address core.AddressHandler) (uint64, error) + ApplyNonceAndGasPrice(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error SendTransaction(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) Close() error } diff --git a/clients/multiversx/mxClientDataGetter.go b/clients/multiversx/mxClientDataGetter.go index 90018d09..d1f78491 100644 --- a/clients/multiversx/mxClientDataGetter.go +++ b/clients/multiversx/mxClientDataGetter.go @@ -8,6 +8,8 @@ import ( "sync" "github.com/multiversx/mx-bridge-eth-go/clients" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/errors" "github.com/multiversx/mx-chain-core-go/core/check" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-sdk-go/builders" @@ -19,6 +21,7 @@ const ( okCodeAfterExecution = "ok" internalError = "internal error" getCurrentTxBatchFuncName = "getCurrentTxBatch" + getBatchFuncName = "getBatch" wasTransferActionProposedFuncName = "wasTransferActionProposed" wasActionExecutedFuncName = "wasActionExecuted" getActionIdForTransferBatchFuncName = "getActionIdForTransferBatch" @@ -33,24 +36,34 @@ const ( signedFuncName = "signed" getAllStakedRelayersFuncName = "getAllStakedRelayers" isPausedFuncName = "isPaused" + isMintBurnTokenFuncName = "isMintBurnToken" + isNativeTokenFuncName = "isNativeToken" + getTotalBalances = "getTotalBalances" + getMintBalances = "getMintBalances" + getBurnBalances = "getBurnBalances" + getAllKnownTokens = "getAllKnownTokens" + getLastBatchId = "getLastBatchId" ) // ArgsMXClientDataGetter is the arguments DTO used in the NewMXClientDataGetter constructor type ArgsMXClientDataGetter struct { MultisigContractAddress core.AddressHandler + SafeContractAddress core.AddressHandler RelayerAddress core.AddressHandler Proxy Proxy Log logger.Logger } type mxClientDataGetter struct { - multisigContractAddress core.AddressHandler - relayerAddress core.AddressHandler - proxy Proxy - log logger.Logger - mutNodeStatus sync.Mutex - wasShardIDFetched bool - shardID uint32 + multisigContractAddress core.AddressHandler + safeContractAddress core.AddressHandler + bech32MultisigContractAddress string + relayerAddress core.AddressHandler + proxy Proxy + log logger.Logger + mutNodeStatus sync.Mutex + wasShardIDFetched bool + shardID uint32 } // NewMXClientDataGetter creates a new instance of the dataGetter type @@ -67,12 +80,21 @@ func NewMXClientDataGetter(args ArgsMXClientDataGetter) (*mxClientDataGetter, er if check.IfNil(args.MultisigContractAddress) { return nil, fmt.Errorf("%w for the MultisigContractAddress argument", errNilAddressHandler) } + if check.IfNil(args.SafeContractAddress) { + return nil, fmt.Errorf("%w for the SafeContractAddress argument", errNilAddressHandler) + } + bech32Address, err := args.MultisigContractAddress.AddressAsBech32String() + if err != nil { + return nil, fmt.Errorf("%w for %x", err, args.MultisigContractAddress.AddressBytes()) + } return &mxClientDataGetter{ - multisigContractAddress: args.MultisigContractAddress, - relayerAddress: args.RelayerAddress, - proxy: args.Proxy, - log: args.Log, + multisigContractAddress: args.MultisigContractAddress, + safeContractAddress: args.SafeContractAddress, + bech32MultisigContractAddress: bech32Address, + relayerAddress: args.RelayerAddress, + proxy: args.Proxy, + log: args.Log, }, nil } @@ -93,7 +115,7 @@ func (dataGetter *mxClientDataGetter) ExecuteQueryReturningBytes(ctx context.Con "response.ReturnCode", response.Data.ReturnCode, "response.ReturnData", fmt.Sprintf("%+v", response.Data.ReturnData)) if response.Data.ReturnCode != okCodeAfterExecution { - return nil, NewQueryResponseError( + return nil, errors.NewQueryResponseError( response.Data.ReturnCode, response.Data.ReturnMessage, request.FuncName, @@ -131,7 +153,7 @@ func (dataGetter *mxClientDataGetter) getShardID(ctx context.Context) (uint32, e } var err error - dataGetter.shardID, err = dataGetter.proxy.GetShardOfAddress(ctx, dataGetter.multisigContractAddress.AddressAsBech32String()) + dataGetter.shardID, err = dataGetter.proxy.GetShardOfAddress(ctx, dataGetter.bech32MultisigContractAddress) if err == nil { dataGetter.wasShardIDFetched = true } @@ -160,7 +182,7 @@ func (dataGetter *mxClientDataGetter) parseBool(buff []byte, funcName string, ad result, err := strconv.ParseBool(fmt.Sprintf("%d", buff[0])) if err != nil { - return false, NewQueryResponseError( + return false, errors.NewQueryResponseError( internalError, fmt.Sprintf("error converting the received bytes to bool, %s", err.Error()), funcName, @@ -188,7 +210,7 @@ func (dataGetter *mxClientDataGetter) ExecuteQueryReturningUint64(ctx context.Co num, err := parseUInt64FromByteSlice(response[0]) if err != nil { - return 0, NewQueryResponseError( + return 0, errors.NewQueryResponseError( internalError, err.Error(), request.FuncName, @@ -200,6 +222,24 @@ func (dataGetter *mxClientDataGetter) ExecuteQueryReturningUint64(ctx context.Co return num, nil } +// ExecuteQueryReturningBigInt will try to execute the provided query and return the result as big.Int +func (dataGetter *mxClientDataGetter) ExecuteQueryReturningBigInt(ctx context.Context, request *data.VmValueRequest) (*big.Int, error) { + response, err := dataGetter.ExecuteQueryReturningBytes(ctx, request) + if err != nil { + return nil, err + } + + if len(response) == 0 { + return big.NewInt(0), nil + } + if len(response[0]) == 0 { + return big.NewInt(0), nil + } + + num := big.NewInt(0).SetBytes(response[0]) + return num, nil +} + func parseUInt64FromByteSlice(bytes []byte) (uint64, error) { num := big.NewInt(0).SetBytes(bytes) if !num.IsUint64() { @@ -227,6 +267,15 @@ func (dataGetter *mxClientDataGetter) executeQueryUint64FromBuilder(ctx context. return dataGetter.ExecuteQueryReturningUint64(ctx, vmValuesRequest) } +func (dataGetter *mxClientDataGetter) executeQueryBigIntFromBuilder(ctx context.Context, builder builders.VMQueryBuilder) (*big.Int, error) { + vmValuesRequest, err := builder.ToVmValueRequest() + if err != nil { + return nil, err + } + + return dataGetter.ExecuteQueryReturningBigInt(ctx, vmValuesRequest) +} + func (dataGetter *mxClientDataGetter) executeQueryBoolFromBuilder(ctx context.Context, builder builders.VMQueryBuilder) (bool, error) { vmValuesRequest, err := builder.ToVmValueRequest() if err != nil { @@ -236,21 +285,34 @@ func (dataGetter *mxClientDataGetter) executeQueryBoolFromBuilder(ctx context.Co return dataGetter.ExecuteQueryReturningBool(ctx, vmValuesRequest) } -func (dataGetter *mxClientDataGetter) createDefaultVmQueryBuilder() builders.VMQueryBuilder { +func (dataGetter *mxClientDataGetter) createMultisigDefaultVmQueryBuilder() builders.VMQueryBuilder { return builders.NewVMQueryBuilder().Address(dataGetter.multisigContractAddress).CallerAddress(dataGetter.relayerAddress) } +func (dataGetter *mxClientDataGetter) createSafeDefaultVmQueryBuilder() builders.VMQueryBuilder { + return builders.NewVMQueryBuilder().Address(dataGetter.safeContractAddress).CallerAddress(dataGetter.relayerAddress) +} + // GetCurrentBatchAsDataBytes will assemble a builder and query the proxy for the current pending batch func (dataGetter *mxClientDataGetter) GetCurrentBatchAsDataBytes(ctx context.Context) ([][]byte, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(getCurrentTxBatchFuncName) return dataGetter.executeQueryFromBuilder(ctx, builder) } +// GetBatchAsDataBytes will assemble a builder and query the proxy for the batch info +func (dataGetter *mxClientDataGetter) GetBatchAsDataBytes(ctx context.Context, batchID uint64) ([][]byte, error) { + builder := dataGetter.createMultisigDefaultVmQueryBuilder() + builder.Function(getBatchFuncName) + builder.ArgInt64(int64(batchID)) + + return dataGetter.executeQueryFromBuilder(ctx, builder) +} + // GetTokenIdForErc20Address will assemble a builder and query the proxy for a token id given a specific erc20 address func (dataGetter *mxClientDataGetter) GetTokenIdForErc20Address(ctx context.Context, erc20Address []byte) ([][]byte, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(getTokenIdForErc20AddressFuncName) builder.ArgBytes(erc20Address) @@ -259,53 +321,53 @@ func (dataGetter *mxClientDataGetter) GetTokenIdForErc20Address(ctx context.Cont // GetERC20AddressForTokenId will assemble a builder and query the proxy for an erc20 address given a specific token id func (dataGetter *mxClientDataGetter) GetERC20AddressForTokenId(ctx context.Context, tokenId []byte) ([][]byte, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(getErc20AddressForTokenIdFuncName) builder.ArgBytes(tokenId) return dataGetter.executeQueryFromBuilder(ctx, builder) } // WasProposedTransfer returns true if the transfer action proposed was triggered -func (dataGetter *mxClientDataGetter) WasProposedTransfer(ctx context.Context, batch *clients.TransferBatch) (bool, error) { +func (dataGetter *mxClientDataGetter) WasProposedTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) { if batch == nil { return false, clients.ErrNilBatch } - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(wasTransferActionProposedFuncName).ArgInt64(int64(batch.ID)) - addBatchInfo(builder, batch) + dataGetter.addBatchInfo(builder, batch) return dataGetter.executeQueryBoolFromBuilder(ctx, builder) } // WasExecuted returns true if the provided actionID was executed or not func (dataGetter *mxClientDataGetter) WasExecuted(ctx context.Context, actionID uint64) (bool, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(wasActionExecutedFuncName).ArgInt64(int64(actionID)) return dataGetter.executeQueryBoolFromBuilder(ctx, builder) } // GetActionIDForProposeTransfer returns the action ID for the proposed transfer operation -func (dataGetter *mxClientDataGetter) GetActionIDForProposeTransfer(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { +func (dataGetter *mxClientDataGetter) GetActionIDForProposeTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { if batch == nil { return 0, clients.ErrNilBatch } - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(getActionIdForTransferBatchFuncName).ArgInt64(int64(batch.ID)) - addBatchInfo(builder, batch) + dataGetter.addBatchInfo(builder, batch) return dataGetter.executeQueryUint64FromBuilder(ctx, builder) } // WasProposedSetStatus returns true if the proposed set status was triggered -func (dataGetter *mxClientDataGetter) WasProposedSetStatus(ctx context.Context, batch *clients.TransferBatch) (bool, error) { +func (dataGetter *mxClientDataGetter) WasProposedSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) { if batch == nil { return false, clients.ErrNilBatch } - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(wasSetCurrentTransactionBatchStatusActionProposedFuncName).ArgInt64(int64(batch.ID)) for _, stat := range batch.Statuses { builder.ArgBytes([]byte{stat}) @@ -316,7 +378,7 @@ func (dataGetter *mxClientDataGetter) WasProposedSetStatus(ctx context.Context, // GetTransactionsStatuses will return the transactions statuses from the batch ID func (dataGetter *mxClientDataGetter) GetTransactionsStatuses(ctx context.Context, batchID uint64) ([]byte, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(getStatusesAfterExecutionFuncName).ArgInt64(int64(batchID)) values, err := dataGetter.executeQueryFromBuilder(ctx, builder) @@ -327,7 +389,7 @@ func (dataGetter *mxClientDataGetter) GetTransactionsStatuses(ctx context.Contex return nil, fmt.Errorf("%w for batch ID %v", errNoStatusForBatchID, batchID) } - isFinished, err := dataGetter.parseBool(values[0], getStatusesAfterExecutionFuncName, dataGetter.multisigContractAddress.AddressAsBech32String()) + isFinished, err := dataGetter.parseBool(values[0], getStatusesAfterExecutionFuncName, dataGetter.bech32MultisigContractAddress) if err != nil { return nil, err } @@ -351,12 +413,12 @@ func (dataGetter *mxClientDataGetter) GetTransactionsStatuses(ctx context.Contex } // GetActionIDForSetStatusOnPendingTransfer returns the action ID for setting the status on the pending transfer batch -func (dataGetter *mxClientDataGetter) GetActionIDForSetStatusOnPendingTransfer(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { +func (dataGetter *mxClientDataGetter) GetActionIDForSetStatusOnPendingTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { if batch == nil { return 0, clients.ErrNilBatch } - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(getActionIdForSetCurrentTransactionBatchStatusFuncName).ArgInt64(int64(batch.ID)) for _, stat := range batch.Statuses { builder.ArgBytes([]byte{stat}) @@ -367,7 +429,7 @@ func (dataGetter *mxClientDataGetter) GetActionIDForSetStatusOnPendingTransfer(c // QuorumReached returns true if the provided action ID reached the set quorum func (dataGetter *mxClientDataGetter) QuorumReached(ctx context.Context, actionID uint64) (bool, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(quorumReachedFuncName).ArgInt64(int64(actionID)) return dataGetter.executeQueryBoolFromBuilder(ctx, builder) @@ -375,21 +437,21 @@ func (dataGetter *mxClientDataGetter) QuorumReached(ctx context.Context, actionI // GetLastExecutedEthBatchID returns the last executed Ethereum batch ID func (dataGetter *mxClientDataGetter) GetLastExecutedEthBatchID(ctx context.Context) (uint64, error) { - builder := dataGetter.createDefaultVmQueryBuilder().Function(getLastExecutedEthBatchIdFuncName) + builder := dataGetter.createMultisigDefaultVmQueryBuilder().Function(getLastExecutedEthBatchIdFuncName) return dataGetter.executeQueryUint64FromBuilder(ctx, builder) } // GetLastExecutedEthTxID returns the last executed Ethereum deposit ID func (dataGetter *mxClientDataGetter) GetLastExecutedEthTxID(ctx context.Context) (uint64, error) { - builder := dataGetter.createDefaultVmQueryBuilder().Function(getLastExecutedEthTxId) + builder := dataGetter.createMultisigDefaultVmQueryBuilder().Function(getLastExecutedEthTxId) return dataGetter.executeQueryUint64FromBuilder(ctx, builder) } // WasSigned returns true if the action was already signed by the current relayer func (dataGetter *mxClientDataGetter) WasSigned(ctx context.Context, actionID uint64) (bool, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(signedFuncName).ArgAddress(dataGetter.relayerAddress).ArgInt64(int64(actionID)) return dataGetter.executeQueryBoolFromBuilder(ctx, builder) @@ -397,7 +459,7 @@ func (dataGetter *mxClientDataGetter) WasSigned(ctx context.Context, actionID ui // GetAllStakedRelayers returns all staked relayers defined in MultiversX SC func (dataGetter *mxClientDataGetter) GetAllStakedRelayers(ctx context.Context) ([][]byte, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(getAllStakedRelayersFuncName) return dataGetter.executeQueryFromBuilder(ctx, builder) @@ -405,30 +467,83 @@ func (dataGetter *mxClientDataGetter) GetAllStakedRelayers(ctx context.Context) // IsPaused returns true if the multisig contract is paused func (dataGetter *mxClientDataGetter) IsPaused(ctx context.Context) (bool, error) { - builder := dataGetter.createDefaultVmQueryBuilder() + builder := dataGetter.createMultisigDefaultVmQueryBuilder() builder.Function(isPausedFuncName) return dataGetter.executeQueryBoolFromBuilder(ctx, builder) } -func getStatusFromBuff(buff []byte) (byte, error) { - if len(buff) == 0 { - return 0, errMalformedBatchResponse - } +func (dataGetter *mxClientDataGetter) isMintBurnToken(ctx context.Context, token []byte) (bool, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(isMintBurnTokenFuncName).ArgBytes(token) - return buff[len(buff)-1], nil + return dataGetter.executeQueryBoolFromBuilder(ctx, builder) +} + +// isNativeToken returns true if the token is native +func (dataGetter *mxClientDataGetter) isNativeToken(ctx context.Context, token []byte) (bool, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(isNativeTokenFuncName).ArgBytes(token) + + return dataGetter.executeQueryBoolFromBuilder(ctx, builder) +} + +func (dataGetter *mxClientDataGetter) getTotalBalances(ctx context.Context, token []byte) (*big.Int, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(getTotalBalances).ArgBytes(token) + + return dataGetter.executeQueryBigIntFromBuilder(ctx, builder) +} + +func (dataGetter *mxClientDataGetter) getMintBalances(ctx context.Context, token []byte) (*big.Int, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(getMintBalances).ArgBytes(token) + + return dataGetter.executeQueryBigIntFromBuilder(ctx, builder) +} + +func (dataGetter *mxClientDataGetter) getBurnBalances(ctx context.Context, token []byte) (*big.Int, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(getBurnBalances).ArgBytes(token) + + return dataGetter.executeQueryBigIntFromBuilder(ctx, builder) } -func addBatchInfo(builder builders.VMQueryBuilder, batch *clients.TransferBatch) { +func (dataGetter *mxClientDataGetter) addBatchInfo(builder builders.VMQueryBuilder, batch *bridgeCore.TransferBatch) { for _, dt := range batch.Deposits { builder.ArgBytes(dt.FromBytes). ArgBytes(dt.ToBytes). - ArgBytes(dt.ConvertedTokenBytes). + ArgBytes(dt.DestinationTokenBytes). ArgBigInt(dt.Amount). - ArgInt64(int64(dt.Nonce)) + ArgInt64(int64(dt.Nonce)). + ArgBytes(dt.Data) } } +func getStatusFromBuff(buff []byte) (byte, error) { + if len(buff) == 0 { + return 0, errMalformedBatchResponse + } + + return buff[len(buff)-1], nil +} + +// GetAllKnownTokens returns all registered tokens +func (dataGetter *mxClientDataGetter) GetAllKnownTokens(ctx context.Context) ([][]byte, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(getAllKnownTokens) + + return dataGetter.executeQueryFromBuilder(ctx, builder) +} + +// GetLastMvxBatchID returns the highest batch ID the safe contract reached. This might be a WIP batch that is not executable yet +func (dataGetter *mxClientDataGetter) GetLastMvxBatchID(ctx context.Context) (uint64, error) { + builder := dataGetter.createSafeDefaultVmQueryBuilder() + builder.Function(getLastBatchId) + + return dataGetter.executeQueryUint64FromBuilder(ctx, builder) +} + // IsInterfaceNil returns true if there is no value under the interface func (dataGetter *mxClientDataGetter) IsInterfaceNil() bool { return dataGetter == nil diff --git a/clients/multiversx/mxClientDataGetter_test.go b/clients/multiversx/mxClientDataGetter_test.go index fab0bdb8..2531acd2 100644 --- a/clients/multiversx/mxClientDataGetter_test.go +++ b/clients/multiversx/mxClientDataGetter_test.go @@ -11,11 +11,15 @@ import ( "testing" "github.com/multiversx/mx-bridge-eth-go/clients" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + bridgeErrors "github.com/multiversx/mx-bridge-eth-go/errors" + bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/multiversx/mx-bridge-eth-go/testsCommon/interactors" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/vm" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-sdk-go/builders" + "github.com/multiversx/mx-sdk-go/core" "github.com/multiversx/mx-sdk-go/data" "github.com/stretchr/testify/assert" ) @@ -35,11 +39,18 @@ func createMockArgsMXClientDataGetter() ArgsMXClientDataGetter { } args.MultisigContractAddress, _ = data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf") + args.SafeContractAddress, _ = data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus") args.RelayerAddress, _ = data.NewAddressFromBech32String("erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede") return args } +func getBech32Address(addressHandler core.AddressHandler) string { + bech32Address, _ := addressHandler.AddressAsBech32String() + + return bech32Address +} + func createMockProxy(returningBytes [][]byte) *interactors.ProxyStub { return &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { @@ -53,34 +64,38 @@ func createMockProxy(returningBytes [][]byte) *interactors.ProxyStub { } } -func createMockBatch() *clients.TransferBatch { - return &clients.TransferBatch{ +func createMockBatch() *bridgeCore.TransferBatch { + return &bridgeCore.TransferBatch{ ID: 112233, - Deposits: []*clients.DepositTransfer{ + Deposits: []*bridgeCore.DepositTransfer{ { - Nonce: 1, - ToBytes: []byte("to1"), - DisplayableTo: "to1", - FromBytes: []byte("from1"), - DisplayableFrom: "from1", - TokenBytes: []byte("token1"), - ConvertedTokenBytes: []byte("converted_token1"), - DisplayableToken: "token1", - Amount: big.NewInt(2), + Nonce: 1, + ToBytes: []byte("to1"), + DisplayableTo: "to1", + FromBytes: []byte("from1"), + DisplayableFrom: "from1", + SourceTokenBytes: []byte("token1"), + DestinationTokenBytes: []byte("converted_token1"), + DisplayableToken: "token1", + Amount: big.NewInt(2), + Data: []byte{0x00}, + DisplayableData: "00", }, { - Nonce: 3, - ToBytes: []byte("to2"), - DisplayableTo: "to2", - FromBytes: []byte("from2"), - DisplayableFrom: "from2", - TokenBytes: []byte("token2"), - ConvertedTokenBytes: []byte("converted_token2"), - DisplayableToken: "token2", - Amount: big.NewInt(4), + Nonce: 3, + ToBytes: []byte("to2"), + DisplayableTo: "to2", + FromBytes: []byte("from2"), + DisplayableFrom: "from2", + SourceTokenBytes: []byte("token2"), + DestinationTokenBytes: []byte("converted_token2"), + DisplayableToken: "token2", + Amount: big.NewInt(4), + Data: []byte{0x00}, + DisplayableData: "00", }, }, - Statuses: []byte{clients.Rejected, clients.Executed}, + Statuses: []byte{bridgeCore.Rejected, bridgeCore.Executed}, } } @@ -118,6 +133,17 @@ func TestNewMXClientDataGetter(t *testing.T) { assert.True(t, strings.Contains(err.Error(), "MultisigContractAddress")) assert.True(t, check.IfNil(dg)) }) + t.Run("nil safe contact address", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + args.SafeContractAddress = nil + + dg, err := NewMXClientDataGetter(args) + assert.True(t, errors.Is(err, errNilAddressHandler)) + assert.True(t, strings.Contains(err.Error(), "SafeContractAddress")) + assert.True(t, check.IfNil(dg)) + }) t.Run("nil relayer address", func(t *testing.T) { t.Parallel() @@ -173,7 +199,7 @@ func TestMXClientDataGetter_ExecuteQueryReturningBytes(t *testing.T) { dg, _ := NewMXClientDataGetter(args) - expectedErr := NewQueryResponseError(returnCode, returnMessage, calledFunction, dg.multisigContractAddress.AddressAsBech32String(), calledArgs...) + expectedErr := bridgeErrors.NewQueryResponseError(returnCode, returnMessage, calledFunction, getBech32Address(dg.multisigContractAddress), calledArgs...) dg.proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { return &data.VmValuesResponseData{ @@ -193,9 +219,9 @@ func TestMXClientDataGetter_ExecuteQueryReturningBytes(t *testing.T) { } request := &data.VmValueRequest{ - Address: dg.multisigContractAddress.AddressAsBech32String(), + Address: getBech32Address(dg.multisigContractAddress), FuncName: calledFunction, - CallerAddr: dg.relayerAddress.AddressAsBech32String(), + CallerAddr: getBech32Address(dg.relayerAddress), CallValue: "0", Args: calledArgs, } @@ -229,9 +255,9 @@ func TestMXClientDataGetter_ExecuteQueryReturningBytes(t *testing.T) { } request := &data.VmValueRequest{ - Address: dg.multisigContractAddress.AddressAsBech32String(), + Address: getBech32Address(dg.multisigContractAddress), FuncName: calledFunction, - CallerAddr: dg.relayerAddress.AddressAsBech32String(), + CallerAddr: getBech32Address(dg.relayerAddress), CallValue: "0", Args: calledArgs, } @@ -281,7 +307,7 @@ func TestMXClientDataGetter_ExecuteQueryReturningBool(t *testing.T) { dg, _ := NewMXClientDataGetter(args) dg.proxy = createMockProxy([][]byte{[]byte("random bytes")}) - expectedError := NewQueryResponseError( + expectedError := bridgeErrors.NewQueryResponseError( internalError, `error converting the received bytes to bool, strconv.ParseBool: parsing "114": invalid syntax`, "", @@ -349,7 +375,7 @@ func TestMXClientDataGetter_ExecuteQueryReturningUint64(t *testing.T) { dg, _ := NewMXClientDataGetter(args) dg.proxy = createMockProxy([][]byte{[]byte("random bytes")}) - expectedError := NewQueryResponseError( + expectedError := bridgeErrors.NewQueryResponseError( internalError, errNotUint64Bytes.Error(), "", @@ -378,6 +404,60 @@ func TestMXClientDataGetter_ExecuteQueryReturningUint64(t *testing.T) { }) } +func TestMXClientDataGetter_ExecuteQueryReturningBigInt(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + t.Run("nil request", func(t *testing.T) { + t.Parallel() + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.ExecuteQueryReturningBigInt(context.Background(), nil) + assert.Nil(t, result) + assert.Equal(t, errNilRequest, err) + }) + t.Run("empty response", func(t *testing.T) { + t.Parallel() + + dg, _ := NewMXClientDataGetter(args) + dg.proxy = createMockProxy(make([][]byte, 0)) + + result, err := dg.ExecuteQueryReturningBigInt(context.Background(), &data.VmValueRequest{}) + assert.Equal(t, big.NewInt(0), result) + assert.Nil(t, err) + }) + t.Run("empty byte slice on first element", func(t *testing.T) { + t.Parallel() + + dg, _ := NewMXClientDataGetter(args) + dg.proxy = createMockProxy([][]byte{make([]byte, 0)}) + + result, err := dg.ExecuteQueryReturningBigInt(context.Background(), &data.VmValueRequest{}) + assert.Equal(t, big.NewInt(0), result) + assert.Nil(t, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + dg, _ := NewMXClientDataGetter(args) + largeNumber := new(big.Int) + largeNumber.SetString("18446744073709551616", 10) + dg.proxy = createMockProxy([][]byte{largeNumber.Bytes()}) + + result, err := dg.ExecuteQueryReturningBigInt(context.Background(), &data.VmValueRequest{}) + assert.Equal(t, largeNumber, result) + assert.Nil(t, err) + + dg.proxy = createMockProxy([][]byte{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}) + + result, err = dg.ExecuteQueryReturningBigInt(context.Background(), &data.VmValueRequest{}) + largeNumber.SetString("79228162514264337593543950335", 10) + assert.Equal(t, largeNumber, result) + assert.Nil(t, err) + }) +} + func TestMXClientDataGetter_GetCurrentBatchAsDataBytes(t *testing.T) { t.Parallel() @@ -385,8 +465,8 @@ func TestMXClientDataGetter_GetCurrentBatchAsDataBytes(t *testing.T) { returningBytes := [][]byte{[]byte("buff0"), []byte("buff1"), []byte("buff2")} args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) assert.Equal(t, 0, len(vmRequest.CallValue)) assert.Equal(t, getCurrentTxBatchFuncName, vmRequest.FuncName) @@ -437,8 +517,8 @@ func TestMXClientDataGetter_GetTokenIdForErc20Address(t *testing.T) { returningBytes := [][]byte{[]byte(erdAddress)} args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) assert.Equal(t, 0, len(vmRequest.CallValue)) assert.Equal(t, []string{hex.EncodeToString([]byte(erc20Address))}, vmRequest.Args) assert.Equal(t, getTokenIdForErc20AddressFuncName, vmRequest.FuncName) @@ -468,8 +548,8 @@ func TestMXClientDataGetter_GetERC20AddressForTokenId(t *testing.T) { returningBytes := [][]byte{[]byte(erc20Address)} args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) assert.Equal(t, 0, len(vmRequest.CallValue)) assert.Equal(t, []string{hex.EncodeToString([]byte(erdAddress))}, vmRequest.Args) assert.Equal(t, getErc20AddressForTokenIdFuncName, vmRequest.FuncName) @@ -508,11 +588,13 @@ func TestMXClientDataGetter_WasProposedTransfer(t *testing.T) { args := createMockArgsMXClientDataGetter() proxyCalled := false + batch := createMockBatch() + args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, wasTransferActionProposedFuncName, vmRequest.FuncName) @@ -524,12 +606,14 @@ func TestMXClientDataGetter_WasProposedTransfer(t *testing.T) { hex.EncodeToString([]byte("converted_token1")), hex.EncodeToString(big.NewInt(2).Bytes()), hex.EncodeToString(big.NewInt(1).Bytes()), + hex.EncodeToString([]byte{bridgeCore.MissingDataProtocolMarker}), hex.EncodeToString([]byte("from2")), hex.EncodeToString([]byte("to2")), hex.EncodeToString([]byte("converted_token2")), hex.EncodeToString(big.NewInt(4).Bytes()), hex.EncodeToString(big.NewInt(3).Bytes()), + hex.EncodeToString([]byte{bridgeCore.MissingDataProtocolMarker}), } assert.Equal(t, expectedArgs, vmRequest.Args) @@ -545,7 +629,57 @@ func TestMXClientDataGetter_WasProposedTransfer(t *testing.T) { dg, _ := NewMXClientDataGetter(args) + result, err := dg.WasProposedTransfer(context.Background(), batch) + assert.True(t, result) + assert.Nil(t, err) + assert.True(t, proxyCalled) + }) + t.Run("should work with SC calls", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false batch := createMockBatch() + batch.Deposits[0].Data = bridgeTests.CallDataMock + + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, wasTransferActionProposedFuncName, vmRequest.FuncName) + + expectedArgs := []string{ + hex.EncodeToString(big.NewInt(112233).Bytes()), + + hex.EncodeToString([]byte("from1")), + hex.EncodeToString([]byte("to1")), + hex.EncodeToString([]byte("converted_token1")), + hex.EncodeToString(big.NewInt(2).Bytes()), + hex.EncodeToString(big.NewInt(1).Bytes()), + hex.EncodeToString(bridgeTests.CallDataMock), + + hex.EncodeToString([]byte("from2")), + hex.EncodeToString([]byte("to2")), + hex.EncodeToString([]byte("converted_token2")), + hex.EncodeToString(big.NewInt(4).Bytes()), + hex.EncodeToString(big.NewInt(3).Bytes()), + hex.EncodeToString([]byte{bridgeCore.MissingDataProtocolMarker}), + } + + assert.Equal(t, expectedArgs, vmRequest.Args) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{{1}}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) result, err := dg.WasProposedTransfer(context.Background(), batch) assert.True(t, result) @@ -562,8 +696,8 @@ func TestMXClientDataGetter_WasExecuted(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, wasActionExecutedFuncName, vmRequest.FuncName) @@ -629,11 +763,12 @@ func TestMXClientDataGetter_GetActionIDForProposeTransfer(t *testing.T) { args := createMockArgsMXClientDataGetter() proxyCalled := false + batch := createMockBatch() args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, getActionIdForTransferBatchFuncName, vmRequest.FuncName) @@ -645,12 +780,14 @@ func TestMXClientDataGetter_GetActionIDForProposeTransfer(t *testing.T) { hex.EncodeToString([]byte("converted_token1")), hex.EncodeToString(big.NewInt(2).Bytes()), hex.EncodeToString(big.NewInt(1).Bytes()), + hex.EncodeToString([]byte{bridgeCore.MissingDataProtocolMarker}), hex.EncodeToString([]byte("from2")), hex.EncodeToString([]byte("to2")), hex.EncodeToString([]byte("converted_token2")), hex.EncodeToString(big.NewInt(4).Bytes()), hex.EncodeToString(big.NewInt(3).Bytes()), + hex.EncodeToString([]byte{bridgeCore.MissingDataProtocolMarker}), } assert.Equal(t, expectedArgs, vmRequest.Args) @@ -666,7 +803,56 @@ func TestMXClientDataGetter_GetActionIDForProposeTransfer(t *testing.T) { dg, _ := NewMXClientDataGetter(args) + result, err := dg.GetActionIDForProposeTransfer(context.Background(), batch) + assert.Equal(t, uint64(1234), result) + assert.Nil(t, err) + assert.True(t, proxyCalled) + }) + t.Run("should work with SC calls", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false batch := createMockBatch() + batch.Deposits[0].Data = bridgeTests.CallDataMock + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getActionIdForTransferBatchFuncName, vmRequest.FuncName) + + expectedArgs := []string{ + hex.EncodeToString(big.NewInt(112233).Bytes()), + + hex.EncodeToString([]byte("from1")), + hex.EncodeToString([]byte("to1")), + hex.EncodeToString([]byte("converted_token1")), + hex.EncodeToString(big.NewInt(2).Bytes()), + hex.EncodeToString(big.NewInt(1).Bytes()), + hex.EncodeToString(bridgeTests.CallDataMock), + + hex.EncodeToString([]byte("from2")), + hex.EncodeToString([]byte("to2")), + hex.EncodeToString([]byte("converted_token2")), + hex.EncodeToString(big.NewInt(4).Bytes()), + hex.EncodeToString(big.NewInt(3).Bytes()), + hex.EncodeToString([]byte{bridgeCore.MissingDataProtocolMarker}), + } + + assert.Equal(t, expectedArgs, vmRequest.Args) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{big.NewInt(1234).Bytes()}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) result, err := dg.GetActionIDForProposeTransfer(context.Background(), batch) assert.Equal(t, uint64(1234), result) @@ -697,8 +883,8 @@ func TestMXClientDataGetter_WasProposedSetStatus(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, wasSetCurrentTransactionBatchStatusActionProposedFuncName, vmRequest.FuncName) @@ -773,7 +959,7 @@ func TestMXClientDataGetter_GetTransactionsStatuses(t *testing.T) { result, err := dg.GetTransactionsStatuses(context.Background(), batchID) assert.Nil(t, result) - expectedErr := NewQueryResponseError(internalError, `error converting the received bytes to bool, strconv.ParseBool: parsing "56": invalid syntax`, + expectedErr := bridgeErrors.NewQueryResponseError(internalError, `error converting the received bytes to bool, strconv.ParseBool: parsing "56": invalid syntax`, "getStatusesAfterExecution", "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf") assert.Equal(t, expectedErr, err) }) @@ -823,8 +1009,8 @@ func TestMXClientDataGetter_GetTransactionsStatuses(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, getStatusesAfterExecutionFuncName, vmRequest.FuncName) @@ -875,8 +1061,8 @@ func TestMXClientDataGetter_GetActionIDForSetStatusOnPendingTransfer(t *testing. args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, getActionIdForSetCurrentTransactionBatchStatusFuncName, vmRequest.FuncName) @@ -916,8 +1102,8 @@ func TestMXClientDataGetter_QuorumReached(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, quorumReachedFuncName, vmRequest.FuncName) @@ -950,8 +1136,8 @@ func TestMXClientDataGetter_GetLastExecutedEthBatchID(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, getLastExecutedEthBatchIdFuncName, vmRequest.FuncName) assert.Nil(t, vmRequest.Args) @@ -982,8 +1168,8 @@ func TestMXClientDataGetter_GetLastExecutedEthTxID(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, getLastExecutedEthTxId, vmRequest.FuncName) assert.Nil(t, vmRequest.Args) @@ -1014,8 +1200,8 @@ func TestMXClientDataGetter_WasSigned(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, signedFuncName, vmRequest.FuncName) @@ -1049,8 +1235,8 @@ func TestMXClientDataGetter_GetAllStakedRelayers(t *testing.T) { providedRelayers := [][]byte{[]byte("relayer1"), []byte("relayer2")} args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, getAllStakedRelayersFuncName, vmRequest.FuncName) @@ -1072,6 +1258,35 @@ func TestMXClientDataGetter_GetAllStakedRelayers(t *testing.T) { assert.Equal(t, providedRelayers, result) } +func TestMXClientDataGetter_GetAllKnownTokens(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + providedTokens := [][]byte{[]byte("tkn1"), []byte("tkn2")} + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getAllKnownTokens, vmRequest.FuncName) + + assert.Nil(t, vmRequest.Args) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: providedTokens, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.GetAllKnownTokens(context.Background()) + assert.Nil(t, err) + assert.Equal(t, providedTokens, result) +} + func TestMultiversXClientDataGetter_GetShardCurrentNonce(t *testing.T) { t.Parallel() @@ -1187,8 +1402,8 @@ func TestMultiversXClientDataGetter_IsPaused(t *testing.T) { args.Proxy = &interactors.ProxyStub{ ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { proxyCalled = true - assert.Equal(t, args.RelayerAddress.AddressAsBech32String(), vmRequest.CallerAddr) - assert.Equal(t, args.MultisigContractAddress.AddressAsBech32String(), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, getBech32Address(args.MultisigContractAddress), vmRequest.Address) assert.Equal(t, "", vmRequest.CallValue) assert.Equal(t, isPausedFuncName, vmRequest.FuncName) assert.Empty(t, vmRequest.Args) @@ -1211,3 +1426,198 @@ func TestMultiversXClientDataGetter_IsPaused(t *testing.T) { assert.True(t, result) assert.True(t, proxyCalled) } + +func TestMultiversXClientDataGetter_isMintBurnToken(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, isMintBurnTokenFuncName, vmRequest.FuncName) + assert.Equal(t, []string{"746f6b656e"}, vmRequest.Args) + + strResponse := "AQ==" + response, _ := base64.StdEncoding.DecodeString(strResponse) + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{response}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.isMintBurnToken(context.Background(), []byte("token")) + assert.Nil(t, err) + assert.True(t, result) + assert.True(t, proxyCalled) +} + +func TestMultiversXClientDataGetter_isNativeToken(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, isNativeTokenFuncName, vmRequest.FuncName) + assert.Equal(t, []string{"746f6b656e"}, vmRequest.Args) + + strResponse := "AQ==" + response, _ := base64.StdEncoding.DecodeString(strResponse) + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{response}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.isNativeToken(context.Background(), []byte("token")) + assert.Nil(t, err) + assert.True(t, result) + assert.True(t, proxyCalled) +} + +func TestMultiversXClientDataGetter_getTotalBalances(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false + expectedAccumulatedBurnedTokens := big.NewInt(100) + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getTotalBalances, vmRequest.FuncName) + assert.Equal(t, []string{"746f6b656e"}, vmRequest.Args) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{expectedAccumulatedBurnedTokens.Bytes()}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.getTotalBalances(context.Background(), []byte("token")) + assert.Nil(t, err) + assert.Equal(t, result, expectedAccumulatedBurnedTokens) + assert.True(t, proxyCalled) +} + +func TestMultiversXClientDataGetter_getMintBalances(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false + expectedAccumulatedMintedTokens := big.NewInt(100) + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getMintBalances, vmRequest.FuncName) + assert.Equal(t, []string{"746f6b656e"}, vmRequest.Args) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{expectedAccumulatedMintedTokens.Bytes()}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.getMintBalances(context.Background(), []byte("token")) + assert.Nil(t, err) + assert.Equal(t, result, expectedAccumulatedMintedTokens) + assert.True(t, proxyCalled) +} + +func TestMultiversXClientDataGetter_getBurnBalances(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false + expectedAccumulatedBurnedTokens := big.NewInt(100) + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getBurnBalances, vmRequest.FuncName) + assert.Equal(t, []string{"746f6b656e"}, vmRequest.Args) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{expectedAccumulatedBurnedTokens.Bytes()}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.getBurnBalances(context.Background(), []byte("token")) + assert.Nil(t, err) + assert.Equal(t, result, expectedAccumulatedBurnedTokens) + assert.True(t, proxyCalled) +} + +func TestMultiversXClientDataGetter_GetLastMvxBatchID(t *testing.T) { + t.Parallel() + + args := createMockArgsMXClientDataGetter() + proxyCalled := false + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + proxyCalled = true + assert.Equal(t, getBech32Address(args.SafeContractAddress), vmRequest.Address) + assert.Equal(t, getBech32Address(args.RelayerAddress), vmRequest.CallerAddr) + assert.Equal(t, "", vmRequest.CallValue) + assert.Equal(t, getLastBatchId, vmRequest.FuncName) + assert.Empty(t, vmRequest.Args) + + strResponse := "Dpk=" + response, _ := base64.StdEncoding.DecodeString(strResponse) + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{response}, + }, + }, nil + }, + } + + dg, _ := NewMXClientDataGetter(args) + + result, err := dg.GetLastMvxBatchID(context.Background()) + assert.Nil(t, err) + assert.Equal(t, uint64(3737), result) + assert.True(t, proxyCalled) +} diff --git a/clients/multiversx/transactionHandler.go b/clients/multiversx/transactionHandler.go index c6b43fa6..c9c1c031 100644 --- a/clients/multiversx/transactionHandler.go +++ b/clients/multiversx/transactionHandler.go @@ -40,12 +40,12 @@ func (txHandler *transactionHandler) signTransaction(ctx context.Context, builde return nil, err } - nonce, err := txHandler.nonceTxHandler.GetNonce(context.Background(), txHandler.relayerAddress) + dataBytes, err := builder.ToDataBytes() if err != nil { return nil, err } - dataBytes, err := builder.ToDataBytes() + bech32Address, err := txHandler.relayerAddress.AddressAsBech32String() if err != nil { return nil, err } @@ -54,14 +54,17 @@ func (txHandler *transactionHandler) signTransaction(ctx context.Context, builde ChainID: networkConfig.ChainID, Version: networkConfig.MinTransactionVersion, GasLimit: gasLimit, - GasPrice: networkConfig.MinGasPrice, - Nonce: nonce, Data: dataBytes, - Sender: txHandler.relayerAddress.AddressAsBech32String(), + Sender: bech32Address, Receiver: txHandler.multisigAddressAsBech32, Value: "0", } + err = txHandler.nonceTxHandler.ApplyNonceAndGasPrice(context.Background(), txHandler.relayerAddress, tx) + if err != nil { + return nil, err + } + err = txHandler.signTransactionWithPrivateKey(tx) if err != nil { return nil, err diff --git a/clients/multiversx/transactionHandler_test.go b/clients/multiversx/transactionHandler_test.go index 7aa9cef0..0bb86aaf 100644 --- a/clients/multiversx/transactionHandler_test.go +++ b/clients/multiversx/transactionHandler_test.go @@ -65,8 +65,8 @@ func TestTransactionHandler_SendTransactionReturnHash(t *testing.T) { expectedErr := errors.New("expected error in get nonce") txHandlerInstance := createTransactionHandlerWithMockComponents() txHandlerInstance.nonceTxHandler = &bridgeTests.NonceTransactionsHandlerStub{ - GetNonceCalled: func(ctx context.Context, address core.AddressHandler) (uint64, error) { - return 0, expectedErr + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + return expectedErr }, } @@ -140,12 +140,15 @@ func TestTransactionHandler_SendTransactionReturnHash(t *testing.T) { } txHandlerInstance.nonceTxHandler = &bridgeTests.NonceTransactionsHandlerStub{ - GetNonceCalled: func(ctx context.Context, address core.AddressHandler) (uint64, error) { - if address.AddressAsBech32String() == relayerAddress { - return nonce, nil + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + if getBech32Address(address) == relayerAddress { + tx.Nonce = nonce + tx.GasPrice = minGasPrice + + return nil } - return 0, errors.New("unexpected address to fetch the nonce") + return errors.New("unexpected address to fetch the nonce") }, SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { sendWasCalled = true diff --git a/clients/roleProviders/multiversXRoleProvider.go b/clients/roleProviders/multiversXRoleProvider.go index 7365397c..9b718848 100644 --- a/clients/roleProviders/multiversXRoleProvider.go +++ b/clients/roleProviders/multiversXRoleProvider.go @@ -77,7 +77,12 @@ func (erp *multiversXRoleProvider) processResults(results [][]byte) error { return fmt.Errorf("%w for index %d, malformed address: %s", ErrInvalidAddressBytes, i, hex.EncodeToString(result)) } - currentList = append(currentList, address.AddressAsBech32String()) + bech32Address, err := address.AddressAsBech32String() + if err != nil { + return fmt.Errorf("%w for index %d, malformed address: %s", err, i, hex.EncodeToString(result)) + } + + currentList = append(currentList, bech32Address) temporaryMap[string(address.AddressBytes())] = struct{}{} } diff --git a/cmd/bridge/config/config.toml b/cmd/bridge/config/config.toml index 8d429a19..70e3d547 100644 --- a/cmd/bridge/config/config.toml +++ b/cmd/bridge/config/config.toml @@ -8,7 +8,7 @@ GasLimitForEach = 30000 IntervalToWaitForTransferInSeconds = 600 #10 minutes MaxRetriesOnQuorumReached = 3 - MaxBlocksDelta = 10 + ClientAvailabilityAllowDelta = 10 [Eth.GasStation] Enabled = true URL = "https://api.etherscan.io/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here @@ -24,17 +24,20 @@ [MultiversX] NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf" # the multiversx address for the bridge contract + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract PrivateKeyFile = "keys/multiversx.pem" # the path to the pem file containing the relayer multiversx wallet IntervalToResendTxsInSeconds = 60 # the time in seconds between nonce reads MaxRetriesOnQuorumReached = 3 MaxRetriesOnWasTransferProposed = 3 - ProxyCacherExpirationSeconds = 600 # the caching time in seconds + ClientAvailabilityAllowDelta = 10 + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds - # valid options for ProxyRestAPIEntityType are `observer` and `proxy`. Any other value will trigger an error. - # `observer` is useful when querying an observer, directly and `proxy` is useful when querying a squad's proxy - ProxyRestAPIEntityType = "observer" - ProxyFinalityCheck = true - ProxyMaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "observer" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized [MultiversX.GasMap] Sign = 8000000 ProposeTransferBase = 11000000 @@ -43,68 +46,81 @@ ProposeStatusForEach = 7000000 PerformActionBase = 40000000 PerformActionForEach = 5500000 + ScCallPerByte = 100000 # 1500 tx data field + the rest for the actual storage in the contract + ScCallPerformForEach = 10000000 [P2P] Port = "10010" InitialPeerList = [] ProtocolID = "/erd/relay/1.0.0" - [AntifloodConfig] + [P2P.Transports] + QUICAddress = "" # optional QUIC address. If this transport should be activated, should be in this format: /ip4/0.0.0.0/udp/%d/quic-v1 + WebSocketAddress = "" # optional WebSocket address. If this transport should be activated, should be in this format: /ip4/0.0.0.0/tcp/%d/ws + WebTransportAddress = "" # optional WebTransport address. If this transport should be activated, should be in this format: /ip4/0.0.0.0/udp/%d/quic-v1/webtransport + [P2P.Transports.TCP] + ListenAddress = "/ip4/0.0.0.0/tcp/%d" # TCP listen address + PreventPortReuse = false + [P2P.ResourceLimiter] + Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". + ManualSystemMemoryInMB = 0 # not taken into account if the type is not "default with manual scale" + ManualMaximumFD = 0 # not taken into account if the type is not "default with manual scale" + [P2P.AntifloodConfig] Enabled = true NumConcurrentResolverJobs = 50 - [Antiflood.FastReacting] + [P2P.AntifloodConfig.FastReacting] IntervalInSeconds = 1 ReservedPercent = 20.0 - [Antiflood.FastReacting.PeerMaxInput] + [P2P.AntifloodConfig.FastReacting.PeerMaxInput] BaseMessagesPerInterval = 10 TotalSizePerInterval = 1048576 #1MB/s - [Antiflood.FastReacting.PeerMaxInput.IncreaseFactor] + [P2P.AntifloodConfig.FastReacting.PeerMaxInput.IncreaseFactor] Threshold = 10 #if consensus size will exceed this value, then Factor = 1.0 #increase the base value with [factor*consensus size] - [Antiflood.FastReacting.BlackList] + [P2P.AntifloodConfig.FastReacting.BlackList] ThresholdNumMessagesPerInterval = 70 ThresholdSizePerInterval = 2097154 #2MB/s NumFloodingRounds = 10 PeerBanDurationInSeconds = 300 - [Antiflood.SlowReacting] + [P2P.AntifloodConfig.SlowReacting] IntervalInSeconds = 30 ReservedPercent = 20.0 - [Antiflood.SlowReacting.PeerMaxInput] + [P2P.AntifloodConfig.SlowReacting.PeerMaxInput] BaseMessagesPerInterval = 400 TotalSizePerInterval = 10485760 #10MB/interval - [Antiflood.SlowReacting.PeerMaxInput.IncreaseFactor] + [P2P.AntifloodConfig.SlowReacting.PeerMaxInput.IncreaseFactor] Threshold = 10 #if consensus size will exceed this value, then Factor = 0.0 #increase the base value with [factor*consensus size] - [Antiflood.SlowReacting.BlackList] + [P2P.AntifloodConfig.SlowReacting.BlackList] ThresholdNumMessagesPerInterval = 800 ThresholdSizePerInterval = 20971540 #20MB/interval NumFloodingRounds = 2 PeerBanDurationInSeconds = 3600 - [Antiflood.OutOfSpecs] + [P2P.AntifloodConfig.OutOfSpecs] IntervalInSeconds = 1 ReservedPercent = 0.0 - [Antiflood.OutOfSpecs.PeerMaxInput] + [P2P.AntifloodConfig.OutOfSpecs.PeerMaxInput] BaseMessagesPerInterval = 140 TotalSizePerInterval = 4194304 #4MB/s - [Antiflood.OutOfSpecs.PeerMaxInput.IncreaseFactor] + [P2P.AntifloodConfig.OutOfSpecs.PeerMaxInput.IncreaseFactor] Threshold = 0 #if consensus size will exceed this value, then Factor = 0.0 #increase the base value with [factor*consensus size] - [Antiflood.OutOfSpecs.BlackList] + [P2P.AntifloodConfig.OutOfSpecs.BlackList] ThresholdNumMessagesPerInterval = 200 ThresholdSizePerInterval = 6291456 #6MB/s NumFloodingRounds = 2 PeerBanDurationInSeconds = 3600 - [Antiflood.PeerMaxOutput] + [P2P.AntifloodConfig.PeerMaxOutput] BaseMessagesPerInterval = 5 TotalSizePerInterval = 524288 #512KB/s - [Antiflood.Cache] + [P2P.AntifloodConfig.Cache] Name = "Antiflood" Capacity = 7000 Type = "LRU" - [Antiflood.Topic] + [P2P.AntifloodConfig.Topic] DefaultMaxMessagesPerSec = 300 # default number of messages per interval for a topic MaxMessages = [{ Topic = "EthereumToMultiversX_join", NumMessagesPerSec = 100 }, { Topic = "EthereumToMultiversX_sign", NumMessagesPerSec = 100 }] @@ -114,7 +130,6 @@ Type = "gogo protobuf" SizeCheckDelta = 10 [Relayer.RoleProvider] - UsePolling = true PollingIntervalInMillis = 60000 # 1 minute [Relayer.StatusMetricsStorage] [Relayer.StatusMetricsStorage.Cache] @@ -141,9 +156,9 @@ LogFileLifeSpanInSec = 86400 # 24h LogFileLifeSpanInMB = 1024 # 1GB -[Antiflood] +[WebAntiflood] Enabled = true - [Antiflood.WebServer] + [WebAntiflood.WebServer] # SimultaneousRequests represents the number of concurrent requests accepted by the web server # this is a global throttler that acts on all http connections regardless of the originating source SimultaneousRequests = 100 @@ -153,11 +168,6 @@ # SameSourceResetIntervalInSec time frame between counter reset, in seconds SameSourceResetIntervalInSec = 1 -[BatchValidator] - Enabled = false - URL = "https://devnet-bridge-api.multiversx.com/validateBatch" # batch validator URL. - RequestTimeInSeconds = 2 # maximum timeout (in seconds) for the batch validation request - [PeersRatingConfig] TopRatedCacheCapacity = 5000 BadRatedCacheCapacity = 5000 diff --git a/cmd/bridge/flags.go b/cmd/bridge/flags.go index 25ceb47d..800742e9 100644 --- a/cmd/bridge/flags.go +++ b/cmd/bridge/flags.go @@ -30,7 +30,7 @@ var ( "all available routes for Rest API and options to enable or disable them.", Value: "config/api.toml", } - // logFile is used when the log output needs to be logged in a file + // logSaveFile is used when the log output needs to be logged in a file logSaveFile = cli.BoolFlag{ Name: "log-save", Usage: "Boolean option for enabling log saving. If set, it will automatically save all the logs into a file.", diff --git a/cmd/bridge/keys/multiversx.pem b/cmd/bridge/keys/multiversx.pem index 7b0836a5..90acc33e 100644 --- a/cmd/bridge/keys/multiversx.pem +++ b/cmd/bridge/keys/multiversx.pem @@ -2,4 +2,4 @@ ODY5Yzk4ZGYwYjExZmRhMDgwNGQ3ODM1NGVjYTQzMWY4N2E2ZTkxMGZhZTE0MDgx ZjczOGMzMTZhMjVlYTQwOTFlOGE4YjZiNDlkZTViN2JlMTBhYWExNThhNWE2YTRh YmI0YjU2Y2MwOGY1MjRiYjVlNmNkNWYyMTFhZDNlMTM= ------END PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- \ No newline at end of file +-----END PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- diff --git a/cmd/bridge/main.go b/cmd/bridge/main.go index 81a20ea7..6a84da96 100644 --- a/cmd/bridge/main.go +++ b/cmd/bridge/main.go @@ -19,6 +19,7 @@ import ( "github.com/multiversx/mx-bridge-eth-go/factory" "github.com/multiversx/mx-bridge-eth-go/p2p" "github.com/multiversx/mx-bridge-eth-go/status" + "github.com/multiversx/mx-chain-communication-go/p2p/libp2p" chainCore "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/typeConverters/uint64ByteSlice" @@ -29,7 +30,6 @@ import ( "github.com/multiversx/mx-chain-crypto-go/signing/secp256k1/singlesig" chainFactory "github.com/multiversx/mx-chain-go/cmd/node/factory" chainCommon "github.com/multiversx/mx-chain-go/common" - chainP2P "github.com/multiversx/mx-chain-go/p2p" p2pConfig "github.com/multiversx/mx-chain-go/p2p/config" p2pFactory "github.com/multiversx/mx-chain-go/p2p/factory" "github.com/multiversx/mx-chain-go/statusHandler" @@ -38,7 +38,6 @@ import ( "github.com/multiversx/mx-chain-go/update/disabled" logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-chain-logger-go/file" - "github.com/multiversx/mx-chain-p2p-go/libp2p" "github.com/multiversx/mx-sdk-go/blockchain" sdkCore "github.com/multiversx/mx-sdk-go/core" "github.com/urfave/cli" @@ -61,10 +60,13 @@ var log = logger.GetOrCreate("main") // appVersion should be populated at build time using ldflags // Usage examples: // linux/mac: -// go build -i -v -ldflags="-X main.appVersion=$(git describe --tags --long --dirty)" +// +// go build -i -v -ldflags="-X main.appVersion=$(git describe --tags --long --dirty)" +// // windows: -// for /f %i in ('git describe --tags --long --dirty') do set VERS=%i -// go build -i -v -ldflags="-X main.appVersion=%VERS%" +// +// for /f %i in ('git describe --tags --long --dirty') do set VERS=%i +// go build -i -v -ldflags="-X main.appVersion=%VERS%" var appVersion = chainCommon.UnVersionedAppString func main() { @@ -160,10 +162,10 @@ func startRelay(ctx *cli.Context, version string) error { ProxyURL: cfg.MultiversX.NetworkAddress, SameScState: false, ShouldBeSynced: false, - FinalityCheck: cfg.MultiversX.ProxyFinalityCheck, - AllowedDeltaToFinal: cfg.MultiversX.ProxyMaxNoncesDelta, - CacheExpirationTime: time.Second * time.Duration(cfg.MultiversX.ProxyCacherExpirationSeconds), - EntityType: sdkCore.RestAPIEntityType(cfg.MultiversX.ProxyRestAPIEntityType), + FinalityCheck: cfg.MultiversX.Proxy.FinalityCheck, + AllowedDeltaToFinal: cfg.MultiversX.Proxy.MaxNoncesDelta, + CacheExpirationTime: time.Second * time.Duration(cfg.MultiversX.Proxy.CacherExpirationSeconds), + EntityType: sdkCore.RestAPIEntityType(cfg.MultiversX.Proxy.RestAPIEntityType), } proxy, err := blockchain.NewProxy(argsProxy) if err != nil { @@ -181,6 +183,12 @@ func startRelay(ctx *cli.Context, version string) error { return err } + safeEthAddress := ethCommon.HexToAddress(cfg.Eth.SafeContractAddress) + safeInstance, err := contract.NewERC20Safe(safeEthAddress, ethClient) + if err != nil { + return err + } + argsContractsHolder := ethereum.ArgsErc20SafeContractsHolder{ EthClient: ethClient, EthClientStatusHandler: ethClientStatusHandler, @@ -209,6 +217,7 @@ func startRelay(ctx *cli.Context, version string) error { argsClientWrapper := wrappers.ArgsEthereumChainWrapper{ StatusHandler: ethClientStatusHandler, MultiSigContract: multiSigInstance, + SafeContract: safeInstance, BlockchainClient: ethClient, } @@ -349,6 +358,8 @@ func buildNetMessenger(cfg config.Config, marshalizer marshal.Marshalizer) (p2p. Port: cfg.P2P.Port, MaximumExpectedPeerCount: 0, ThresholdMinConnectedPeers: 0, + Transports: cfg.P2P.Transports, + ResourceLimiter: cfg.P2P.ResourceLimiter, } peerDiscoveryConfig := p2pConfig.KadDhtPeerDiscoveryConfig{ Enabled: true, @@ -373,6 +384,7 @@ func buildNetMessenger(cfg config.Config, marshalizer marshal.Marshalizer) (p2p. }, } + p2pLog := logger.GetOrCreate("p2p") topRatedCache, err := cache.NewLRUCache(cfg.PeersRatingConfig.TopRatedCacheCapacity) if err != nil { return nil, err @@ -384,6 +396,7 @@ func buildNetMessenger(cfg config.Config, marshalizer marshal.Marshalizer) (p2p. argsPeersRatingHandler := p2pFactory.ArgPeersRatingHandler{ TopRatedCache: topRatedCache, BadRatedCache: badRatedCache, + Logger: p2pLog, } peersRatingHandler, err := p2pFactory.NewPeersRatingHandler(argsPeersRatingHandler) if err != nil { @@ -395,17 +408,16 @@ func buildNetMessenger(cfg config.Config, marshalizer marshal.Marshalizer) (p2p. p2pPrivKey, _ := p2pKeyGen.GeneratePair() args := libp2p.ArgsNetworkMessenger{ - ListenAddress: chainP2P.ListenAddrWithIp4AndTcp, - Marshalizer: marshalizer, + Marshaller: marshalizer, P2pConfig: p2pCfg, SyncTimer: &libp2p.LocalSyncTimer{}, PreferredPeersHolder: disabled.NewPreferredPeersHolder(), - NodeOperationMode: chainP2P.NormalOperation, PeersRatingHandler: peersRatingHandler, ConnectionWatcherType: disabledWatcher, P2pPrivateKey: p2pPrivKey, P2pSingleSigner: p2pSingleSigner, P2pKeyGenerator: p2pKeyGen, + Logger: p2pLog, } return libp2p.NewNetworkMessenger(args) diff --git a/cmd/migration/config/config-mainnet-bsc.toml b/cmd/migration/config/config-mainnet-bsc.toml new file mode 100644 index 00000000..186ba577 --- /dev/null +++ b/cmd/migration/config/config-mainnet-bsc.toml @@ -0,0 +1,36 @@ +[Eth] + Chain = "BSC" + NetworkAddress = "" # a network address + PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key + MultisigContractAddress = "0xc58848bc00e6522C7Fd3F16a94BBb33b90549a12" + SafeContractAddress = "0x7334ba16020c1444957b75032165c0a6292ba09a" + GasLimitBase = 350000 + GasLimitForEach = 30000 + [Eth.GasStation] + Enabled = true + URL = "https://api.bscscan.com/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 1000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + PollingIntervalInSeconds = 60 # number of seconds between gas price polling + RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request + MaxFetchRetries = 3 # number of fetch retries before printing an error + RequestTimeInSeconds = 5 # maximum timeout (in seconds) for the gas price request + MaximumAllowedGasPrice = 300 # maximum value allowed for the fetched gas price value + # GasPriceSelector available options: "SafeGasPrice", "ProposeGasPrice", "FastGasPrice" + GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price + +[MultiversX] + NetworkAddress = "https://gateway.multiversx.com" # the network address + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgq6pg5e8twgk6vvnzxzmvfyrphrfs3e3c3yfkqe6kx0x" + SafeContractAddress = "erd1qqqqqqqqqqqqqpgq2tf9zm9xxv96gz50p8jq5jesudhz45dvyfkqyefrss" + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds + + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "proxy" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB diff --git a/cmd/migration/config/config-mainnet-eth.toml b/cmd/migration/config/config-mainnet-eth.toml new file mode 100644 index 00000000..a1a4d8ff --- /dev/null +++ b/cmd/migration/config/config-mainnet-eth.toml @@ -0,0 +1,36 @@ +[Eth] + Chain = "Ethereum" + NetworkAddress = "" # a network address + PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key + MultisigContractAddress = "0x1Ff78EB04d44a803E73c44FEf8790c5cAbD14596" + SafeContractAddress = "0x92A26975433A61CF1134802586aa669bAB8B69f3" + GasLimitBase = 350000 + GasLimitForEach = 30000 + [Eth.GasStation] + Enabled = true + URL = "https://api.etherscan.io/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 1000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + PollingIntervalInSeconds = 60 # number of seconds between gas price polling + RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request + MaxFetchRetries = 3 # number of fetch retries before printing an error + RequestTimeInSeconds = 5 # maximum timeout (in seconds) for the gas price request + MaximumAllowedGasPrice = 300 # maximum value allowed for the fetched gas price value + # GasPriceSelector available options: "SafeGasPrice", "ProposeGasPrice", "FastGasPrice" + GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price + +[MultiversX] + NetworkAddress = "https://gateway.multiversx.com" # the network address + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqxexs26vrvhwh2m4he62d6y3jzmv3qkujyfkq8yh4z2" + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqhxkc48lt5uv2hejj4wtjqvugfm4wgv6gyfkqw0uuxl" + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds + + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "proxy" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB diff --git a/cmd/migration/config/config.toml b/cmd/migration/config/config.toml new file mode 100644 index 00000000..6f8923a2 --- /dev/null +++ b/cmd/migration/config/config.toml @@ -0,0 +1,36 @@ +[Eth] + Chain = "Ethereum" + NetworkAddress = "http://127.0.0.1:8545" # a network address + PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key + MultisigContractAddress = "1Ff78EB04d44a803E73c44FEf8790c5cAbD14596" + SafeContractAddress = "92A26975433A61CF1134802586aa669bAB8B69f3" + GasLimitBase = 350000 + GasLimitForEach = 30000 + [Eth.GasStation] + Enabled = true + URL = "https://api.bscscan.com/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 5000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + PollingIntervalInSeconds = 60 # number of seconds between gas price polling + RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request + MaxFetchRetries = 3 # number of fetch retries before printing an error + RequestTimeInSeconds = 2 # maximum timeout (in seconds) for the gas price request + MaximumAllowedGasPrice = 300 # maximum value allowed for the fetched gas price value + # GasPriceSelector available options: "SafeGasPrice", "ProposeGasPrice", "FastGasPrice" + GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price + +[MultiversX] + NetworkAddress = "https://gateway.multiversx.com" # the network address + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqxexs26vrvhwh2m4he62d6y3jzmv3qkujyfkq8yh4z2" + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqhxkc48lt5uv2hejj4wtjqvugfm4wgv6gyfkqw0uuxl" + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds + + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "proxy" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB diff --git a/cmd/migration/disabled/statusHandler.go b/cmd/migration/disabled/statusHandler.go new file mode 100644 index 00000000..7949c9cb --- /dev/null +++ b/cmd/migration/disabled/statusHandler.go @@ -0,0 +1,34 @@ +package disabled + +import "github.com/multiversx/mx-bridge-eth-go/core" + +// StatusHandler represents the disabled status handler implementation +type StatusHandler struct { +} + +// SetIntMetric does nothing +func (handler *StatusHandler) SetIntMetric(_ string, _ int) { +} + +// AddIntMetric does nothing +func (handler *StatusHandler) AddIntMetric(_ string, _ int) { +} + +// SetStringMetric does nothing +func (handler *StatusHandler) SetStringMetric(_ string, _ string) { +} + +// Name returns an empty string +func (handler *StatusHandler) Name() string { + return "" +} + +// GetAllMetrics returns an empty map +func (handler *StatusHandler) GetAllMetrics() core.GeneralMetrics { + return make(core.GeneralMetrics) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (handler *StatusHandler) IsInterfaceNil() bool { + return handler == nil +} diff --git a/cmd/migration/flags.go b/cmd/migration/flags.go new file mode 100644 index 00000000..e15c15a4 --- /dev/null +++ b/cmd/migration/flags.go @@ -0,0 +1,73 @@ +package main + +import ( + "path" + + "github.com/multiversx/mx-bridge-eth-go/config" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/urfave/cli" +) + +var ( + logLevel = cli.StringFlag{ + Name: "log-level", + Usage: "This flag specifies the logger `level(s)`. It can contain multiple comma-separated value. For example" + + ", if set to *:INFO the logs for all packages will have the INFO level. However, if set to *:INFO,api:DEBUG" + + " the logs for all packages will have the INFO level, excepting the api package which will receive a DEBUG" + + " log level.", + Value: "*:" + logger.LogInfo.String(), + } + configurationFile = cli.StringFlag{ + Name: "config", + Usage: "The `" + filePathPlaceholder + "` for the main configuration file. This TOML file contain the main " + + "configurations such as storage setups, epoch duration and so on.", + Value: "config/config.toml", + } + mode = cli.StringFlag{ + Name: "mode", + Usage: "This flag specifies the operation mode. Usage: query, sign or execute", + Value: queryMode, + } + migrationJsonFile = cli.StringFlag{ + Name: "migration-file", + Usage: "The output .json file containing the migration data", + Value: path.Join(configPath, "migration-"+timestampPlaceholder+".json"), + } + signatureJsonFile = cli.StringFlag{ + Name: "signature-file", + Usage: "The output .json file containing the signature data", + Value: path.Join(configPath, publicKeyPlaceholder+"-"+timestampPlaceholder+".json"), + } + newSafeAddress = cli.StringFlag{ + Name: "new-safe-address", + Usage: "The new safe address on Ethereum", + Value: "", + } + partialMigration = cli.StringFlag{ + Name: "partial-migration", + Usage: "If a partial migration is wanted, this option can be very handy. We can specify an unlimited tuples in a single string, like this: " + + "`-partial-migration token1:amount1,token2:amount2,token1:amount3` and so on. You can see that the same token can be specified multiple times, " + + "the amounts will be added. The amount should be specified as a denominated value (does not contain all decimals, the conversion will be done " + + "automatically by the tool). Real example: `-partial-migration token1:amount1,token2:amount2,token1:amount3`", + } +) + +func getFlags() []cli.Flag { + return []cli.Flag{ + logLevel, + configurationFile, + mode, + migrationJsonFile, + signatureJsonFile, + newSafeAddress, + partialMigration, + } +} +func getFlagsConfig(ctx *cli.Context) config.ContextFlagsConfig { + flagsConfig := config.ContextFlagsConfig{} + + flagsConfig.LogLevel = ctx.GlobalString(logLevel.Name) + flagsConfig.ConfigurationFile = ctx.GlobalString(configurationFile.Name) + + return flagsConfig +} diff --git a/cmd/migration/interface.go b/cmd/migration/interface.go new file mode 100644 index 00000000..086ccffc --- /dev/null +++ b/cmd/migration/interface.go @@ -0,0 +1,15 @@ +package main + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum" +) + +// BatchCreator defines the operations implemented by an entity that can create an Ethereum batch message that can be used +// in signing or transfer execution +type BatchCreator interface { + CreateBatchInfo(ctx context.Context, newSafeAddress common.Address, partialMigration map[string]*big.Float) (*ethereum.BatchInfo, error) +} diff --git a/cmd/migration/keys/ethereum.sk b/cmd/migration/keys/ethereum.sk new file mode 100644 index 00000000..5675c4b6 --- /dev/null +++ b/cmd/migration/keys/ethereum.sk @@ -0,0 +1 @@ +9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb1 diff --git a/cmd/migration/main.go b/cmd/migration/main.go new file mode 100644 index 00000000..5ff13500 --- /dev/null +++ b/cmd/migration/main.go @@ -0,0 +1,350 @@ +package main + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + ethereumClient "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" + "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement" + "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement/factory" + "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" + "github.com/multiversx/mx-bridge-eth-go/cmd/migration/disabled" + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" + chainCore "github.com/multiversx/mx-chain-core-go/core" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-sdk-go/blockchain" + sdkCore "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/data" + "github.com/urfave/cli" +) + +const ( + filePathPlaceholder = "[path]" + queryMode = "query" + signMode = "sign" + executeMode = "execute" + configPath = "config" + timestampPlaceholder = "[timestamp]" + publicKeyPlaceholder = "[public-key]" +) + +var log = logger.GetOrCreate("main") + +type internalComponents struct { + creator BatchCreator + batch *ethereum.BatchInfo + cryptoHandler ethereumClient.CryptoHandler + ethClient *ethclient.Client + ethereumChainWrapper ethereum.EthereumChainWrapper +} + +func main() { + app := cli.NewApp() + app.Name = "Funds migration CLI tool" + app.Usage = "This is the entry point for the migration CLI tool" + app.Flags = getFlags() + app.Authors = []cli.Author{ + { + Name: "The MultiversX Team", + Email: "contact@multiversx.com", + }, + } + + app.Action = func(c *cli.Context) error { + return execute(c) + } + + err := app.Run(os.Args) + if err != nil { + log.Error(err.Error()) + os.Exit(1) + } + + log.Info("process finished successfully") +} + +func execute(ctx *cli.Context) error { + flagsConfig := getFlagsConfig(ctx) + + err := logger.SetLogLevel(flagsConfig.LogLevel) + if err != nil { + return err + } + + cfg, err := loadConfig(flagsConfig.ConfigurationFile) + if err != nil { + return err + } + + log.Info("starting migration help tool", "pid", os.Getpid()) + + operationMode := strings.ToLower(ctx.GlobalString(mode.Name)) + switch operationMode { + case queryMode: + return executeQuery(cfg) + case signMode: + _, err = generateAndSign(ctx, cfg) + return err + case executeMode: + return executeTransfer(ctx, cfg) + } + + return fmt.Errorf("unknown execution mode: %s", operationMode) +} + +func executeQuery(cfg config.MigrationToolConfig) error { + components, err := createInternalComponentsWithBatchCreator(cfg) + if err != nil { + return err + } + + dummyEthAddress := common.Address{} + info, err := components.creator.CreateBatchInfo(context.Background(), dummyEthAddress, nil) + if err != nil { + return err + } + + log.Info(fmt.Sprintf("Token balances for ERC20 safe address %s\n%s", + cfg.Eth.SafeContractAddress, + ethereum.TokensBalancesDisplayString(info), + )) + + return nil +} + +func createInternalComponentsWithBatchCreator(cfg config.MigrationToolConfig) (*internalComponents, error) { + argsProxy := blockchain.ArgsProxy{ + ProxyURL: cfg.MultiversX.NetworkAddress, + SameScState: false, + ShouldBeSynced: false, + FinalityCheck: cfg.MultiversX.Proxy.FinalityCheck, + AllowedDeltaToFinal: cfg.MultiversX.Proxy.MaxNoncesDelta, + CacheExpirationTime: time.Second * time.Duration(cfg.MultiversX.Proxy.CacherExpirationSeconds), + EntityType: sdkCore.RestAPIEntityType(cfg.MultiversX.Proxy.RestAPIEntityType), + } + proxy, err := blockchain.NewProxy(argsProxy) + if err != nil { + return nil, err + } + + dummyAddress := data.NewAddressFromBytes(bytes.Repeat([]byte{0x1}, 32)) + multisigAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.MultisigContractAddress) + if err != nil { + return nil, err + } + + safeAddress, err := data.NewAddressFromBech32String(cfg.MultiversX.SafeContractAddress) + if err != nil { + return nil, err + } + + argsMXClientDataGetter := multiversx.ArgsMXClientDataGetter{ + MultisigContractAddress: multisigAddress, + SafeContractAddress: safeAddress, + RelayerAddress: dummyAddress, + Proxy: proxy, + Log: log, + } + mxDataGetter, err := multiversx.NewMXClientDataGetter(argsMXClientDataGetter) + if err != nil { + return nil, err + } + + ethClient, err := ethclient.Dial(cfg.Eth.NetworkAddress) + if err != nil { + return nil, err + } + + argsContractsHolder := ethereumClient.ArgsErc20SafeContractsHolder{ + EthClient: ethClient, + EthClientStatusHandler: &disabled.StatusHandler{}, + } + erc20ContractsHolder, err := ethereumClient.NewErc20SafeContractsHolder(argsContractsHolder) + if err != nil { + return nil, err + } + + safeEthAddress := common.HexToAddress(cfg.Eth.SafeContractAddress) + + bridgeEthAddress := common.HexToAddress(cfg.Eth.MultisigContractAddress) + multiSigInstance, err := contract.NewBridge(bridgeEthAddress, ethClient) + if err != nil { + return nil, err + } + + argsClientWrapper := bridgeV2Wrappers.ArgsEthereumChainWrapper{ + StatusHandler: &disabled.StatusHandler{}, + MultiSigContract: multiSigInstance, + BlockchainClient: ethClient, + } + ethereumChainWrapper, err := bridgeV2Wrappers.NewEthereumChainWrapper(argsClientWrapper) + if err != nil { + return nil, err + } + + argsCreator := ethereum.ArgsMigrationBatchCreator{ + MvxDataGetter: mxDataGetter, + Erc20ContractsHolder: erc20ContractsHolder, + SafeContractAddress: safeEthAddress, + EthereumChainWrapper: ethereumChainWrapper, + Logger: log, + } + + creator, err := ethereum.NewMigrationBatchCreator(argsCreator) + if err != nil { + return nil, err + } + + return &internalComponents{ + creator: creator, + ethClient: ethClient, + ethereumChainWrapper: ethereumChainWrapper, + }, nil +} + +func generateAndSign(ctx *cli.Context, cfg config.MigrationToolConfig) (*internalComponents, error) { + components, err := createInternalComponentsWithBatchCreator(cfg) + if err != nil { + return nil, err + } + + newSafeAddressString := ctx.GlobalString(newSafeAddress.Name) + if len(newSafeAddressString) == 0 { + return nil, fmt.Errorf("invalid new safe address for Ethereum") + } + newSafeAddressValue := common.HexToAddress(ctx.GlobalString(newSafeAddress.Name)) + + partialMigration, err := ethereum.ConvertPartialMigrationStringToMap(ctx.GlobalString(partialMigration.Name)) + if err != nil { + return nil, err + } + + components.batch, err = components.creator.CreateBatchInfo(context.Background(), newSafeAddressValue, partialMigration) + if err != nil { + return nil, err + } + + val, err := json.MarshalIndent(components.batch, "", " ") + if err != nil { + return nil, err + } + + components.cryptoHandler, err = ethereumClient.NewCryptoHandler(cfg.Eth.PrivateKeyFile) + if err != nil { + return nil, err + } + + log.Info("signing batch", "message hash", components.batch.MessageHash.String(), + "public key", components.cryptoHandler.GetAddress().String()) + + signature, err := components.cryptoHandler.Sign(components.batch.MessageHash) + if err != nil { + return nil, err + } + + log.Info("Migration .json file contents: \n" + string(val)) + + jsonFilename := ctx.GlobalString(migrationJsonFile.Name) + jsonFilename = applyTimestamp(jsonFilename) + err = os.WriteFile(jsonFilename, val, os.ModePerm) + if err != nil { + return nil, err + } + + sigInfo := ðereum.SignatureInfo{ + Address: components.cryptoHandler.GetAddress().String(), + MessageHash: components.batch.MessageHash.String(), + Signature: hex.EncodeToString(signature), + } + + sigFilename := ctx.GlobalString(signatureJsonFile.Name) + sigFilename = applyTimestamp(sigFilename) + sigFilename = applyPublicKey(sigFilename, sigInfo.Address) + val, err = json.MarshalIndent(sigInfo, "", " ") + if err != nil { + return nil, err + } + + log.Info("Signature .json file contents: \n" + string(val)) + + err = os.WriteFile(sigFilename, val, os.ModePerm) + if err != nil { + return nil, err + } + + return components, nil +} + +func executeTransfer(ctx *cli.Context, cfg config.MigrationToolConfig) error { + components, err := generateAndSign(ctx, cfg) + if err != nil { + return err + } + + gasStationConfig := cfg.Eth.GasStation + argsGasStation := gasManagement.ArgsGasStation{ + RequestURL: gasStationConfig.URL, + RequestPollingInterval: time.Duration(gasStationConfig.PollingIntervalInSeconds) * time.Second, + RequestRetryDelay: time.Duration(gasStationConfig.RequestRetryDelayInSeconds) * time.Second, + MaximumFetchRetries: gasStationConfig.MaxFetchRetries, + RequestTime: time.Duration(gasStationConfig.RequestTimeInSeconds) * time.Second, + MaximumGasPrice: gasStationConfig.MaximumAllowedGasPrice, + GasPriceSelector: core.EthGasPriceSelector(gasStationConfig.GasPriceSelector), + GasPriceMultiplier: gasStationConfig.GasPriceMultiplier, + } + gs, err := factory.CreateGasStation(argsGasStation, gasStationConfig.Enabled) + if err != nil { + return err + } + + args := ethereum.ArgsMigrationBatchExecutor{ + EthereumChainWrapper: components.ethereumChainWrapper, + CryptoHandler: components.cryptoHandler, + Batch: *components.batch, + Signatures: ethereum.LoadAllSignatures(log, configPath), + Logger: log, + GasHandler: gs, + TransferGasLimitBase: cfg.Eth.GasLimitBase, + TransferGasLimitForEach: cfg.Eth.GasLimitForEach, + } + + executor, err := ethereum.NewMigrationBatchExecutor(args) + if err != nil { + return err + } + + return executor.ExecuteTransfer(context.Background()) +} + +func loadConfig(filepath string) (config.MigrationToolConfig, error) { + cfg := config.MigrationToolConfig{} + err := chainCore.LoadTomlFile(&cfg, filepath) + if err != nil { + return config.MigrationToolConfig{}, err + } + + return cfg, nil +} + +func applyTimestamp(input string) string { + actualTimestamp := time.Now().Format("2006-01-02T15-04-05") + actualTimestamp = strings.Replace(actualTimestamp, "T", "-", 1) + + return strings.Replace(input, timestampPlaceholder, actualTimestamp, 1) +} + +func applyPublicKey(input string, publickey string) string { + return strings.Replace(input, publicKeyPlaceholder, publickey, 1) +} diff --git a/cmd/scCallsExecutor/config/config.toml b/cmd/scCallsExecutor/config/config.toml new file mode 100644 index 00000000..b9a18034 --- /dev/null +++ b/cmd/scCallsExecutor/config/config.toml @@ -0,0 +1,29 @@ +ScProxyBech32Address = "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s" +ExtraGasToExecute = 60000000 # this value allow the SC calls without provided gas limit to be refunded +MaxGasLimitToUse = 249999999 # this is a safe max gas limit to use both intra-shard & cross-shard +GasLimitForOutOfGasTransactions = 30000000 # this value will be used when a transaction specified a gas limit > 249999999 +NetworkAddress = "http://127.0.0.1:8085" +ProxyMaxNoncesDelta = 7 +ProxyFinalityCheck = true +ProxyCacherExpirationSeconds = 600 +ProxyRestAPIEntityType = "proxy" +IntervalToResendTxsInSeconds = 60 +PrivateKeyFile = "keys/multiversx.pem" +PollingIntervalInMillis = 6000 + +[Filter] + AllowedEthAddresses = ["*"] # execute SC calls from all ETH addresses + AllowedMvxAddresses = ["*"] # execute SC calls to all MvX contracts + AllowedTokens = ["*"] # execute SC calls for all tokens + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB + +[TransactionChecks] + CheckTransactionResults = true # enable or disable the transaction execution checking + TimeInSecondsBetweenChecks = 6 # the number of seconds to recheck the status of the transaction + ExecutionTimeoutInSeconds = 120 # the number of seconds reserved for each execution to complete + CloseAppOnError = false # enable or disable if the executor should automatically close on a transaction execution error + ExtraDelayInSecondsOnError = 300 # extra delay in seconds if the transaction execution errored + diff --git a/cmd/scCallsExecutor/flags.go b/cmd/scCallsExecutor/flags.go new file mode 100644 index 00000000..658fd4eb --- /dev/null +++ b/cmd/scCallsExecutor/flags.go @@ -0,0 +1,116 @@ +package main + +import ( + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-chain-go/facade" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/urfave/cli" +) + +var ( + logLevel = cli.StringFlag{ + Name: "log-level", + Usage: "This flag specifies the logger `level(s)`. It can contain multiple comma-separated value. For example" + + ", if set to *:INFO the logs for all packages will have the INFO level. However, if set to *:INFO,api:DEBUG" + + " the logs for all packages will have the INFO level, excepting the api package which will receive a DEBUG" + + " log level.", + Value: "*:" + logger.LogDebug.String(), + } + // configurationFile defines a flag for the path to the main toml configuration file + configurationFile = cli.StringFlag{ + Name: "config", + Usage: "The `" + filePathPlaceholder + "` for the main configuration file. This TOML file contain the main " + + "configurations such as monitored SC, gateway URL, timings and so on", + Value: "config/config.toml", + } + // logFile is used when the log output needs to be logged in a file + logSaveFile = cli.BoolFlag{ + Name: "log-save", + Usage: "Boolean option for enabling log saving. If set, it will automatically save all the logs into a file.", + } + // profileMode defines a flag for profiling the binary + // If enabled, it will open the pprof routes over the default gin rest webserver. + // There are several routes that will be available for profiling (profiling can be analyzed with: go tool pprof): + // /debug/pprof/ (can be accessed in the browser, will list the available options) + // /debug/pprof/goroutine + // /debug/pprof/heap + // /debug/pprof/threadcreate + // /debug/pprof/block + // /debug/pprof/mutex + // /debug/pprof/profile (CPU profile) + // /debug/pprof/trace?seconds=5 (CPU trace) -> being a trace, can be analyzed with: go tool trace + // Usage: go tool pprof http(s)://ip.of.the.server/debug/pprof/xxxxx + profileMode = cli.BoolFlag{ + Name: "profile-mode", + Usage: "Boolean option for enabling the profiling mode. If set, the /debug/pprof routes will be available " + + "on the node for profiling the application.", + } + // restApiInterface defines a flag for the interface on which the rest API will try to bind with + restApiInterface = cli.StringFlag{ + Name: "rest-api-interface", + Usage: "The interface `address and port` to which the REST API will attempt to bind. " + + "To bind to all available interfaces, set this flag to :8080", + Value: facade.DefaultRestInterface, + } + // workingDirectory defines a flag for the path for the working directory. + workingDirectory = cli.StringFlag{ + Name: "working-directory", + Usage: "This flag specifies the `directory` where the node will store databases and logs.", + Value: "", + } + // disableAnsiColor defines if the logger subsystem should prevent displaying ANSI colors + disableAnsiColor = cli.BoolFlag{ + Name: "disable-ansi-color", + Usage: "Boolean option for disabling ANSI colors in the logging system.", + } + // logWithLoggerName is used to enable log correlation elements + logWithLoggerName = cli.BoolFlag{ + Name: "log-logger-name", + Usage: "Boolean option for logger name in the logs.", + } + // networkAddress is used to specify the network address used + networkAddress = cli.StringFlag{ + Name: "network-address", + Usage: "The network address (gateway) to be used. Example: 'https://testnet-explorer.multiversx.com'", + } + // scProxyBech32Address is the smart contract address used to interact with this tool + scProxyBech32Address = cli.StringFlag{ + Name: "sc-proxy-address", + Usage: "The smart contract address in bech32 format to interact with", + } + // privateKeyFile is the MultiversX private key file used to issue transaction for the SC calls + privateKeyFile = cli.StringFlag{ + Name: "private-key-file", + Usage: "The MultiversX private key file used to issue transaction for the SC calls", + } +) + +func getFlags() []cli.Flag { + return []cli.Flag{ + workingDirectory, + logLevel, + disableAnsiColor, + configurationFile, + logSaveFile, + logWithLoggerName, + profileMode, + restApiInterface, + networkAddress, + scProxyBech32Address, + privateKeyFile, + } +} +func getFlagsConfig(ctx *cli.Context) config.ContextFlagsConfig { + flagsConfig := config.ContextFlagsConfig{} + + flagsConfig.WorkingDir = ctx.GlobalString(workingDirectory.Name) + flagsConfig.LogLevel = ctx.GlobalString(logLevel.Name) + flagsConfig.DisableAnsiColor = ctx.GlobalBool(disableAnsiColor.Name) + flagsConfig.ConfigurationFile = ctx.GlobalString(configurationFile.Name) + flagsConfig.SaveLogFile = ctx.GlobalBool(logSaveFile.Name) + flagsConfig.EnableLogName = ctx.GlobalBool(logWithLoggerName.Name) + flagsConfig.EnablePprof = ctx.GlobalBool(profileMode.Name) + flagsConfig.RestApiInterface = ctx.GlobalString(restApiInterface.Name) + + return flagsConfig +} diff --git a/cmd/scCallsExecutor/main.go b/cmd/scCallsExecutor/main.go new file mode 100644 index 00000000..d1493199 --- /dev/null +++ b/cmd/scCallsExecutor/main.go @@ -0,0 +1,198 @@ +package main + +import ( + "fmt" + "os" + "os/signal" + "runtime" + "syscall" + "time" + + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/executors/multiversx/module" + chainCore "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/core/check" + chainFactory "github.com/multiversx/mx-chain-go/cmd/node/factory" + chainCommon "github.com/multiversx/mx-chain-go/common" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-chain-logger-go/file" + "github.com/urfave/cli" +) + +const ( + filePathPlaceholder = "[path]" + defaultLogsPath = "logs" + logFilePrefix = "sc-calls-executor" +) + +var log = logger.GetOrCreate("main") + +// appVersion should be populated at build time using ldflags +// Usage examples: +// linux/mac: +// +// go build -i -v -ldflags="-X main.appVersion=$(git describe --tags --long --dirty)" +// +// windows: +// +// for /f %i in ('git describe --tags --long --dirty') do set VERS=%i +// go build -i -v -ldflags="-X main.appVersion=%VERS%" +var appVersion = chainCommon.UnVersionedAppString + +func main() { + app := cli.NewApp() + app.Name = "SC calls executor CLI app" + app.Usage = "This is the entry point for the module that periodically tries to execute SC calls" + app.Flags = getFlags() + machineID := chainCore.GetAnonymizedMachineID(app.Name) + app.Version = fmt.Sprintf("%s/%s/%s-%s/%s", appVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH, machineID) + app.Authors = []cli.Author{ + { + Name: "The MultiversX Team", + Email: "contact@multiversx.com", + }, + } + + app.Action = func(c *cli.Context) error { + return startExecutor(c, app.Version) + } + + err := app.Run(os.Args) + if err != nil { + log.Error(err.Error()) + os.Exit(1) + } +} + +func startExecutor(ctx *cli.Context, version string) error { + flagsConfig := getFlagsConfig(ctx) + + fileLogging, errLogger := attachFileLogger(log, flagsConfig) + if errLogger != nil { + return errLogger + } + + log.Info("starting SC calls executor node", "version", version, "pid", os.Getpid()) + + err := logger.SetLogLevel(flagsConfig.LogLevel) + if err != nil { + return err + } + + cfg, err := loadConfig(flagsConfig.ConfigurationFile) + if err != nil { + return err + } + + if !check.IfNil(fileLogging) { + timeLogLifeSpan := time.Second * time.Duration(cfg.Logs.LogFileLifeSpanInSec) + sizeLogLifeSpanInMB := uint64(cfg.Logs.LogFileLifeSpanInMB) + err = fileLogging.ChangeFileLifeSpan(timeLogLifeSpan, sizeLogLifeSpanInMB) + if err != nil { + return err + } + } + + if ctx.IsSet(scProxyBech32Address.Name) { + cfg.ScProxyBech32Address = ctx.GlobalString(scProxyBech32Address.Name) + log.Info("using flag-defined SC proxy address", "address", cfg.ScProxyBech32Address) + } + if ctx.IsSet(networkAddress.Name) { + cfg.NetworkAddress = ctx.GlobalString(networkAddress.Name) + log.Info("using flag-defined network address", "address", cfg.NetworkAddress) + } + if ctx.IsSet(privateKeyFile.Name) { + cfg.PrivateKeyFile = ctx.GlobalString(privateKeyFile.Name) + log.Info("using flag-defined private key file", "filename", cfg.PrivateKeyFile) + } + + if len(cfg.NetworkAddress) == 0 { + return fmt.Errorf("empty NetworkAddress in config file") + } + + args := config.ScCallsModuleConfig{ + ScProxyBech32Address: cfg.ScProxyBech32Address, + ExtraGasToExecute: cfg.ExtraGasToExecute, + MaxGasLimitToUse: cfg.MaxGasLimitToUse, + GasLimitForOutOfGasTransactions: cfg.GasLimitForOutOfGasTransactions, + NetworkAddress: cfg.NetworkAddress, + ProxyMaxNoncesDelta: cfg.ProxyMaxNoncesDelta, + ProxyFinalityCheck: cfg.ProxyFinalityCheck, + ProxyCacherExpirationSeconds: cfg.ProxyCacherExpirationSeconds, + ProxyRestAPIEntityType: cfg.ProxyRestAPIEntityType, + IntervalToResendTxsInSeconds: cfg.IntervalToResendTxsInSeconds, + PrivateKeyFile: cfg.PrivateKeyFile, + PollingIntervalInMillis: cfg.PollingIntervalInMillis, + Filter: cfg.Filter, + Logs: cfg.Logs, + TransactionChecks: cfg.TransactionChecks, + } + + chCloseApp := make(chan struct{}, 1) + scCallsExecutor, err := module.NewScCallsModule(args, log, chCloseApp) + if err != nil { + return err + } + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + select { + case <-sigs: + log.Info("application closing by user error input, calling Close on all subcomponents...") + case <-chCloseApp: + log.Info("application closing, requested internally, calling Close on all subcomponents...") + } + + return scCallsExecutor.Close() +} + +func loadConfig(filepath string) (config.ScCallsModuleConfig, error) { + cfg := config.ScCallsModuleConfig{} + err := chainCore.LoadTomlFile(&cfg, filepath) + if err != nil { + return config.ScCallsModuleConfig{}, err + } + + return cfg, nil +} + +func attachFileLogger(log logger.Logger, flagsConfig config.ContextFlagsConfig) (chainFactory.FileLoggingHandler, error) { + var fileLogging chainFactory.FileLoggingHandler + var err error + if flagsConfig.SaveLogFile { + argsFileLogging := file.ArgsFileLogging{ + WorkingDir: flagsConfig.WorkingDir, + DefaultLogsPath: defaultLogsPath, + LogFilePrefix: logFilePrefix, + } + fileLogging, err = file.NewFileLogging(argsFileLogging) + if err != nil { + return nil, fmt.Errorf("%w creating a log file", err) + } + } + + err = logger.SetDisplayByteSlice(logger.ToHex) + log.LogIfError(err) + logger.ToggleLoggerName(flagsConfig.EnableLogName) + logLevelFlagValue := flagsConfig.LogLevel + err = logger.SetLogLevel(logLevelFlagValue) + if err != nil { + return nil, err + } + + if flagsConfig.DisableAnsiColor { + err = logger.RemoveLogObserver(os.Stdout) + if err != nil { + return nil, err + } + + err = logger.AddLogObserver(os.Stdout, &logger.PlainFormatter{}) + if err != nil { + return nil, err + } + } + log.Trace("logger updated", "level", logLevelFlagValue, "disable ANSI color", flagsConfig.DisableAnsiColor) + + return fileLogging, nil +} diff --git a/config/config.go b/config/config.go index 8fd3dd75..cce4ce7d 100644 --- a/config/config.go +++ b/config/config.go @@ -3,6 +3,7 @@ package config import ( "github.com/multiversx/mx-bridge-eth-go/clients/chain" "github.com/multiversx/mx-chain-go/config" + p2pConfig "github.com/multiversx/mx-chain-go/p2p/config" ) // Configs is a holder for the relayer configuration parameters @@ -20,8 +21,7 @@ type Config struct { StateMachine map[string]ConfigStateMachine Relayer ConfigRelayer Logs LogsConfig - Antiflood AntifloodConfig - BatchValidator BatchValidatorConfig + WebAntiflood WebAntifloodConfig PeersRatingConfig PeersRatingConfig } @@ -38,7 +38,9 @@ type EthereumConfig struct { GasStation GasStationConfig MaxRetriesOnQuorumReached uint64 IntervalToWaitForTransferInSeconds uint64 - MaxBlocksDelta uint64 + ClientAvailabilityAllowDelta uint64 + EventsBlockRangeFrom int64 + EventsBlockRangeTo int64 } // GasStationConfig represents the configuration for the gas station handler @@ -59,7 +61,9 @@ type ConfigP2P struct { Port string InitialPeerList []string ProtocolID string + Transports p2pConfig.P2PTransportConfig AntifloodConfig config.AntifloodConfig + ResourceLimiter p2pConfig.P2PResourceLimiterConfig } // ConfigRelayer configuration for general relayer configuration @@ -95,19 +99,12 @@ type WebServerAntifloodConfig struct { SameSourceResetIntervalInSec uint32 } -// AntifloodConfig will hold all p2p antiflood parameters -type AntifloodConfig struct { +// WebAntifloodConfig will hold all web antiflood parameters +type WebAntifloodConfig struct { Enabled bool WebServer WebServerAntifloodConfig } -// BatchValidatorConfig represents the configuration for the batch validator -type BatchValidatorConfig struct { - Enabled bool - URL string - RequestTimeInSeconds int -} - // ApiRoutesConfig holds the configuration related to Rest API routes type ApiRoutesConfig struct { Logging ApiLoggingConfig @@ -146,15 +143,22 @@ type RoleProviderConfig struct { type MultiversXConfig struct { NetworkAddress string MultisigContractAddress string + SafeContractAddress string PrivateKeyFile string IntervalToResendTxsInSeconds uint64 GasMap MultiversXGasMapConfig MaxRetriesOnQuorumReached uint64 MaxRetriesOnWasTransferProposed uint64 - ProxyCacherExpirationSeconds uint64 - ProxyRestAPIEntityType string - ProxyMaxNoncesDelta int - ProxyFinalityCheck bool + ClientAvailabilityAllowDelta uint64 + Proxy ProxyConfig +} + +// ProxyConfig represents the configuration for the MultiversX proxy +type ProxyConfig struct { + CacherExpirationSeconds uint64 + RestAPIEntityType string + MaxNoncesDelta int + FinalityCheck bool } // MultiversXGasMapConfig represents the gas limits for MultiversX operations @@ -166,6 +170,8 @@ type MultiversXGasMapConfig struct { ProposeStatusForEach uint64 PerformActionBase uint64 PerformActionForEach uint64 + ScCallPerByte uint64 + ScCallPerformForEach uint64 } // PeersRatingConfig will hold settings related to peers rating @@ -173,3 +179,48 @@ type PeersRatingConfig struct { TopRatedCacheCapacity int BadRatedCacheCapacity int } + +// PendingOperationsFilterConfig defines the filter structure +type PendingOperationsFilterConfig struct { + DeniedEthAddresses []string + AllowedEthAddresses []string + DeniedMvxAddresses []string + AllowedMvxAddresses []string + DeniedTokens []string + AllowedTokens []string +} + +// ScCallsModuleConfig will hold the settings for the SC calls module +type ScCallsModuleConfig struct { + ScProxyBech32Address string + ExtraGasToExecute uint64 + MaxGasLimitToUse uint64 + GasLimitForOutOfGasTransactions uint64 + NetworkAddress string + ProxyMaxNoncesDelta int + ProxyFinalityCheck bool + ProxyCacherExpirationSeconds uint64 + ProxyRestAPIEntityType string + IntervalToResendTxsInSeconds uint64 + PrivateKeyFile string + PollingIntervalInMillis uint64 + Filter PendingOperationsFilterConfig + Logs LogsConfig + TransactionChecks TransactionChecksConfig +} + +// TransactionChecksConfig will hold the setting for how to handle the transaction execution +type TransactionChecksConfig struct { + CheckTransactionResults bool + TimeInSecondsBetweenChecks uint64 + ExecutionTimeoutInSeconds uint64 + CloseAppOnError bool + ExtraDelayInSecondsOnError uint64 +} + +// MigrationToolConfig is the migration tool config struct +type MigrationToolConfig struct { + Eth EthereumConfig + MultiversX MultiversXConfig + Logs LogsConfig +} diff --git a/config/tomlConfigs_test.go b/config/tomlConfigs_test.go new file mode 100644 index 00000000..1dedb717 --- /dev/null +++ b/config/tomlConfigs_test.go @@ -0,0 +1,578 @@ +package config + +import ( + "testing" + + "github.com/multiversx/mx-chain-communication-go/p2p/config" + chainConfig "github.com/multiversx/mx-chain-go/config" + p2pConfig "github.com/multiversx/mx-chain-go/p2p/config" + "github.com/pelletier/go-toml" + "github.com/stretchr/testify/require" +) + +func TestConfigs(t *testing.T) { + t.Parallel() + + expectedConfig := Config{ + Eth: EthereumConfig{ + Chain: "Ethereum", + NetworkAddress: "http://127.0.0.1:8545", + MultisigContractAddress: "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c", + SafeContractAddress: "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + PrivateKeyFile: "keys/ethereum.sk", + IntervalToWaitForTransferInSeconds: 600, + GasLimitBase: 350000, + GasLimitForEach: 30000, + GasStation: GasStationConfig{ + Enabled: true, + URL: "https://api.etherscan.io/api?module=gastracker&action=gasoracle", + PollingIntervalInSeconds: 60, + RequestRetryDelayInSeconds: 5, + MaxFetchRetries: 3, + RequestTimeInSeconds: 2, + MaximumAllowedGasPrice: 300, + GasPriceSelector: "SafeGasPrice", + GasPriceMultiplier: 1000000000, + }, + MaxRetriesOnQuorumReached: 3, + ClientAvailabilityAllowDelta: 10, + EventsBlockRangeFrom: -100, + EventsBlockRangeTo: 400, + }, + MultiversX: MultiversXConfig{ + NetworkAddress: "https://devnet-gateway.multiversx.com", + MultisigContractAddress: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + SafeContractAddress: "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus", + PrivateKeyFile: "keys/multiversx.pem", + IntervalToResendTxsInSeconds: 60, + GasMap: MultiversXGasMapConfig{ + Sign: 8000000, + ProposeTransferBase: 11000000, + ProposeTransferForEach: 5500000, + ProposeStatusBase: 10000000, + ProposeStatusForEach: 7000000, + PerformActionBase: 40000000, + PerformActionForEach: 5500000, + }, + MaxRetriesOnQuorumReached: 3, + MaxRetriesOnWasTransferProposed: 3, + ClientAvailabilityAllowDelta: 10, + Proxy: ProxyConfig{ + CacherExpirationSeconds: 600, + RestAPIEntityType: "observer", + MaxNoncesDelta: 7, + FinalityCheck: true, + }, + }, + P2P: ConfigP2P{ + Port: "10010", + InitialPeerList: make([]string, 0), + ProtocolID: "/erd/relay/1.0.0", + Transports: p2pConfig.P2PTransportConfig{ + TCP: config.TCPProtocolConfig{ + ListenAddress: "/ip4/0.0.0.0/tcp/%d", + PreventPortReuse: false, + }, + QUICAddress: "", + WebSocketAddress: "", + WebTransportAddress: "", + }, + ResourceLimiter: config.ResourceLimiterConfig{ + Type: "default autoscale", + ManualSystemMemoryInMB: 1, + ManualMaximumFD: 2, + }, + AntifloodConfig: chainConfig.AntifloodConfig{ + Enabled: true, + NumConcurrentResolverJobs: 50, + OutOfSpecs: chainConfig.FloodPreventerConfig{ + IntervalInSeconds: 1, + ReservedPercent: 0, + PeerMaxInput: chainConfig.AntifloodLimitsConfig{ + BaseMessagesPerInterval: 140, + TotalSizePerInterval: 4194304, + IncreaseFactor: chainConfig.IncreaseFactorConfig{ + Threshold: 0, + Factor: 0, + }, + }, + BlackList: chainConfig.BlackListConfig{ + ThresholdNumMessagesPerInterval: 200, + ThresholdSizePerInterval: 6291456, + NumFloodingRounds: 2, + PeerBanDurationInSeconds: 3600, + }, + }, + FastReacting: chainConfig.FloodPreventerConfig{ + IntervalInSeconds: 1, + ReservedPercent: 20, + PeerMaxInput: chainConfig.AntifloodLimitsConfig{ + BaseMessagesPerInterval: 10, + TotalSizePerInterval: 1048576, + IncreaseFactor: chainConfig.IncreaseFactorConfig{ + Threshold: 10, + Factor: 1, + }, + }, + BlackList: chainConfig.BlackListConfig{ + ThresholdNumMessagesPerInterval: 70, + ThresholdSizePerInterval: 2097154, + NumFloodingRounds: 10, + PeerBanDurationInSeconds: 300, + }, + }, + SlowReacting: chainConfig.FloodPreventerConfig{ + IntervalInSeconds: 30, + ReservedPercent: 20, + PeerMaxInput: chainConfig.AntifloodLimitsConfig{ + BaseMessagesPerInterval: 400, + TotalSizePerInterval: 10485760, + IncreaseFactor: chainConfig.IncreaseFactorConfig{ + Threshold: 10, + Factor: 0, + }, + }, + BlackList: chainConfig.BlackListConfig{ + ThresholdNumMessagesPerInterval: 800, + ThresholdSizePerInterval: 20971540, + NumFloodingRounds: 2, + PeerBanDurationInSeconds: 3600, + }, + }, + PeerMaxOutput: chainConfig.AntifloodLimitsConfig{ + BaseMessagesPerInterval: 5, + TotalSizePerInterval: 524288, + IncreaseFactor: chainConfig.IncreaseFactorConfig{}, + }, + Cache: chainConfig.CacheConfig{ + Name: "Antiflood", + Type: "LRU", + Capacity: 7000, + }, + Topic: chainConfig.TopicAntifloodConfig{ + DefaultMaxMessagesPerSec: 300, + MaxMessages: []chainConfig.TopicMaxMessagesConfig{ + { + Topic: "EthereumToMultiversX_join", + NumMessagesPerSec: 100, + }, + { + Topic: "EthereumToMultiversX_sign", + NumMessagesPerSec: 100, + }, + }, + }, + TxAccumulator: chainConfig.TxAccumulatorConfig{}, + }, + }, + StateMachine: map[string]ConfigStateMachine{ + "EthereumToMultiversX": { + StepDurationInMillis: 12000, + IntervalForLeaderInSeconds: 120, + }, + "MultiversXToEthereum": { + StepDurationInMillis: 12000, + IntervalForLeaderInSeconds: 720, + }, + }, + Relayer: ConfigRelayer{ + Marshalizer: chainConfig.MarshalizerConfig{ + Type: "gogo protobuf", + SizeCheckDelta: 10, + }, + RoleProvider: RoleProviderConfig{ + PollingIntervalInMillis: 60000, + }, + StatusMetricsStorage: chainConfig.StorageConfig{ + Cache: chainConfig.CacheConfig{ + Name: "StatusMetricsStorage", + Type: "LRU", + Capacity: 1000, + }, + DB: chainConfig.DBConfig{ + FilePath: "StatusMetricsStorageDB", + Type: "LvlDBSerial", + BatchDelaySeconds: 2, + MaxBatchSize: 100, + MaxOpenFiles: 10, + }, + }, + }, + Logs: LogsConfig{ + LogFileLifeSpanInSec: 86400, + LogFileLifeSpanInMB: 1024, + }, + WebAntiflood: WebAntifloodConfig{ + Enabled: true, + WebServer: WebServerAntifloodConfig{ + SimultaneousRequests: 100, + SameSourceRequests: 10000, + SameSourceResetIntervalInSec: 1, + }, + }, + PeersRatingConfig: PeersRatingConfig{ + TopRatedCacheCapacity: 5000, + BadRatedCacheCapacity: 5000, + }, + } + + testString := ` +[Eth] + Chain = "Ethereum" + NetworkAddress = "http://127.0.0.1:8545" # a network address + MultisigContractAddress = "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c" # the eth address for the bridge contract + SafeContractAddress = "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c" + PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key + GasLimitBase = 350000 + GasLimitForEach = 30000 + IntervalToWaitForTransferInSeconds = 600 #10 minutes + MaxRetriesOnQuorumReached = 3 + ClientAvailabilityAllowDelta = 10 + EventsBlockRangeFrom = -100 + EventsBlockRangeTo = 400 + [Eth.GasStation] + Enabled = true + URL = "https://api.etherscan.io/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 1000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + PollingIntervalInSeconds = 60 # number of seconds between gas price polling + RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request + MaxFetchRetries = 3 # number of fetch retries before printing an error + RequestTimeInSeconds = 2 # maximum timeout (in seconds) for the gas price request + MaximumAllowedGasPrice = 300 # maximum value allowed for the fetched gas price value + # GasPriceSelector available options: "SafeGasPrice", "ProposeGasPrice", "FastGasPrice" + GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price + +[MultiversX] + NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf" # the multiversx address for the bridge contract + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract + PrivateKeyFile = "keys/multiversx.pem" # the path to the pem file containing the relayer multiversx wallet + IntervalToResendTxsInSeconds = 60 # the time in seconds between nonce reads + MaxRetriesOnQuorumReached = 3 + MaxRetriesOnWasTransferProposed = 3 + ClientAvailabilityAllowDelta = 10 + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds + + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "observer" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + [MultiversX.GasMap] + Sign = 8000000 + ProposeTransferBase = 11000000 + ProposeTransferForEach = 5500000 + ProposeStatusBase = 10000000 + ProposeStatusForEach = 7000000 + PerformActionBase = 40000000 + PerformActionForEach = 5500000 + +[P2P] + Port = "10010" + InitialPeerList = [] + ProtocolID = "/erd/relay/1.0.0" + [P2P.Transports] + QUICAddress = "" # optional QUIC address. If this transport should be activated, should be in this format: /ip4/0.0.0.0/udp/%d/quic-v1 + WebSocketAddress = "" # optional WebSocket address. If this transport should be activated, should be in this format: /ip4/0.0.0.0/tcp/%d/ws + WebTransportAddress = "" # optional WebTransport address. If this transport should be activated, should be in this format: /ip4/0.0.0.0/udp/%d/quic-v1/webtransport + [P2P.Transports.TCP] + ListenAddress = "/ip4/0.0.0.0/tcp/%d" # TCP listen address + PreventPortReuse = false + [P2P.ResourceLimiter] + Type = "default autoscale" #available options "default autoscale", "infinite", "default with manual scale". + ManualSystemMemoryInMB = 1 # not taken into account if the type is not "default with manual scale" + ManualMaximumFD = 2 # not taken into account if the type is not "default with manual scale" + [P2P.AntifloodConfig] + Enabled = true + NumConcurrentResolverJobs = 50 + [P2P.AntifloodConfig.FastReacting] + IntervalInSeconds = 1 + ReservedPercent = 20.0 + [P2P.AntifloodConfig.FastReacting.PeerMaxInput] + BaseMessagesPerInterval = 10 + TotalSizePerInterval = 1048576 #1MB/s + [P2P.AntifloodConfig.FastReacting.PeerMaxInput.IncreaseFactor] + Threshold = 10 #if consensus size will exceed this value, then + Factor = 1.0 #increase the base value with [factor*consensus size] + [P2P.AntifloodConfig.FastReacting.BlackList] + ThresholdNumMessagesPerInterval = 70 + ThresholdSizePerInterval = 2097154 #2MB/s + NumFloodingRounds = 10 + PeerBanDurationInSeconds = 300 + + [P2P.AntifloodConfig.SlowReacting] + IntervalInSeconds = 30 + ReservedPercent = 20.0 + [P2P.AntifloodConfig.SlowReacting.PeerMaxInput] + BaseMessagesPerInterval = 400 + TotalSizePerInterval = 10485760 #10MB/interval + [P2P.AntifloodConfig.SlowReacting.PeerMaxInput.IncreaseFactor] + Threshold = 10 #if consensus size will exceed this value, then + Factor = 0.0 #increase the base value with [factor*consensus size] + [P2P.AntifloodConfig.SlowReacting.BlackList] + ThresholdNumMessagesPerInterval = 800 + ThresholdSizePerInterval = 20971540 #20MB/interval + NumFloodingRounds = 2 + PeerBanDurationInSeconds = 3600 + + [P2P.AntifloodConfig.OutOfSpecs] + IntervalInSeconds = 1 + ReservedPercent = 0.0 + [P2P.AntifloodConfig.OutOfSpecs.PeerMaxInput] + BaseMessagesPerInterval = 140 + TotalSizePerInterval = 4194304 #4MB/s + [P2P.AntifloodConfig.OutOfSpecs.PeerMaxInput.IncreaseFactor] + Threshold = 0 #if consensus size will exceed this value, then + Factor = 0.0 #increase the base value with [factor*consensus size] + [P2P.AntifloodConfig.OutOfSpecs.BlackList] + ThresholdNumMessagesPerInterval = 200 + ThresholdSizePerInterval = 6291456 #6MB/s + NumFloodingRounds = 2 + PeerBanDurationInSeconds = 3600 + + [P2P.AntifloodConfig.PeerMaxOutput] + BaseMessagesPerInterval = 5 + TotalSizePerInterval = 524288 #512KB/s + + [P2P.AntifloodConfig.Cache] + Name = "Antiflood" + Capacity = 7000 + Type = "LRU" + [P2P.AntifloodConfig.Topic] + DefaultMaxMessagesPerSec = 300 # default number of messages per interval for a topic + MaxMessages = [{ Topic = "EthereumToMultiversX_join", NumMessagesPerSec = 100 }, + { Topic = "EthereumToMultiversX_sign", NumMessagesPerSec = 100 }] + +[Relayer] + [Relayer.Marshalizer] + Type = "gogo protobuf" + SizeCheckDelta = 10 + [Relayer.RoleProvider] + PollingIntervalInMillis = 60000 # 1 minute + [Relayer.StatusMetricsStorage] + [Relayer.StatusMetricsStorage.Cache] + Name = "StatusMetricsStorage" + Capacity = 1000 + Type = "LRU" + [Relayer.StatusMetricsStorage.DB] + FilePath = "StatusMetricsStorageDB" + Type = "LvlDBSerial" + BatchDelaySeconds = 2 + MaxBatchSize = 100 + MaxOpenFiles = 10 + +[StateMachine] + [StateMachine.EthereumToMultiversX] + StepDurationInMillis = 12000 #12 seconds + IntervalForLeaderInSeconds = 120 #2 minutes + + [StateMachine.MultiversXToEthereum] + StepDurationInMillis = 12000 #12 seconds + IntervalForLeaderInSeconds = 720 #12 minutes + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB + +[WebAntiflood] + Enabled = true + [WebAntiflood.WebServer] + # SimultaneousRequests represents the number of concurrent requests accepted by the web server + # this is a global throttler that acts on all http connections regardless of the originating source + SimultaneousRequests = 100 + # SameSourceRequests defines how many requests are allowed from the same source in the specified + # time frame (SameSourceResetIntervalInSec) + SameSourceRequests = 10000 + # SameSourceResetIntervalInSec time frame between counter reset, in seconds + SameSourceResetIntervalInSec = 1 + +[PeersRatingConfig] + TopRatedCacheCapacity = 5000 + BadRatedCacheCapacity = 5000 + +` + + cfg := Config{} + + err := toml.Unmarshal([]byte(testString), &cfg) + + require.Nil(t, err) + require.Equal(t, expectedConfig, cfg) +} + +func TestScCallsExecutorConfigs(t *testing.T) { + t.Parallel() + + expectedConfig := ScCallsModuleConfig{ + ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s", + ExtraGasToExecute: 50000000, + MaxGasLimitToUse: 249999999, + GasLimitForOutOfGasTransactions: 30000000, + NetworkAddress: "127.0.0.1:8085", + ProxyMaxNoncesDelta: 7, + ProxyFinalityCheck: true, + ProxyCacherExpirationSeconds: 600, + ProxyRestAPIEntityType: "observer", + IntervalToResendTxsInSeconds: 60, + PrivateKeyFile: "keys/multiversx.pem", + PollingIntervalInMillis: 6000, + Filter: PendingOperationsFilterConfig{ + AllowedEthAddresses: []string{"*"}, + AllowedMvxAddresses: []string{"*"}, + AllowedTokens: []string{"MEME-a43fa1"}, + }, + Logs: LogsConfig{ + LogFileLifeSpanInSec: 86400, + LogFileLifeSpanInMB: 1024, + }, + TransactionChecks: TransactionChecksConfig{ + CheckTransactionResults: true, + TimeInSecondsBetweenChecks: 6, + ExecutionTimeoutInSeconds: 120, + CloseAppOnError: false, + ExtraDelayInSecondsOnError: 120, + }, + } + + testString := ` +ScProxyBech32Address = "erd1qqqqqqqqqqqqqpgqnef5f5aq32d63kljld8w5vnvz4gk5sy9hrrq2ld08s" +ExtraGasToExecute = 50000000 +MaxGasLimitToUse = 249999999 # this is a safe max gas limit to use both intra-shard & cross-shard +GasLimitForOutOfGasTransactions = 30000000 # this value will be used when a transaction specified a gas limit > 249999999 +NetworkAddress = "127.0.0.1:8085" +ProxyMaxNoncesDelta = 7 +ProxyFinalityCheck = true +ProxyCacherExpirationSeconds = 600 +ProxyRestAPIEntityType = "observer" +IntervalToResendTxsInSeconds = 60 +PrivateKeyFile = "keys/multiversx.pem" +PollingIntervalInMillis = 6000 + +[Filter] + AllowedEthAddresses = ["*"] # execute SC calls from all ETH addresses + AllowedMvxAddresses = ["*"] # execute SC calls to all MvX contracts + AllowedTokens = ["MEME-a43fa1"] # execute SC calls for this token only + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB + +[TransactionChecks] + CheckTransactionResults = true # enable or disable the transaction execution checking + TimeInSecondsBetweenChecks = 6 # the number of seconds to recheck the status of the transaction + ExecutionTimeoutInSeconds = 120 # the number of seconds after the transaction is considered failed if it was not seen by the blockchain + CloseAppOnError = false # enable or disable if the executor should automatically close on a transaction execution error + ExtraDelayInSecondsOnError = 120 # extra delay in seconds if the transaction execution errored +` + + cfg := ScCallsModuleConfig{} + + err := toml.Unmarshal([]byte(testString), &cfg) + + require.Nil(t, err) + require.Equal(t, expectedConfig, cfg) + +} + +func TestMigrationToolConfig(t *testing.T) { + t.Parallel() + + expectedConfig := MigrationToolConfig{ + Eth: EthereumConfig{ + Chain: "Ethereum", + NetworkAddress: "http://127.0.0.1:8545", + SafeContractAddress: "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + PrivateKeyFile: "keys/ethereum.sk", + GasLimitBase: 350000, + GasLimitForEach: 30000, + GasStation: GasStationConfig{ + Enabled: true, + URL: "https://api.etherscan.io/api?module=gastracker&action=gasoracle", + PollingIntervalInSeconds: 60, + RequestRetryDelayInSeconds: 5, + MaxFetchRetries: 3, + RequestTimeInSeconds: 2, + MaximumAllowedGasPrice: 300, + GasPriceSelector: "SafeGasPrice", + GasPriceMultiplier: 1000000000, + }, + }, + MultiversX: MultiversXConfig{ + NetworkAddress: "https://devnet-gateway.multiversx.com", + MultisigContractAddress: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + SafeContractAddress: "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus", + Proxy: ProxyConfig{ + CacherExpirationSeconds: 600, + RestAPIEntityType: "observer", + MaxNoncesDelta: 7, + FinalityCheck: true, + }, + }, + Logs: LogsConfig{ + LogFileLifeSpanInSec: 86400, + LogFileLifeSpanInMB: 1024, + }, + } + + testString := ` +[Eth] + Chain = "Ethereum" + NetworkAddress = "http://127.0.0.1:8545" # a network address + PrivateKeyFile = "keys/ethereum.sk" # the path to the file containing the relayer eth private key + SafeContractAddress = "A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c" + GasLimitBase = 350000 + GasLimitForEach = 30000 + [Eth.GasStation] + Enabled = true + URL = "https://api.etherscan.io/api?module=gastracker&action=gasoracle" # gas station URL. Suggestion to provide the api-key here + GasPriceMultiplier = 1000000000 # the value to be multiplied with the fetched value. Useful in test chains. On production chain should be 1000000000 + PollingIntervalInSeconds = 60 # number of seconds between gas price polling + RequestRetryDelayInSeconds = 5 # number of seconds of delay after one failed request + MaxFetchRetries = 3 # number of fetch retries before printing an error + RequestTimeInSeconds = 2 # maximum timeout (in seconds) for the gas price request + MaximumAllowedGasPrice = 300 # maximum value allowed for the fetched gas price value + # GasPriceSelector available options: "SafeGasPrice", "ProposeGasPrice", "FastGasPrice" + GasPriceSelector = "SafeGasPrice" # selector used to provide the gas price + +[MultiversX] + NetworkAddress = "https://devnet-gateway.multiversx.com" # the network address + MultisigContractAddress = "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf" # the multiversx address for the bridge contract + SafeContractAddress = "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus" # the multiversx address for the safe contract + [MultiversX.Proxy] + CacherExpirationSeconds = 600 # the caching time in seconds + + # valid options for ProxyRestAPIEntityType are "observer" and "proxy". Any other value will trigger an error. + # "observer" is useful when querying an observer, directly and "proxy" is useful when querying a squad's proxy (gateway) + RestAPIEntityType = "observer" + FinalityCheck = true + MaxNoncesDelta = 7 # the number of maximum blocks allowed to be "in front" of what the metachain has notarized + +[Logs] + LogFileLifeSpanInSec = 86400 # 24h + LogFileLifeSpanInMB = 1024 # 1GB + +[WhitelistedTokens] + List = [ + "ETHUSDC-220753", + "ETHUTK-8cdf7a", + "ETHUSDT-9c73c6", + "ETHBUSD-450923", + "ETHHMT-18538a", + "ETHCGG-ee4e0c", + "ETHINFRA-60a3bf", + "ETHWBTC-74e282", + "ETHWETH-e1c126", + "ETHWSDAI-572803", + "ETHWDAI-bd65f9", + "ETHUMB-291202", + ] +` + + cfg := MigrationToolConfig{} + + err := toml.Unmarshal([]byte(testString), &cfg) + + require.Nil(t, err) + require.Equal(t, expectedConfig, cfg) +} diff --git a/clients/batch.go b/core/batch.go similarity index 57% rename from clients/batch.go rename to core/batch.go index 601c0630..c0907914 100644 --- a/clients/batch.go +++ b/core/batch.go @@ -1,4 +1,4 @@ -package clients +package core import ( "encoding/hex" @@ -12,9 +12,10 @@ var log = logger.GetOrCreate("clients") // TransferBatch is the transfer batch structure agnostic of any chain implementation type TransferBatch struct { - ID uint64 `json:"batchId"` - Deposits []*DepositTransfer `json:"deposits"` - Statuses []byte `json:"statuses"` + ID uint64 `json:"batchId"` + BlockNumber uint64 `json:"blockNumber"` + Deposits []*DepositTransfer `json:"deposits"` + Statuses []byte `json:"statuses"` } // Clone will deep clone the current TransferBatch instance @@ -65,41 +66,46 @@ func (tb *TransferBatch) ResolveNewDeposits(newNumDeposits int) { // DepositTransfer is the deposit transfer structure agnostic of any chain implementation type DepositTransfer struct { - Nonce uint64 `json:"nonce"` - ToBytes []byte `json:"-"` - DisplayableTo string `json:"to"` - FromBytes []byte `json:"-"` - DisplayableFrom string `json:"from"` - TokenBytes []byte `json:"-"` - ConvertedTokenBytes []byte `json:"-"` - DisplayableToken string `json:"token"` - Amount *big.Int `json:"amount"` + Nonce uint64 `json:"nonce"` + ToBytes []byte `json:"-"` + DisplayableTo string `json:"to"` + FromBytes []byte `json:"-"` + DisplayableFrom string `json:"from"` + SourceTokenBytes []byte `json:"-"` + DestinationTokenBytes []byte `json:"-"` + DisplayableToken string `json:"token"` + Amount *big.Int `json:"amount"` + Data []byte `json:"-"` + DisplayableData string `json:"data"` } // String will convert the deposit transfer to a string func (dt *DepositTransfer) String() string { - return fmt.Sprintf("to: %s, from: %s, token address: %s, amount: %v, deposit nonce: %d", - dt.DisplayableTo, dt.DisplayableFrom, dt.DisplayableToken, dt.Amount, dt.Nonce) + return fmt.Sprintf("to: %s, from: %s, token address: %s, amount: %v, deposit nonce: %d, data: %s", + dt.DisplayableTo, dt.DisplayableFrom, dt.DisplayableToken, dt.Amount, dt.Nonce, dt.DisplayableData) } -// Clone will deep clone the current DepositTransfer instance +// Clone will deeply clone the current DepositTransfer instance func (dt *DepositTransfer) Clone() *DepositTransfer { cloned := &DepositTransfer{ - Nonce: dt.Nonce, - ToBytes: make([]byte, len(dt.ToBytes)), - DisplayableTo: dt.DisplayableTo, - FromBytes: make([]byte, len(dt.FromBytes)), - DisplayableFrom: dt.DisplayableFrom, - TokenBytes: make([]byte, len(dt.TokenBytes)), - ConvertedTokenBytes: make([]byte, len(dt.ConvertedTokenBytes)), - DisplayableToken: dt.DisplayableToken, - Amount: big.NewInt(0), + Nonce: dt.Nonce, + ToBytes: make([]byte, len(dt.ToBytes)), + DisplayableTo: dt.DisplayableTo, + FromBytes: make([]byte, len(dt.FromBytes)), + DisplayableFrom: dt.DisplayableFrom, + SourceTokenBytes: make([]byte, len(dt.SourceTokenBytes)), + DestinationTokenBytes: make([]byte, len(dt.DestinationTokenBytes)), + DisplayableToken: dt.DisplayableToken, + Amount: big.NewInt(0), + Data: make([]byte, len(dt.Data)), + DisplayableData: dt.DisplayableData, } copy(cloned.ToBytes, dt.ToBytes) copy(cloned.FromBytes, dt.FromBytes) - copy(cloned.TokenBytes, dt.TokenBytes) - copy(cloned.ConvertedTokenBytes, dt.ConvertedTokenBytes) + copy(cloned.SourceTokenBytes, dt.SourceTokenBytes) + copy(cloned.DestinationTokenBytes, dt.DestinationTokenBytes) + copy(cloned.Data, dt.Data) if dt.Amount != nil { cloned.Amount.Set(dt.Amount) } diff --git a/core/batchProcessor/batchProcessor.go b/core/batchProcessor/batchProcessor.go new file mode 100644 index 00000000..a8ec2bcd --- /dev/null +++ b/core/batchProcessor/batchProcessor.go @@ -0,0 +1,80 @@ +package batchProcessor + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" +) + +// Direction is the direction of the transfer +type Direction string + +const ( + // FromMultiversX is the direction of the transfer + FromMultiversX Direction = "FromMultiversX" + // ToMultiversX is the direction of the transfer + ToMultiversX Direction = "ToMultiversX" +) + +// ArgListsBatch is a struct that contains the batch data in a format that is easy to use +type ArgListsBatch struct { + EthTokens []common.Address + Recipients []common.Address + MvxTokenBytes [][]byte + Amounts []*big.Int + Nonces []*big.Int + Direction Direction +} + +// ExtractListMvxToEth will extract the batch data into a format that is easy to use +// The transfer is from MultiversX to Ethereum +func ExtractListMvxToEth(batch *bridgeCore.TransferBatch) *ArgListsBatch { + arg := &ArgListsBatch{ + Direction: FromMultiversX, + } + + for _, dt := range batch.Deposits { + recipient := common.BytesToAddress(dt.ToBytes) + arg.Recipients = append(arg.Recipients, recipient) + + token := common.BytesToAddress(dt.DestinationTokenBytes) + arg.EthTokens = append(arg.EthTokens, token) + + amount := big.NewInt(0).Set(dt.Amount) + arg.Amounts = append(arg.Amounts, amount) + + nonce := big.NewInt(0).SetUint64(dt.Nonce) + arg.Nonces = append(arg.Nonces, nonce) + + arg.MvxTokenBytes = append(arg.MvxTokenBytes, dt.SourceTokenBytes) + } + + return arg +} + +// ExtractListEthToMvx will extract the batch data into a format that is easy to use +// The transfer is from Ehtereum to MultiversX +func ExtractListEthToMvx(batch *bridgeCore.TransferBatch) *ArgListsBatch { + arg := &ArgListsBatch{ + Direction: ToMultiversX, + } + + for _, dt := range batch.Deposits { + recipient := common.BytesToAddress(dt.ToBytes) + arg.Recipients = append(arg.Recipients, recipient) + + token := common.BytesToAddress(dt.SourceTokenBytes) + arg.EthTokens = append(arg.EthTokens, token) + + amount := big.NewInt(0).Set(dt.Amount) + arg.Amounts = append(arg.Amounts, amount) + + nonce := big.NewInt(0).SetUint64(dt.Nonce) + arg.Nonces = append(arg.Nonces, nonce) + + arg.MvxTokenBytes = append(arg.MvxTokenBytes, dt.DestinationTokenBytes) + } + + return arg +} diff --git a/core/batchProcessor/batchProcessor_test.go b/core/batchProcessor/batchProcessor_test.go new file mode 100644 index 00000000..20837086 --- /dev/null +++ b/core/batchProcessor/batchProcessor_test.go @@ -0,0 +1,128 @@ +package batchProcessor + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/stretchr/testify/assert" +) + +func TestExtractListEthToMvx(t *testing.T) { + t.Parallel() + + testBatch := &bridgeCore.TransferBatch{ + ID: 37, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: 1, + ToBytes: []byte("to 1"), + FromBytes: []byte("from 1"), + SourceTokenBytes: []byte("source token 1"), + DestinationTokenBytes: []byte("destination token 1"), + Amount: big.NewInt(11), + }, + { + Nonce: 2, + ToBytes: []byte("to 2"), + FromBytes: []byte("from 2"), + SourceTokenBytes: []byte("source token 2"), + DestinationTokenBytes: []byte("destination token 2"), + Amount: big.NewInt(22), + }, + }, + Statuses: nil, + } + + args := ExtractListEthToMvx(testBatch) + + expectedEthTokens := []common.Address{ + common.BytesToAddress([]byte("source token 1")), + common.BytesToAddress([]byte("source token 2")), + } + assert.Equal(t, expectedEthTokens, args.EthTokens) + + expectedRecipients := []common.Address{ + common.BytesToAddress([]byte("to 1")), + common.BytesToAddress([]byte("to 2")), + } + assert.Equal(t, expectedRecipients, args.Recipients) + + expectedMvxTokenBytes := [][]byte{ + []byte("destination token 1"), + []byte("destination token 2"), + } + assert.Equal(t, expectedMvxTokenBytes, args.MvxTokenBytes) + + expectedAmounts := []*big.Int{ + big.NewInt(11), + big.NewInt(22), + } + assert.Equal(t, expectedAmounts, args.Amounts) + + expectedNonces := []*big.Int{ + big.NewInt(1), + big.NewInt(2), + } + assert.Equal(t, expectedNonces, args.Nonces) +} + +func TestExtractListMvxToEth(t *testing.T) { + t.Parallel() + + testBatch := &bridgeCore.TransferBatch{ + ID: 37, + Deposits: []*bridgeCore.DepositTransfer{ + { + Nonce: 1, + ToBytes: []byte("to 1"), + FromBytes: []byte("from 1"), + SourceTokenBytes: []byte("source token 1"), + DestinationTokenBytes: []byte("destination token 1"), + Amount: big.NewInt(11), + }, + { + Nonce: 2, + ToBytes: []byte("to 2"), + FromBytes: []byte("from 2"), + SourceTokenBytes: []byte("source token 2"), + DestinationTokenBytes: []byte("destination token 2"), + Amount: big.NewInt(22), + }, + }, + Statuses: nil, + } + + args := ExtractListMvxToEth(testBatch) + + expectedEthTokens := []common.Address{ + common.BytesToAddress([]byte("destination token 1")), + common.BytesToAddress([]byte("destination token 2")), + } + assert.Equal(t, expectedEthTokens, args.EthTokens) + + expectedRecipients := []common.Address{ + common.BytesToAddress([]byte("to 1")), + common.BytesToAddress([]byte("to 2")), + } + assert.Equal(t, expectedRecipients, args.Recipients) + + expectedMvxTokenBytes := [][]byte{ + []byte("source token 1"), + []byte("source token 2"), + } + assert.Equal(t, expectedMvxTokenBytes, args.MvxTokenBytes) + + expectedAmounts := []*big.Int{ + big.NewInt(11), + big.NewInt(22), + } + assert.Equal(t, expectedAmounts, args.Amounts) + + expectedNonces := []*big.Int{ + big.NewInt(1), + big.NewInt(2), + } + assert.Equal(t, expectedNonces, args.Nonces) +} diff --git a/clients/batch_test.go b/core/batch_test.go similarity index 65% rename from clients/batch_test.go rename to core/batch_test.go index 2f362c4d..48b2cef7 100644 --- a/clients/batch_test.go +++ b/core/batch_test.go @@ -1,4 +1,4 @@ -package clients +package core import ( "math/big" @@ -11,15 +11,16 @@ func TestDepositTransfer_Clone(t *testing.T) { t.Parallel() dt := &DepositTransfer{ - Nonce: 112334, - ToBytes: []byte("to"), - DisplayableTo: "to", - FromBytes: []byte("from"), - DisplayableFrom: "from", - TokenBytes: []byte("token"), - DisplayableToken: "token", - Amount: big.NewInt(7463), - ConvertedTokenBytes: []byte("converted token"), + Nonce: 112334, + ToBytes: []byte("to"), + DisplayableTo: "to", + FromBytes: []byte("from"), + DisplayableFrom: "from", + SourceTokenBytes: []byte("source token"), + DisplayableToken: "token", + Amount: big.NewInt(7463), + DestinationTokenBytes: []byte("destination token"), + Data: []byte("tx data"), } cloned := dt.Clone() @@ -37,12 +38,12 @@ func TestDepositTransfer_String(t *testing.T) { DisplayableTo: "to", FromBytes: []byte("from"), DisplayableFrom: "from", - TokenBytes: []byte("token"), + SourceTokenBytes: []byte("source token"), DisplayableToken: "token", Amount: big.NewInt(7463), } - expectedString := "to: to, from: from, token address: token, amount: 7463, deposit nonce: 112334" + expectedString := "to: to, from: from, token address: token, amount: 7463, deposit nonce: 112334, data: " assert.Equal(t, expectedString, dt.String()) } @@ -53,26 +54,28 @@ func TestTransferBatch_Clone(t *testing.T) { ID: 2243, Deposits: []*DepositTransfer{ { - Nonce: 1, - ToBytes: []byte("to1"), - DisplayableTo: "to1", - FromBytes: []byte("from1"), - DisplayableFrom: "from1", - TokenBytes: []byte("token1"), - DisplayableToken: "token1", - Amount: big.NewInt(3344), - ConvertedTokenBytes: []byte("converted token1"), + Nonce: 1, + ToBytes: []byte("to1"), + DisplayableTo: "to1", + FromBytes: []byte("from1"), + DisplayableFrom: "from1", + SourceTokenBytes: []byte("source token1"), + DisplayableToken: "token1", + Amount: big.NewInt(3344), + DestinationTokenBytes: []byte("destination token1"), + Data: []byte("tx data"), }, { - Nonce: 2, - ToBytes: []byte("to2"), - DisplayableTo: "to2", - FromBytes: []byte("from2"), - DisplayableFrom: "from2", - TokenBytes: []byte("token2"), - DisplayableToken: "token2", - Amount: big.NewInt(5566), - ConvertedTokenBytes: []byte("converted token2"), + Nonce: 2, + ToBytes: []byte("to2"), + DisplayableTo: "to2", + FromBytes: []byte("from2"), + DisplayableFrom: "from2", + SourceTokenBytes: []byte("source token2"), + DisplayableToken: "token2", + Amount: big.NewInt(5566), + DestinationTokenBytes: []byte("destination token2"), + Data: []byte("tx data"), }, }, Statuses: []byte{Executed, Rejected}, @@ -96,7 +99,7 @@ func TestTransferBatch_String(t *testing.T) { DisplayableTo: "to1", FromBytes: []byte("from1"), DisplayableFrom: "from1", - TokenBytes: []byte("token1"), + SourceTokenBytes: []byte("source token1"), DisplayableToken: "token1", Amount: big.NewInt(3344), }, @@ -106,7 +109,7 @@ func TestTransferBatch_String(t *testing.T) { DisplayableTo: "to2", FromBytes: []byte("from2"), DisplayableFrom: "from2", - TokenBytes: []byte("token2"), + SourceTokenBytes: []byte("source token2"), DisplayableToken: "token2", Amount: big.NewInt(5566), }, @@ -115,8 +118,8 @@ func TestTransferBatch_String(t *testing.T) { } expectedString := `Batch id 2243: - to: to1, from: from1, token address: token1, amount: 3344, deposit nonce: 1 - to: to2, from: from2, token address: token2, amount: 5566, deposit nonce: 2 + to: to1, from: from1, token address: token1, amount: 3344, deposit nonce: 1, data: + to: to2, from: from2, token address: token2, amount: 5566, deposit nonce: 2, data: Statuses: 0304` assert.Equal(t, expectedString, tb.String()) } diff --git a/core/constants.go b/core/constants.go index 85f6da20..0af06355 100644 --- a/core/constants.go +++ b/core/constants.go @@ -1,5 +1,24 @@ package core +const ( + // Executed is the Executed with success status value + Executed = byte(3) + // Rejected is the Rejected status value + Rejected = byte(4) + + // Uint32ArgBytes is the constant used for the number of bytes to encode an Uint32 value + Uint32ArgBytes = 4 + + // Uint64ArgBytes is the constant used for the number of bytes to encode an Uint64 value + Uint64ArgBytes = 8 + + // MissingDataProtocolMarker defines the marker for missing data (simple transfers) + MissingDataProtocolMarker byte = 0x00 + + // DataPresentProtocolMarker defines the marker for existing data (transfers with SC calls) + DataPresentProtocolMarker byte = 0x01 +) + const ( // EthFastGasPrice represents the fast gas price value EthFastGasPrice EthGasPriceSelector = "FastGasPrice" diff --git a/bridges/ethMultiversX/constants_test.go b/core/constants_test.go similarity index 92% rename from bridges/ethMultiversX/constants_test.go rename to core/constants_test.go index c7d4c185..57191cff 100644 --- a/bridges/ethMultiversX/constants_test.go +++ b/core/constants_test.go @@ -1,4 +1,4 @@ -package ethmultiversx +package core import ( "testing" diff --git a/core/converters/converters.go b/core/converters/converters.go index 78b731e3..d1549019 100644 --- a/core/converters/converters.go +++ b/core/converters/converters.go @@ -6,13 +6,11 @@ import ( "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-core-go/core/pubkeyConverter" - logger "github.com/multiversx/mx-chain-logger-go" sdkCore "github.com/multiversx/mx-sdk-go/core" ) -var log = logger.GetOrCreate("core") - const hexPrefix = "0x" +const hrp = "erd" type addressConverter struct { converter core.PubkeyConverter @@ -22,7 +20,7 @@ type addressConverter struct { func NewAddressConverter() (*addressConverter, error) { var err error ac := &addressConverter{} - ac.converter, err = pubkeyConverter.NewBech32PubkeyConverter(sdkCore.AddressBytesLen, log) + ac.converter, err = pubkeyConverter.NewBech32PubkeyConverter(sdkCore.AddressBytesLen, hrp) if err != nil { return nil, err } @@ -41,10 +39,17 @@ func (ac *addressConverter) ToHexStringWithPrefix(addressBytes []byte) string { } // ToBech32String will convert the addressBytes to the bech32 representation -func (ac *addressConverter) ToBech32String(addressBytes []byte) string { +func (ac *addressConverter) ToBech32String(addressBytes []byte) (string, error) { return ac.converter.Encode(addressBytes) } +// ToBech32StringSilent will try to convert the addressBytes to the bech32 representation +func (ac *addressConverter) ToBech32StringSilent(addressBytes []byte) string { + bech32Address, _ := ac.converter.Encode(addressBytes) + + return bech32Address +} + // IsInterfaceNil returns true if there is no value under the interface func (ac *addressConverter) IsInterfaceNil() bool { return ac == nil diff --git a/core/converters/converters_test.go b/core/converters/converters_test.go index 0fe47324..51033ec2 100644 --- a/core/converters/converters_test.go +++ b/core/converters/converters_test.go @@ -46,13 +46,17 @@ func TestAddressConverter_ToBech32String(t *testing.T) { assert.False(t, check.IfNil(addrConv)) t.Run("invalid bytes should return empty", func(t *testing.T) { - str := addrConv.ToBech32String([]byte("invalid")) - assert.Equal(t, "", str) + str, errLocal := addrConv.ToBech32String([]byte("invalid")) + assert.NotNil(t, errLocal) + assert.Contains(t, errLocal.Error(), "wrong size when encoding address, expected length 32, received 7") + assert.Empty(t, str) }) t.Run("should work", func(t *testing.T) { expected := "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede" bytes, _ := hex.DecodeString("1e8a8b6b49de5b7be10aaa158a5a6a4abb4b56cc08f524bb5e6cd5f211ad3e13") - assert.Equal(t, expected, addrConv.ToBech32String(bytes)) + bech32Address, errLocal := addrConv.ToBech32String(bytes) + assert.Equal(t, expected, bech32Address) + assert.Nil(t, errLocal) }) } diff --git a/core/types.go b/core/types.go index 31d2c39d..fe94cb29 100644 --- a/core/types.go +++ b/core/types.go @@ -2,6 +2,7 @@ package core import ( "context" + "fmt" ) // StepIdentifier defines a step name @@ -32,7 +33,8 @@ type Timer interface { type AddressConverter interface { ToHexString(addressBytes []byte) string ToHexStringWithPrefix(addressBytes []byte) string - ToBech32String(addressBytes []byte) string + ToBech32String(addressBytes []byte) (string, error) + ToBech32StringSilent(addressBytes []byte) string IsInterfaceNil() bool } @@ -79,3 +81,23 @@ type StringMetrics map[string]string // IntMetrics represents string metrics map type IntMetrics map[string]int + +// ClientStatus represents the possible statuses of a client +type ClientStatus int + +const ( + Available ClientStatus = 0 + Unavailable ClientStatus = 1 +) + +// String will return status as string based on the int value +func (cs ClientStatus) String() string { + switch cs { + case Available: + return "Available" + case Unavailable: + return "Unavailable" + default: + return fmt.Sprintf("Invalid status %d", cs) + } +} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 00000000..72ecc41b --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.9" + +services: + chain-simulator: + image: multiversx/chainsimulator:v1.7.13-patch2 + ports: + - 8085:8085 + volumes: + - "../scripts:/docker/scripts" + entrypoint: "./chainsimulator -log-level *:INFO" diff --git a/docker/scCallsExecutor-docker-compose.yml b/docker/scCallsExecutor-docker-compose.yml new file mode 100644 index 00000000..66e6bd06 --- /dev/null +++ b/docker/scCallsExecutor-docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.9" + +services: + sc-calls-executor: + build: + context: .. + dockerfile: scCallsExecutor.Dockerfile + restart: unless-stopped + volumes: + - "../keys:/multiversx/keys" + # change the `network-address`, `sc-proxy-address` and `private-key-file` accordingly + entrypoint: "./scCallsExecutor \ + -log-level *:DEBUG \ + -log-save \ + -network-address https://devnet-gateway.multiversx.com \ + -sc-proxy-address erd1qqqqqqqqqqqqqpgq5l0743nyv45vdptmfh3jydkqtyzjqpgsrcjq8yuzxk \ + -private-key-file ./keys/walletKey.pem" diff --git a/clients/multiversx/queryResponseError.go b/errors/queryResponseError.go similarity index 97% rename from clients/multiversx/queryResponseError.go rename to errors/queryResponseError.go index 7a794d43..70df2472 100644 --- a/clients/multiversx/queryResponseError.go +++ b/errors/queryResponseError.go @@ -1,4 +1,4 @@ -package multiversx +package errors import "fmt" diff --git a/clients/multiversx/queryResponseError_test.go b/errors/queryResponseError_test.go similarity index 95% rename from clients/multiversx/queryResponseError_test.go rename to errors/queryResponseError_test.go index 434926a2..faf261f4 100644 --- a/clients/multiversx/queryResponseError_test.go +++ b/errors/queryResponseError_test.go @@ -1,4 +1,4 @@ -package multiversx +package errors import ( "testing" diff --git a/executors/ethereum/bridgeV2Wrappers/contract/Bridge.abi.json b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.abi.json new file mode 100755 index 00000000..0beb35bd --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.abi.json @@ -0,0 +1,514 @@ +[ + { + "inputs": [ + { + "internalType": "address[]", + "name": "board", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "initialQuorum", + "type": "uint256" + }, + { + "internalType": "contract ERC20Safe", + "name": "erc20Safe", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminRoleTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "name": "QuorumChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RelayerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RelayerRemoved", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "addRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSettleBlockCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "crossTransferStatuses", + "outputs": [ + { + "internalType": "uint256", + "name": "createdBlockNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "depositNonces", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "batchNonceElrondETH", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "executeTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "executedBatches", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatch", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "internalType": "struct Batch", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatchDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "internalType": "struct Deposit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRelayer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRelayers", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRelayersCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonceElrondETH", + "type": "uint256" + } + ], + "name": "getStatusesAfterExecution", + "outputs": [ + { + "internalType": "enum DepositStatus[]", + "name": "", + "type": "uint8[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isRelayer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "quorum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "removeRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchSettleLimit", + "type": "uint8" + } + ], + "name": "setBatchSettleLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newQuorum", + "type": "uint256" + } + ], + "name": "setQuorum", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonceElrondETH", + "type": "uint256" + } + ], + "name": "wasBatchExecuted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/clients/ethereum/contract/contract.go b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.go old mode 100644 new mode 100755 similarity index 96% rename from clients/ethereum/contract/contract.go rename to executors/ethereum/bridgeV2Wrappers/contract/Bridge.go index 02d1eece..6c1ad42f --- a/clients/ethereum/contract/contract.go +++ b/executors/ethereum/bridgeV2Wrappers/contract/Bridge.go @@ -26,6 +26,7 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // Batch is an auto generated low-level Go binding around an user-defined struct. @@ -48,7 +49,7 @@ type Deposit struct { // BridgeMetaData contains all meta data concerning the Bridge contract. var BridgeMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"board\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"initialQuorum\",\"type\":\"uint256\"},{\"internalType\":\"contractERC20Safe\",\"name\":\"erc20Safe\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminRoleTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"quorum\",\"type\":\"uint256\"}],\"name\":\"QuorumChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerRemoved\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"addRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleBlockCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"crossTransferStatuses\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"createdBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"depositNonces\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"executeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"executedBatches\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatchDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRelayer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayersCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"}],\"name\":\"getStatusesAfterExecution\",\"outputs\":[{\"internalType\":\"enumDepositStatus[]\",\"name\":\"\",\"type\":\"uint8[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isRelayer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"removeRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorum\",\"type\":\"uint256\"}],\"name\":\"setQuorum\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"}],\"name\":\"wasBatchExecuted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"board\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"initialQuorum\",\"type\":\"uint256\"},{\"internalType\":\"contractERC20Safe\",\"name\":\"erc20Safe\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminRoleTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"quorum\",\"type\":\"uint256\"}],\"name\":\"QuorumChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RelayerRemoved\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"addRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleBlockCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"crossTransferStatuses\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"createdBlockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256[]\",\"name\":\"depositNonces\",\"type\":\"uint256[]\"},{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"signatures\",\"type\":\"bytes[]\"}],\"name\":\"executeTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"executedBatches\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatchDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getRelayer\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getRelayersCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"}],\"name\":\"getStatusesAfterExecution\",\"outputs\":[{\"internalType\":\"enumDepositStatus[]\",\"name\":\"\",\"type\":\"uint8[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"isRelayer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"quorum\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"removeRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRelayer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchSettleLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchSettleLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newQuorum\",\"type\":\"uint256\"}],\"name\":\"setQuorum\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonceElrondETH\",\"type\":\"uint256\"}],\"name\":\"wasBatchExecuted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // BridgeABI is the input ABI used to generate the binding from. @@ -152,11 +153,11 @@ func NewBridgeFilterer(address common.Address, filterer bind.ContractFilterer) ( // bindBridge binds a generic wrapper to an already deployed contract. func bindBridge(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(BridgeABI)) + parsed, err := BridgeMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and @@ -757,6 +758,27 @@ func (_Bridge *BridgeTransactorSession) RenounceRelayer(account common.Address) return _Bridge.Contract.RenounceRelayer(&_Bridge.TransactOpts, account) } +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeTransactor) SetBatchSettleLimit(opts *bind.TransactOpts, newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.contract.Transact(opts, "setBatchSettleLimit", newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.Contract.SetBatchSettleLimit(&_Bridge.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_Bridge *BridgeTransactorSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _Bridge.Contract.SetBatchSettleLimit(&_Bridge.TransactOpts, newBatchSettleLimit) +} + // SetQuorum is a paid mutator transaction binding the contract method 0xc1ba4e59. // // Solidity: function setQuorum(uint256 newQuorum) returns() diff --git a/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.abi.json b/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.abi.json new file mode 100644 index 00000000..81126a5e --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.abi.json @@ -0,0 +1,763 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminRoleTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousBridge", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newBridge", + "type": "address" + } + ], + "name": "BridgeTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "depositNonce", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "batchId", + "type": "uint112" + } + ], + "name": "ERC20Deposit", + "type": "event" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchBlockLimit", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "batchDeposits", + "outputs": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSettleLimit", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSize", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "batches", + "outputs": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchesCount", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridge", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "recipientAddress", + "type": "bytes32" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositsCount", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatch", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "internalType": "struct Batch", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "internalType": "struct Deposit[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenMaxLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenMinLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "initSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isAnyBatchInProgress", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "isTokenWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "recoverLostFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "removeTokenFromWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchBlockLimit", + "type": "uint8" + } + ], + "name": "setBatchBlockLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchSettleLimit", + "type": "uint8" + } + ], + "name": "setBatchSettleLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "newBatchSize", + "type": "uint16" + } + ], + "name": "setBatchSize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newBridge", + "type": "address" + } + ], + "name": "setBridge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "setTokenMaxLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "setTokenMinLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenBalances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMaxLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMinLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipientAddress", + "type": "address" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minimumAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximumAmount", + "type": "uint256" + } + ], + "name": "whitelistToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "whitelistedTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.go b/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.go new file mode 100644 index 00000000..e397ab67 --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/contract/ERC20Safe.go @@ -0,0 +1,1636 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// ERC20SafeMetaData contains all meta data concerning the ERC20Safe contract. +var ERC20SafeMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"AddressInsufficientBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedInnerCall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminRoleTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousBridge\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"BridgeTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"depositNonce\",\"type\":\"uint112\"},{\"indexed\":false,\"internalType\":\"uint112\",\"name\":\"batchId\",\"type\":\"uint112\"}],\"name\":\"ERC20Deposit\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"admin\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchBlockLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batchDeposits\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSettleLimit\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchSize\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"batches\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchesCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"bridge\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"recipientAddress\",\"type\":\"bytes32\"}],\"name\":\"deposit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"depositsCount\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getBatch\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"uint64\",\"name\":\"blockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastUpdatedBlockNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint16\",\"name\":\"depositsCount\",\"type\":\"uint16\"}],\"internalType\":\"structBatch\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"batchNonce\",\"type\":\"uint256\"}],\"name\":\"getDeposits\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"nonce\",\"type\":\"uint112\"},{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"recipient\",\"type\":\"bytes32\"},{\"internalType\":\"enumDepositStatus\",\"name\":\"status\",\"type\":\"uint8\"}],\"internalType\":\"structDeposit[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMaxLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"getTokenMinLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"initSupply\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"isAnyBatchInProgress\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"isTokenWhitelisted\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"}],\"name\":\"recoverLostFunds\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"removeTokenFromWhitelist\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchBlockLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchBlockLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"newBatchSettleLimit\",\"type\":\"uint8\"}],\"name\":\"setBatchSettleLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"newBatchSize\",\"type\":\"uint16\"}],\"name\":\"setBatchSize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newBridge\",\"type\":\"address\"}],\"name\":\"setBridge\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMaxLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"setTokenMinLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenBalances\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMaxLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"tokenMinLimits\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"tokenAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"recipientAddress\",\"type\":\"address\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"transferAdmin\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minimumAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maximumAmount\",\"type\":\"uint256\"}],\"name\":\"whitelistToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"whitelistedTokens\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// ERC20SafeABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC20SafeMetaData.ABI instead. +var ERC20SafeABI = ERC20SafeMetaData.ABI + +// ERC20Safe is an auto generated Go binding around an Ethereum contract. +type ERC20Safe struct { + ERC20SafeCaller // Read-only binding to the contract + ERC20SafeTransactor // Write-only binding to the contract + ERC20SafeFilterer // Log filterer for contract events +} + +// ERC20SafeCaller is an auto generated read-only Go binding around an Ethereum contract. +type ERC20SafeCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC20SafeTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC20SafeFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC20SafeSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC20SafeSession struct { + Contract *ERC20Safe // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20SafeCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC20SafeCallerSession struct { + Contract *ERC20SafeCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC20SafeTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC20SafeTransactorSession struct { + Contract *ERC20SafeTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC20SafeRaw is an auto generated low-level Go binding around an Ethereum contract. +type ERC20SafeRaw struct { + Contract *ERC20Safe // Generic contract binding to access the raw methods on +} + +// ERC20SafeCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC20SafeCallerRaw struct { + Contract *ERC20SafeCaller // Generic read-only contract binding to access the raw methods on +} + +// ERC20SafeTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC20SafeTransactorRaw struct { + Contract *ERC20SafeTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC20Safe creates a new instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20Safe(address common.Address, backend bind.ContractBackend) (*ERC20Safe, error) { + contract, err := bindERC20Safe(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC20Safe{ERC20SafeCaller: ERC20SafeCaller{contract: contract}, ERC20SafeTransactor: ERC20SafeTransactor{contract: contract}, ERC20SafeFilterer: ERC20SafeFilterer{contract: contract}}, nil +} + +// NewERC20SafeCaller creates a new read-only instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeCaller(address common.Address, caller bind.ContractCaller) (*ERC20SafeCaller, error) { + contract, err := bindERC20Safe(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC20SafeCaller{contract: contract}, nil +} + +// NewERC20SafeTransactor creates a new write-only instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeTransactor(address common.Address, transactor bind.ContractTransactor) (*ERC20SafeTransactor, error) { + contract, err := bindERC20Safe(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC20SafeTransactor{contract: contract}, nil +} + +// NewERC20SafeFilterer creates a new log filterer instance of ERC20Safe, bound to a specific deployed contract. +func NewERC20SafeFilterer(address common.Address, filterer bind.ContractFilterer) (*ERC20SafeFilterer, error) { + contract, err := bindERC20Safe(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC20SafeFilterer{contract: contract}, nil +} + +// bindERC20Safe binds a generic wrapper to an already deployed contract. +func bindERC20Safe(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC20SafeMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC20Safe *ERC20SafeRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20Safe.Contract.ERC20SafeCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC20Safe *ERC20SafeRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.Contract.ERC20SafeTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20Safe *ERC20SafeRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20Safe.Contract.ERC20SafeTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC20Safe *ERC20SafeCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC20Safe.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC20Safe *ERC20SafeTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC20Safe *ERC20SafeTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC20Safe.Contract.contract.Transact(opts, method, params...) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_ERC20Safe *ERC20SafeCaller) Admin(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "admin") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_ERC20Safe *ERC20SafeSession) Admin() (common.Address, error) { + return _ERC20Safe.Contract.Admin(&_ERC20Safe.CallOpts) +} + +// Admin is a free data retrieval call binding the contract method 0xf851a440. +// +// Solidity: function admin() view returns(address) +func (_ERC20Safe *ERC20SafeCallerSession) Admin() (common.Address, error) { + return _ERC20Safe.Contract.Admin(&_ERC20Safe.CallOpts) +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCaller) BatchBlockLimit(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchBlockLimit") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeSession) BatchBlockLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchBlockLimit(&_ERC20Safe.CallOpts) +} + +// BatchBlockLimit is a free data retrieval call binding the contract method 0x9ab7cfaa. +// +// Solidity: function batchBlockLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCallerSession) BatchBlockLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchBlockLimit(&_ERC20Safe.CallOpts) +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeCaller) BatchDeposits(opts *bind.CallOpts, arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchDeposits", arg0, arg1) + + outstruct := new(struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 + }) + if err != nil { + return *outstruct, err + } + + outstruct.Nonce = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.TokenAddress = *abi.ConvertType(out[1], new(common.Address)).(*common.Address) + outstruct.Amount = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + outstruct.Depositor = *abi.ConvertType(out[3], new(common.Address)).(*common.Address) + outstruct.Recipient = *abi.ConvertType(out[4], new([32]byte)).(*[32]byte) + outstruct.Status = *abi.ConvertType(out[5], new(uint8)).(*uint8) + + return *outstruct, err + +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeSession) BatchDeposits(arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + return _ERC20Safe.Contract.BatchDeposits(&_ERC20Safe.CallOpts, arg0, arg1) +} + +// BatchDeposits is a free data retrieval call binding the contract method 0x284c0c44. +// +// Solidity: function batchDeposits(uint256 , uint256 ) view returns(uint112 nonce, address tokenAddress, uint256 amount, address depositor, bytes32 recipient, uint8 status) +func (_ERC20Safe *ERC20SafeCallerSession) BatchDeposits(arg0 *big.Int, arg1 *big.Int) (struct { + Nonce *big.Int + TokenAddress common.Address + Amount *big.Int + Depositor common.Address + Recipient [32]byte + Status uint8 +}, error) { + return _ERC20Safe.Contract.BatchDeposits(&_ERC20Safe.CallOpts, arg0, arg1) +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCaller) BatchSettleLimit(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchSettleLimit") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeSession) BatchSettleLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchSettleLimit(&_ERC20Safe.CallOpts) +} + +// BatchSettleLimit is a free data retrieval call binding the contract method 0x2325b5f7. +// +// Solidity: function batchSettleLimit() view returns(uint8) +func (_ERC20Safe *ERC20SafeCallerSession) BatchSettleLimit() (uint8, error) { + return _ERC20Safe.Contract.BatchSettleLimit(&_ERC20Safe.CallOpts) +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeCaller) BatchSize(opts *bind.CallOpts) (uint16, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchSize") + + if err != nil { + return *new(uint16), err + } + + out0 := *abi.ConvertType(out[0], new(uint16)).(*uint16) + + return out0, err + +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeSession) BatchSize() (uint16, error) { + return _ERC20Safe.Contract.BatchSize(&_ERC20Safe.CallOpts) +} + +// BatchSize is a free data retrieval call binding the contract method 0xf4daaba1. +// +// Solidity: function batchSize() view returns(uint16) +func (_ERC20Safe *ERC20SafeCallerSession) BatchSize() (uint16, error) { + return _ERC20Safe.Contract.BatchSize(&_ERC20Safe.CallOpts) +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeCaller) Batches(opts *bind.CallOpts, arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batches", arg0) + + outstruct := new(struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 + }) + if err != nil { + return *outstruct, err + } + + outstruct.Nonce = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.BlockNumber = *abi.ConvertType(out[1], new(uint64)).(*uint64) + outstruct.LastUpdatedBlockNumber = *abi.ConvertType(out[2], new(uint64)).(*uint64) + outstruct.DepositsCount = *abi.ConvertType(out[3], new(uint16)).(*uint16) + + return *outstruct, err + +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeSession) Batches(arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + return _ERC20Safe.Contract.Batches(&_ERC20Safe.CallOpts, arg0) +} + +// Batches is a free data retrieval call binding the contract method 0xb32c4d8d. +// +// Solidity: function batches(uint256 ) view returns(uint112 nonce, uint64 blockNumber, uint64 lastUpdatedBlockNumber, uint16 depositsCount) +func (_ERC20Safe *ERC20SafeCallerSession) Batches(arg0 *big.Int) (struct { + Nonce *big.Int + BlockNumber uint64 + LastUpdatedBlockNumber uint64 + DepositsCount uint16 +}, error) { + return _ERC20Safe.Contract.Batches(&_ERC20Safe.CallOpts, arg0) +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCaller) BatchesCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "batchesCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeSession) BatchesCount() (uint64, error) { + return _ERC20Safe.Contract.BatchesCount(&_ERC20Safe.CallOpts) +} + +// BatchesCount is a free data retrieval call binding the contract method 0x87ea0961. +// +// Solidity: function batchesCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCallerSession) BatchesCount() (uint64, error) { + return _ERC20Safe.Contract.BatchesCount(&_ERC20Safe.CallOpts) +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeCaller) Bridge(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "bridge") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeSession) Bridge() (common.Address, error) { + return _ERC20Safe.Contract.Bridge(&_ERC20Safe.CallOpts) +} + +// Bridge is a free data retrieval call binding the contract method 0xe78cea92. +// +// Solidity: function bridge() view returns(address) +func (_ERC20Safe *ERC20SafeCallerSession) Bridge() (common.Address, error) { + return _ERC20Safe.Contract.Bridge(&_ERC20Safe.CallOpts) +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCaller) DepositsCount(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "depositsCount") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeSession) DepositsCount() (uint64, error) { + return _ERC20Safe.Contract.DepositsCount(&_ERC20Safe.CallOpts) +} + +// DepositsCount is a free data retrieval call binding the contract method 0x4506e935. +// +// Solidity: function depositsCount() view returns(uint64) +func (_ERC20Safe *ERC20SafeCallerSession) DepositsCount() (uint64, error) { + return _ERC20Safe.Contract.DepositsCount(&_ERC20Safe.CallOpts) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_ERC20Safe *ERC20SafeCaller) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (Batch, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getBatch", batchNonce) + + if err != nil { + return *new(Batch), err + } + + out0 := *abi.ConvertType(out[0], new(Batch)).(*Batch) + + return out0, err + +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_ERC20Safe *ERC20SafeSession) GetBatch(batchNonce *big.Int) (Batch, error) { + return _ERC20Safe.Contract.GetBatch(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetBatch is a free data retrieval call binding the contract method 0x5ac44282. +// +// Solidity: function getBatch(uint256 batchNonce) view returns((uint112,uint64,uint64,uint16)) +func (_ERC20Safe *ERC20SafeCallerSession) GetBatch(batchNonce *big.Int) (Batch, error) { + return _ERC20Safe.Contract.GetBatch(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_ERC20Safe *ERC20SafeCaller) GetDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]Deposit, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getDeposits", batchNonce) + + if err != nil { + return *new([]Deposit), err + } + + out0 := *abi.ConvertType(out[0], new([]Deposit)).(*[]Deposit) + + return out0, err + +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_ERC20Safe *ERC20SafeSession) GetDeposits(batchNonce *big.Int) ([]Deposit, error) { + return _ERC20Safe.Contract.GetDeposits(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetDeposits is a free data retrieval call binding the contract method 0x085c967f. +// +// Solidity: function getDeposits(uint256 batchNonce) view returns((uint112,address,uint256,address,bytes32,uint8)[]) +func (_ERC20Safe *ERC20SafeCallerSession) GetDeposits(batchNonce *big.Int) ([]Deposit, error) { + return _ERC20Safe.Contract.GetDeposits(&_ERC20Safe.CallOpts, batchNonce) +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) GetTokenMaxLimit(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getTokenMaxLimit", token) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) GetTokenMaxLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMaxLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMaxLimit is a free data retrieval call binding the contract method 0xc652a0b5. +// +// Solidity: function getTokenMaxLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) GetTokenMaxLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMaxLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) GetTokenMinLimit(opts *bind.CallOpts, token common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "getTokenMinLimit", token) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) GetTokenMinLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMinLimit(&_ERC20Safe.CallOpts, token) +} + +// GetTokenMinLimit is a free data retrieval call binding the contract method 0x9f0ebb93. +// +// Solidity: function getTokenMinLimit(address token) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) GetTokenMinLimit(token common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.GetTokenMinLimit(&_ERC20Safe.CallOpts, token) +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) IsAnyBatchInProgress(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "isAnyBatchInProgress") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeSession) IsAnyBatchInProgress() (bool, error) { + return _ERC20Safe.Contract.IsAnyBatchInProgress(&_ERC20Safe.CallOpts) +} + +// IsAnyBatchInProgress is a free data retrieval call binding the contract method 0x82146138. +// +// Solidity: function isAnyBatchInProgress() view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) IsAnyBatchInProgress() (bool, error) { + return _ERC20Safe.Contract.IsAnyBatchInProgress(&_ERC20Safe.CallOpts) +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) IsTokenWhitelisted(opts *bind.CallOpts, token common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "isTokenWhitelisted", token) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) IsTokenWhitelisted(token common.Address) (bool, error) { + return _ERC20Safe.Contract.IsTokenWhitelisted(&_ERC20Safe.CallOpts, token) +} + +// IsTokenWhitelisted is a free data retrieval call binding the contract method 0xb5af090f. +// +// Solidity: function isTokenWhitelisted(address token) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) IsTokenWhitelisted(token common.Address) (bool, error) { + return _ERC20Safe.Contract.IsTokenWhitelisted(&_ERC20Safe.CallOpts, token) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) Paused(opts *bind.CallOpts) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "paused") + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeSession) Paused() (bool, error) { + return _ERC20Safe.Contract.Paused(&_ERC20Safe.CallOpts) +} + +// Paused is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) Paused() (bool, error) { + return _ERC20Safe.Contract.Paused(&_ERC20Safe.CallOpts) +} + +// TokenBalances is a free data retrieval call binding the contract method 0x523fba7f. +// +// Solidity: function tokenBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenBalances", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenBalances is a free data retrieval call binding the contract method 0x523fba7f. +// +// Solidity: function tokenBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenBalances(&_ERC20Safe.CallOpts, arg0) +} + +// TokenBalances is a free data retrieval call binding the contract method 0x523fba7f. +// +// Solidity: function tokenBalances(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenBalances(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenBalances(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenMaxLimits(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenMaxLimits", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenMaxLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMaxLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMaxLimits is a free data retrieval call binding the contract method 0xc639651d. +// +// Solidity: function tokenMaxLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenMaxLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMaxLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCaller) TokenMinLimits(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "tokenMinLimits", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeSession) TokenMinLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMinLimits(&_ERC20Safe.CallOpts, arg0) +} + +// TokenMinLimits is a free data retrieval call binding the contract method 0xf6246ea1. +// +// Solidity: function tokenMinLimits(address ) view returns(uint256) +func (_ERC20Safe *ERC20SafeCallerSession) TokenMinLimits(arg0 common.Address) (*big.Int, error) { + return _ERC20Safe.Contract.TokenMinLimits(&_ERC20Safe.CallOpts, arg0) +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCaller) WhitelistedTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + var out []interface{} + err := _ERC20Safe.contract.Call(opts, &out, "whitelistedTokens", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeSession) WhitelistedTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.WhitelistedTokens(&_ERC20Safe.CallOpts, arg0) +} + +// WhitelistedTokens is a free data retrieval call binding the contract method 0xdaf9c210. +// +// Solidity: function whitelistedTokens(address ) view returns(bool) +func (_ERC20Safe *ERC20SafeCallerSession) WhitelistedTokens(arg0 common.Address) (bool, error) { + return _ERC20Safe.Contract.WhitelistedTokens(&_ERC20Safe.CallOpts, arg0) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeTransactor) Deposit(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "deposit", tokenAddress, amount, recipientAddress) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeSession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.Deposit(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// Deposit is a paid mutator transaction binding the contract method 0x26b3293f. +// +// Solidity: function deposit(address tokenAddress, uint256 amount, bytes32 recipientAddress) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Deposit(tokenAddress common.Address, amount *big.Int, recipientAddress [32]byte) (*types.Transaction, error) { + return _ERC20Safe.Contract.Deposit(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) InitSupply(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "initSupply", tokenAddress, amount) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) InitSupply(tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupply(&_ERC20Safe.TransactOpts, tokenAddress, amount) +} + +// InitSupply is a paid mutator transaction binding the contract method 0x4013c89c. +// +// Solidity: function initSupply(address tokenAddress, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) InitSupply(tokenAddress common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.InitSupply(&_ERC20Safe.TransactOpts, tokenAddress, amount) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeTransactor) Pause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "pause") +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeSession) Pause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Pause(&_ERC20Safe.TransactOpts) +} + +// Pause is a paid mutator transaction binding the contract method 0x8456cb59. +// +// Solidity: function pause() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Pause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Pause(&_ERC20Safe.TransactOpts) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactor) RecoverLostFunds(opts *bind.TransactOpts, tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "recoverLostFunds", tokenAddress) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeSession) RecoverLostFunds(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RecoverLostFunds(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// RecoverLostFunds is a paid mutator transaction binding the contract method 0x770be784. +// +// Solidity: function recoverLostFunds(address tokenAddress) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RecoverLostFunds(tokenAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RecoverLostFunds(&_ERC20Safe.TransactOpts, tokenAddress) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeTransactor) RemoveTokenFromWhitelist(opts *bind.TransactOpts, token common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "removeTokenFromWhitelist", token) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeSession) RemoveTokenFromWhitelist(token common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RemoveTokenFromWhitelist(&_ERC20Safe.TransactOpts, token) +} + +// RemoveTokenFromWhitelist is a paid mutator transaction binding the contract method 0x306275be. +// +// Solidity: function removeTokenFromWhitelist(address token) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RemoveTokenFromWhitelist(token common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.RemoveTokenFromWhitelist(&_ERC20Safe.TransactOpts, token) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeTransactor) RenounceAdmin(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "renounceAdmin") +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeSession) RenounceAdmin() (*types.Transaction, error) { + return _ERC20Safe.Contract.RenounceAdmin(&_ERC20Safe.TransactOpts) +} + +// RenounceAdmin is a paid mutator transaction binding the contract method 0x8bad0c0a. +// +// Solidity: function renounceAdmin() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) RenounceAdmin() (*types.Transaction, error) { + return _ERC20Safe.Contract.RenounceAdmin(&_ERC20Safe.TransactOpts) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchBlockLimit(opts *bind.TransactOpts, newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchBlockLimit", newBatchBlockLimit) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchBlockLimit(newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchBlockLimit(&_ERC20Safe.TransactOpts, newBatchBlockLimit) +} + +// SetBatchBlockLimit is a paid mutator transaction binding the contract method 0xe8a70ee2. +// +// Solidity: function setBatchBlockLimit(uint8 newBatchBlockLimit) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchBlockLimit(newBatchBlockLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchBlockLimit(&_ERC20Safe.TransactOpts, newBatchBlockLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchSettleLimit(opts *bind.TransactOpts, newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchSettleLimit", newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSettleLimit(&_ERC20Safe.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSettleLimit is a paid mutator transaction binding the contract method 0xf2e0ec48. +// +// Solidity: function setBatchSettleLimit(uint8 newBatchSettleLimit) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchSettleLimit(newBatchSettleLimit uint8) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSettleLimit(&_ERC20Safe.TransactOpts, newBatchSettleLimit) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBatchSize(opts *bind.TransactOpts, newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBatchSize", newBatchSize) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeSession) SetBatchSize(newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSize(&_ERC20Safe.TransactOpts, newBatchSize) +} + +// SetBatchSize is a paid mutator transaction binding the contract method 0xd4673de9. +// +// Solidity: function setBatchSize(uint16 newBatchSize) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBatchSize(newBatchSize uint16) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBatchSize(&_ERC20Safe.TransactOpts, newBatchSize) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetBridge(opts *bind.TransactOpts, newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setBridge", newBridge) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeSession) SetBridge(newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBridge(&_ERC20Safe.TransactOpts, newBridge) +} + +// SetBridge is a paid mutator transaction binding the contract method 0x8dd14802. +// +// Solidity: function setBridge(address newBridge) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetBridge(newBridge common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetBridge(&_ERC20Safe.TransactOpts, newBridge) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetTokenMaxLimit(opts *bind.TransactOpts, token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setTokenMaxLimit", token, amount) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) SetTokenMaxLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMaxLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMaxLimit is a paid mutator transaction binding the contract method 0x7d7763ce. +// +// Solidity: function setTokenMaxLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetTokenMaxLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMaxLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactor) SetTokenMinLimit(opts *bind.TransactOpts, token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "setTokenMinLimit", token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeSession) SetTokenMinLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMinLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// SetTokenMinLimit is a paid mutator transaction binding the contract method 0x920b0308. +// +// Solidity: function setTokenMinLimit(address token, uint256 amount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) SetTokenMinLimit(token common.Address, amount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.SetTokenMinLimit(&_ERC20Safe.TransactOpts, token, amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeTransactor) Transfer(opts *bind.TransactOpts, tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "transfer", tokenAddress, amount, recipientAddress) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeSession) Transfer(tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.Transfer(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// Transfer is a paid mutator transaction binding the contract method 0xdbba0f01. +// +// Solidity: function transfer(address tokenAddress, uint256 amount, address recipientAddress) returns(bool) +func (_ERC20Safe *ERC20SafeTransactorSession) Transfer(tokenAddress common.Address, amount *big.Int, recipientAddress common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.Transfer(&_ERC20Safe.TransactOpts, tokenAddress, amount, recipientAddress) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeTransactor) TransferAdmin(opts *bind.TransactOpts, newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "transferAdmin", newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.TransferAdmin(&_ERC20Safe.TransactOpts, newAdmin) +} + +// TransferAdmin is a paid mutator transaction binding the contract method 0x75829def. +// +// Solidity: function transferAdmin(address newAdmin) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) TransferAdmin(newAdmin common.Address) (*types.Transaction, error) { + return _ERC20Safe.Contract.TransferAdmin(&_ERC20Safe.TransactOpts, newAdmin) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeTransactor) Unpause(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "unpause") +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeSession) Unpause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Unpause(&_ERC20Safe.TransactOpts) +} + +// Unpause is a paid mutator transaction binding the contract method 0x3f4ba83a. +// +// Solidity: function unpause() returns() +func (_ERC20Safe *ERC20SafeTransactorSession) Unpause() (*types.Transaction, error) { + return _ERC20Safe.Contract.Unpause(&_ERC20Safe.TransactOpts) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x1ce330ec. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount) returns() +func (_ERC20Safe *ERC20SafeTransactor) WhitelistToken(opts *bind.TransactOpts, token common.Address, minimumAmount *big.Int, maximumAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.contract.Transact(opts, "whitelistToken", token, minimumAmount, maximumAmount) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x1ce330ec. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount) returns() +func (_ERC20Safe *ERC20SafeSession) WhitelistToken(token common.Address, minimumAmount *big.Int, maximumAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.WhitelistToken(&_ERC20Safe.TransactOpts, token, minimumAmount, maximumAmount) +} + +// WhitelistToken is a paid mutator transaction binding the contract method 0x1ce330ec. +// +// Solidity: function whitelistToken(address token, uint256 minimumAmount, uint256 maximumAmount) returns() +func (_ERC20Safe *ERC20SafeTransactorSession) WhitelistToken(token common.Address, minimumAmount *big.Int, maximumAmount *big.Int) (*types.Transaction, error) { + return _ERC20Safe.Contract.WhitelistToken(&_ERC20Safe.TransactOpts, token, minimumAmount, maximumAmount) +} + +// ERC20SafeAdminRoleTransferredIterator is returned from FilterAdminRoleTransferred and is used to iterate over the raw logs and unpacked data for AdminRoleTransferred events raised by the ERC20Safe contract. +type ERC20SafeAdminRoleTransferredIterator struct { + Event *ERC20SafeAdminRoleTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeAdminRoleTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeAdminRoleTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeAdminRoleTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeAdminRoleTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeAdminRoleTransferred represents a AdminRoleTransferred event raised by the ERC20Safe contract. +type ERC20SafeAdminRoleTransferred struct { + PreviousAdmin common.Address + NewAdmin common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterAdminRoleTransferred is a free log retrieval operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_ERC20Safe *ERC20SafeFilterer) FilterAdminRoleTransferred(opts *bind.FilterOpts, previousAdmin []common.Address, newAdmin []common.Address) (*ERC20SafeAdminRoleTransferredIterator, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return &ERC20SafeAdminRoleTransferredIterator{contract: _ERC20Safe.contract, event: "AdminRoleTransferred", logs: logs, sub: sub}, nil +} + +// WatchAdminRoleTransferred is a free log subscription operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_ERC20Safe *ERC20SafeFilterer) WatchAdminRoleTransferred(opts *bind.WatchOpts, sink chan<- *ERC20SafeAdminRoleTransferred, previousAdmin []common.Address, newAdmin []common.Address) (event.Subscription, error) { + + var previousAdminRule []interface{} + for _, previousAdminItem := range previousAdmin { + previousAdminRule = append(previousAdminRule, previousAdminItem) + } + var newAdminRule []interface{} + for _, newAdminItem := range newAdmin { + newAdminRule = append(newAdminRule, newAdminItem) + } + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "AdminRoleTransferred", previousAdminRule, newAdminRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeAdminRoleTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseAdminRoleTransferred is a log parse operation binding the contract event 0xe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd. +// +// Solidity: event AdminRoleTransferred(address indexed previousAdmin, address indexed newAdmin) +func (_ERC20Safe *ERC20SafeFilterer) ParseAdminRoleTransferred(log types.Log) (*ERC20SafeAdminRoleTransferred, error) { + event := new(ERC20SafeAdminRoleTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "AdminRoleTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeBridgeTransferredIterator is returned from FilterBridgeTransferred and is used to iterate over the raw logs and unpacked data for BridgeTransferred events raised by the ERC20Safe contract. +type ERC20SafeBridgeTransferredIterator struct { + Event *ERC20SafeBridgeTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeBridgeTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeBridgeTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeBridgeTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeBridgeTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeBridgeTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeBridgeTransferred represents a BridgeTransferred event raised by the ERC20Safe contract. +type ERC20SafeBridgeTransferred struct { + PreviousBridge common.Address + NewBridge common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBridgeTransferred is a free log retrieval operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) FilterBridgeTransferred(opts *bind.FilterOpts, previousBridge []common.Address, newBridge []common.Address) (*ERC20SafeBridgeTransferredIterator, error) { + + var previousBridgeRule []interface{} + for _, previousBridgeItem := range previousBridge { + previousBridgeRule = append(previousBridgeRule, previousBridgeItem) + } + var newBridgeRule []interface{} + for _, newBridgeItem := range newBridge { + newBridgeRule = append(newBridgeRule, newBridgeItem) + } + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "BridgeTransferred", previousBridgeRule, newBridgeRule) + if err != nil { + return nil, err + } + return &ERC20SafeBridgeTransferredIterator{contract: _ERC20Safe.contract, event: "BridgeTransferred", logs: logs, sub: sub}, nil +} + +// WatchBridgeTransferred is a free log subscription operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) WatchBridgeTransferred(opts *bind.WatchOpts, sink chan<- *ERC20SafeBridgeTransferred, previousBridge []common.Address, newBridge []common.Address) (event.Subscription, error) { + + var previousBridgeRule []interface{} + for _, previousBridgeItem := range previousBridge { + previousBridgeRule = append(previousBridgeRule, previousBridgeItem) + } + var newBridgeRule []interface{} + for _, newBridgeItem := range newBridge { + newBridgeRule = append(newBridgeRule, newBridgeItem) + } + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "BridgeTransferred", previousBridgeRule, newBridgeRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeBridgeTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "BridgeTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseBridgeTransferred is a log parse operation binding the contract event 0xcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a0. +// +// Solidity: event BridgeTransferred(address indexed previousBridge, address indexed newBridge) +func (_ERC20Safe *ERC20SafeFilterer) ParseBridgeTransferred(log types.Log) (*ERC20SafeBridgeTransferred, error) { + event := new(ERC20SafeBridgeTransferred) + if err := _ERC20Safe.contract.UnpackLog(event, "BridgeTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC20SafeERC20DepositIterator is returned from FilterERC20Deposit and is used to iterate over the raw logs and unpacked data for ERC20Deposit events raised by the ERC20Safe contract. +type ERC20SafeERC20DepositIterator struct { + Event *ERC20SafeERC20Deposit // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC20SafeERC20DepositIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC20SafeERC20Deposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC20SafeERC20Deposit) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC20SafeERC20DepositIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC20SafeERC20DepositIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC20SafeERC20Deposit represents a ERC20Deposit event raised by the ERC20Safe contract. +type ERC20SafeERC20Deposit struct { + DepositNonce *big.Int + BatchId *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterERC20Deposit is a free log retrieval operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 depositNonce, uint112 batchId) +func (_ERC20Safe *ERC20SafeFilterer) FilterERC20Deposit(opts *bind.FilterOpts) (*ERC20SafeERC20DepositIterator, error) { + + logs, sub, err := _ERC20Safe.contract.FilterLogs(opts, "ERC20Deposit") + if err != nil { + return nil, err + } + return &ERC20SafeERC20DepositIterator{contract: _ERC20Safe.contract, event: "ERC20Deposit", logs: logs, sub: sub}, nil +} + +// WatchERC20Deposit is a free log subscription operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 depositNonce, uint112 batchId) +func (_ERC20Safe *ERC20SafeFilterer) WatchERC20Deposit(opts *bind.WatchOpts, sink chan<- *ERC20SafeERC20Deposit) (event.Subscription, error) { + + logs, sub, err := _ERC20Safe.contract.WatchLogs(opts, "ERC20Deposit") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC20SafeERC20Deposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20Deposit", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseERC20Deposit is a log parse operation binding the contract event 0x6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a7. +// +// Solidity: event ERC20Deposit(uint112 depositNonce, uint112 batchId) +func (_ERC20Safe *ERC20SafeFilterer) ParseERC20Deposit(log types.Log) (*ERC20SafeERC20Deposit, error) { + event := new(ERC20SafeERC20Deposit) + if err := _ERC20Safe.contract.UnpackLog(event, "ERC20Deposit", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/executors/ethereum/bridgeV2Wrappers/errors.go b/executors/ethereum/bridgeV2Wrappers/errors.go new file mode 100644 index 00000000..578f626f --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/errors.go @@ -0,0 +1,8 @@ +package bridgeV2Wrappers + +import "errors" + +var ( + errNilBlockchainClient = errors.New("nil blockchain client") + errNilMultiSigContract = errors.New("nil multi sig contract") +) diff --git a/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper.go b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper.go new file mode 100644 index 00000000..2ac16fe7 --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper.go @@ -0,0 +1,139 @@ +package bridgeV2Wrappers + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" + "github.com/multiversx/mx-chain-core-go/core/check" +) + +// ArgsEthereumChainWrapper is the DTO used to construct a ethereumChainWrapper instance +type ArgsEthereumChainWrapper struct { + StatusHandler core.StatusHandler + MultiSigContract multiSigContract + BlockchainClient blockchainClient +} + +type ethereumChainWrapper struct { + core.StatusHandler + multiSigContract multiSigContract + blockchainClient blockchainClient +} + +// NewEthereumChainWrapper creates a new instance of type ethereumChainWrapper +func NewEthereumChainWrapper(args ArgsEthereumChainWrapper) (*ethereumChainWrapper, error) { + err := checkArgs(args) + if err != nil { + return nil, err + } + + return ðereumChainWrapper{ + StatusHandler: args.StatusHandler, + multiSigContract: args.MultiSigContract, + blockchainClient: args.BlockchainClient, + }, nil +} + +func checkArgs(args ArgsEthereumChainWrapper) error { + if check.IfNil(args.StatusHandler) { + return clients.ErrNilStatusHandler + } + if check.IfNilReflect(args.MultiSigContract) { + return errNilMultiSigContract + } + if check.IfNilReflect(args.BlockchainClient) { + return errNilBlockchainClient + } + + return nil +} + +// GetBatch returns the batch of transactions by providing the batch nonce +func (wrapper *ethereumChainWrapper) GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetBatch(&bind.CallOpts{Context: ctx}, batchNonce) +} + +// GetBatchDeposits returns the transactions of a batch by providing the batch nonce +func (wrapper *ethereumChainWrapper) GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetBatchDeposits(&bind.CallOpts{Context: ctx}, batchNonce) +} + +// GetRelayers returns all whitelisted ethereum addresses +func (wrapper *ethereumChainWrapper) GetRelayers(ctx context.Context) ([]common.Address, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetRelayers(&bind.CallOpts{Context: ctx}) +} + +// WasBatchExecuted returns true if the batch was executed +func (wrapper *ethereumChainWrapper) WasBatchExecuted(ctx context.Context, batchNonce *big.Int) (bool, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.WasBatchExecuted(&bind.CallOpts{Context: ctx}, batchNonce) +} + +// ChainID returns the chain ID +func (wrapper *ethereumChainWrapper) ChainID(ctx context.Context) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.ChainID(ctx) +} + +// BlockNumber returns the current ethereum block number +func (wrapper *ethereumChainWrapper) BlockNumber(ctx context.Context) (uint64, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + val, err := wrapper.blockchainClient.BlockNumber(ctx) + if err != nil { + return 0, err + } + + wrapper.SetIntMetric(core.MetricLastQueriedEthereumBlockNumber, int(val)) + + return val, nil +} + +// NonceAt returns the account's nonce at the specified block number +func (wrapper *ethereumChainWrapper) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.NonceAt(ctx, account, blockNumber) +} + +// ExecuteTransfer will send an execute-transfer transaction on the ethereum chain +func (wrapper *ethereumChainWrapper) ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + wrapper.AddIntMetric(core.MetricNumEthClientTransactions, 1) + return wrapper.multiSigContract.ExecuteTransfer(opts, tokens, recipients, amounts, nonces, batchNonce, signatures) +} + +// Quorum returns the current set quorum value +func (wrapper *ethereumChainWrapper) Quorum(ctx context.Context) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.Quorum(&bind.CallOpts{Context: ctx}) +} + +// GetStatusesAfterExecution returns the statuses of the last executed transfer +func (wrapper *ethereumChainWrapper) GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.multiSigContract.GetStatusesAfterExecution(&bind.CallOpts{Context: ctx}, batchID) +} + +// BalanceAt returns the wei balance of the given account. +// The block number can be nil, in which case the balance is taken from the latest known block. +func (wrapper *ethereumChainWrapper) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + wrapper.AddIntMetric(core.MetricNumEthClientRequests, 1) + return wrapper.blockchainClient.BalanceAt(ctx, account, blockNumber) +} + +// IsPaused returns true if the multisig contract is paused +func (wrapper *ethereumChainWrapper) IsPaused(ctx context.Context) (bool, error) { + return wrapper.multiSigContract.Paused(&bind.CallOpts{Context: ctx}) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (wrapper *ethereumChainWrapper) IsInterfaceNil() bool { + return wrapper == nil +} diff --git a/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper_test.go b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper_test.go new file mode 100644 index 00000000..6540877b --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/ethereumChainWrapper_test.go @@ -0,0 +1,296 @@ +package bridgeV2Wrappers + +import ( + "context" + "errors" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/mock" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/interactors" + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/stretchr/testify/assert" +) + +func createMockArgsEthereumChainWrapper() (ArgsEthereumChainWrapper, *testsCommon.StatusHandlerMock) { + statusHandler := testsCommon.NewStatusHandlerMock("mock") + + return ArgsEthereumChainWrapper{ + MultiSigContract: &mock.MultiSigContractStub{}, + BlockchainClient: &interactors.BlockchainClientStub{}, + StatusHandler: statusHandler, + }, statusHandler +} + +func TestNewMultiSigContractWrapper(t *testing.T) { + t.Parallel() + + t.Run("nil status handler", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + args.StatusHandler = nil + + wrapper, err := NewEthereumChainWrapper(args) + assert.True(t, check.IfNil(wrapper)) + assert.Equal(t, clients.ErrNilStatusHandler, err) + }) + t.Run("nil blockchain client", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + args.BlockchainClient = nil + + wrapper, err := NewEthereumChainWrapper(args) + assert.True(t, check.IfNil(wrapper)) + assert.Equal(t, errNilBlockchainClient, err) + }) + t.Run("nil multisig contract", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + args.MultiSigContract = nil + + wrapper, err := NewEthereumChainWrapper(args) + assert.True(t, check.IfNil(wrapper)) + assert.Equal(t, errNilMultiSigContract, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + + wrapper, err := NewEthereumChainWrapper(args) + assert.False(t, check.IfNil(wrapper)) + assert.Nil(t, err) + }) +} + +func TestEthClientWrapper_GetBatch(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + providedBatchID := big.NewInt(223) + args.MultiSigContract = &mock.MultiSigContractStub{ + GetBatchCalled: func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) { + handlerCalled = true + assert.Equal(t, providedBatchID, batchNonce) + return contract.Batch{}, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + batch, err := wrapper.GetBatch(context.Background(), providedBatchID) + assert.Nil(t, err) + assert.Equal(t, contract.Batch{}, batch) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_GetRelayers(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + GetRelayersCalled: func(opts *bind.CallOpts) ([]common.Address, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + relayers, err := wrapper.GetRelayers(context.Background()) + assert.Nil(t, err) + assert.Nil(t, relayers) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_WasBatchExecuted(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + WasBatchExecutedCalled: func(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) { + handlerCalled = true + return false, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + executed, err := wrapper.WasBatchExecuted(context.Background(), nil) + assert.Nil(t, err) + assert.False(t, executed) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_ChainID(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.BlockchainClient = &interactors.BlockchainClientStub{ + ChainIDCalled: func(ctx context.Context) (*big.Int, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + chainID, err := wrapper.ChainID(context.Background()) + assert.Nil(t, err) + assert.Nil(t, chainID) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_BlockNumber(t *testing.T) { + t.Parallel() + + t.Run("block number call returns error", func(t *testing.T) { + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + expectedError := errors.New("expected error") + args.BlockchainClient = &interactors.BlockchainClientStub{ + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + handlerCalled = true + return 0, expectedError + }, + } + lastBlockNum := 3343 + statusHandler.SetIntMetric(core.MetricLastQueriedEthereumBlockNumber, lastBlockNum) + + wrapper, _ := NewEthereumChainWrapper(args) + blockNum, err := wrapper.BlockNumber(context.Background()) + assert.Equal(t, expectedError, err) + assert.Equal(t, uint64(0), blockNum) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) + assert.Equal(t, lastBlockNum, statusHandler.GetIntMetric(core.MetricLastQueriedEthereumBlockNumber)) + }) + t.Run("block number call returns a value", func(t *testing.T) { + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + newBlockNum := 772537 + args.BlockchainClient = &interactors.BlockchainClientStub{ + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + handlerCalled = true + return uint64(newBlockNum), nil + }, + } + lastBlockNum := 3343 + statusHandler.SetIntMetric(core.MetricLastQueriedEthereumBlockNumber, lastBlockNum) + + wrapper, _ := NewEthereumChainWrapper(args) + blockNum, err := wrapper.BlockNumber(context.Background()) + assert.Nil(t, err) + assert.Equal(t, uint64(newBlockNum), blockNum) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) + assert.Equal(t, newBlockNum, statusHandler.GetIntMetric(core.MetricLastQueriedEthereumBlockNumber)) + }) +} + +func TestEthClientWrapper_NonceAt(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.BlockchainClient = &interactors.BlockchainClientStub{ + NonceAtCalled: func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + handlerCalled = true + return 0, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + nonce, err := wrapper.NonceAt(context.Background(), common.Address{}, nil) + assert.Nil(t, err) + assert.Equal(t, uint64(0), nonce) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_ExecuteTransfer(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, + amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + tx, err := wrapper.ExecuteTransfer(nil, nil, nil, nil, nil, nil, nil) + assert.Nil(t, err) + assert.Nil(t, tx) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientTransactions)) +} + +func TestEthClientWrapper_Quorum(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + QuorumCalled: func(opts *bind.CallOpts) (*big.Int, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + tx, err := wrapper.Quorum(context.Background()) + assert.Nil(t, err) + assert.Nil(t, tx) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthClientWrapper_GetStatusesAfterExecution(t *testing.T) { + t.Parallel() + + args, statusHandler := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + GetStatusesAfterExecutionCalled: func(opts *bind.CallOpts, batchNonceMultiversXETH *big.Int) ([]uint8, error) { + handlerCalled = true + return nil, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + statuses, err := wrapper.GetStatusesAfterExecution(context.Background(), nil) + assert.Nil(t, err) + assert.Nil(t, statuses) + assert.True(t, handlerCalled) + assert.Equal(t, 1, statusHandler.GetIntMetric(core.MetricNumEthClientRequests)) +} + +func TestEthereumChainWrapper_IsPaused(t *testing.T) { + t.Parallel() + + args, _ := createMockArgsEthereumChainWrapper() + handlerCalled := false + args.MultiSigContract = &mock.MultiSigContractStub{ + PausedCalled: func(opts *bind.CallOpts) (bool, error) { + handlerCalled = true + return true, nil + }, + } + wrapper, _ := NewEthereumChainWrapper(args) + result, err := wrapper.IsPaused(context.Background()) + + assert.Nil(t, err) + assert.True(t, result) + assert.True(t, handlerCalled) +} diff --git a/executors/ethereum/bridgeV2Wrappers/interface.go b/executors/ethereum/bridgeV2Wrappers/interface.go new file mode 100644 index 00000000..2d4644c2 --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/interface.go @@ -0,0 +1,29 @@ +package bridgeV2Wrappers + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" +) + +type multiSigContract interface { + GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) + GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) + GetRelayers(opts *bind.CallOpts) ([]common.Address, error) + WasBatchExecuted(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) + ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, depositNonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) + Quorum(opts *bind.CallOpts) (*big.Int, error) + GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) + Paused(opts *bind.CallOpts) (bool, error) +} + +type blockchainClient interface { + BlockNumber(ctx context.Context) (uint64, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) + ChainID(ctx context.Context) (*big.Int, error) + BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) +} diff --git a/executors/ethereum/bridgeV2Wrappers/mock/errors.go b/executors/ethereum/bridgeV2Wrappers/mock/errors.go new file mode 100644 index 00000000..9386b84f --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/mock/errors.go @@ -0,0 +1,7 @@ +package mock + +import "errors" + +var ( + errNotImplemented = errors.New("method not implemented") +) diff --git a/executors/ethereum/bridgeV2Wrappers/mock/multiSigContractStub.go b/executors/ethereum/bridgeV2Wrappers/mock/multiSigContractStub.go new file mode 100644 index 00000000..0750d06d --- /dev/null +++ b/executors/ethereum/bridgeV2Wrappers/mock/multiSigContractStub.go @@ -0,0 +1,103 @@ +package mock + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/executors/ethereum/bridgeV2Wrappers/contract" +) + +// MultiSigContractStub - +type MultiSigContractStub struct { + GetBatchCalled func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) + GetBatchDepositsCalled func(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) + GetRelayersCalled func(opts *bind.CallOpts) ([]common.Address, error) + WasBatchExecutedCalled func(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) + ExecuteTransferCalled func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, + amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) + QuorumCalled func(opts *bind.CallOpts) (*big.Int, error) + GetStatusesAfterExecutionCalled func(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) + PausedCalled func(opts *bind.CallOpts) (bool, error) +} + +// GetBatch - +func (stub *MultiSigContractStub) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) { + if stub.GetBatchCalled != nil { + return stub.GetBatchCalled(opts, batchNonce) + } + + return contract.Batch{}, nil +} + +// GetBatchDeposits - +func (stub *MultiSigContractStub) GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) { + if stub.GetBatchCalled != nil { + return stub.GetBatchDepositsCalled(opts, batchNonce) + } + + return make([]contract.Deposit, 0), nil +} + +// GetRelayers - +func (stub *MultiSigContractStub) GetRelayers(opts *bind.CallOpts) ([]common.Address, error) { + if stub.GetRelayersCalled != nil { + return stub.GetRelayersCalled(opts) + } + + return make([]common.Address, 0), nil +} + +// WasBatchExecuted - +func (stub *MultiSigContractStub) WasBatchExecuted(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) { + if stub.WasBatchExecutedCalled != nil { + return stub.WasBatchExecutedCalled(opts, batchNonce) + } + + return false, nil +} + +// ExecuteTransfer - +func (stub *MultiSigContractStub) ExecuteTransfer( + opts *bind.TransactOpts, + tokens []common.Address, + recipients []common.Address, + amounts []*big.Int, + nonces []*big.Int, + batchNonce *big.Int, + signatures [][]byte, +) (*types.Transaction, error) { + if stub.ExecuteTransferCalled != nil { + return stub.ExecuteTransferCalled(opts, tokens, recipients, amounts, nonces, batchNonce, signatures) + } + + return nil, errNotImplemented +} + +// Quorum - +func (stub *MultiSigContractStub) Quorum(opts *bind.CallOpts) (*big.Int, error) { + if stub.QuorumCalled != nil { + return stub.QuorumCalled(opts) + } + + return big.NewInt(0), nil +} + +// GetStatusesAfterExecution - +func (stub *MultiSigContractStub) GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) { + if stub.GetStatusesAfterExecutionCalled != nil { + return stub.GetStatusesAfterExecutionCalled(opts, batchID) + } + + return make([]byte, 0), nil +} + +// Paused - +func (stub *MultiSigContractStub) Paused(opts *bind.CallOpts) (bool, error) { + if stub.PausedCalled != nil { + return stub.PausedCalled(opts) + } + + return false, nil +} diff --git a/executors/ethereum/common.go b/executors/ethereum/common.go new file mode 100644 index 00000000..c4a93cdb --- /dev/null +++ b/executors/ethereum/common.go @@ -0,0 +1,100 @@ +package ethereum + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" +) + +// DepositInfo is the deposit info list +type DepositInfo struct { + DepositNonce uint64 `json:"DepositNonce"` + Token string `json:"Token"` + ContractAddressString string `json:"ContractAddress"` + Decimals byte `json:"Decimals"` + ContractAddress common.Address `json:"-"` + Amount *big.Int `json:"-"` + AmountString string `json:"Amount"` + DenominatedAmount *big.Float `json:"-"` + DenominatedAmountString string `json:"DenominatedAmount"` +} + +// BatchInfo is the batch info list +type BatchInfo struct { + OldSafeContractAddress string `json:"OldSafeContractAddress"` + NewSafeContractAddress string `json:"NewSafeContractAddress"` + BatchID uint64 `json:"BatchID"` + MessageHash common.Hash `json:"MessageHash"` + DepositsInfo []*DepositInfo `json:"DepositsInfo"` +} + +// SignatureInfo is the struct holding signature info +type SignatureInfo struct { + Address string `json:"Address"` + MessageHash string `json:"MessageHash"` + Signature string `json:"Signature"` +} + +// TokensBalancesDisplayString will convert the deposit balances into a human-readable string +func TokensBalancesDisplayString(batchInfo *BatchInfo) string { + maxTokenLen := 0 + maxIntegerValueLen := 0 + integerIndex := 0 + tokenIntegerSpace := make(map[string]int) + decimalSeparator := "." // src/math/big/ftoa.go L302 + for _, deposit := range batchInfo.DepositsInfo { + if len(deposit.Token) > maxTokenLen { + maxTokenLen = len(deposit.Token) + } + + valueParts := strings.Split(deposit.DenominatedAmountString, decimalSeparator) + integerPart := valueParts[integerIndex] + if len(integerPart) > maxIntegerValueLen { + maxIntegerValueLen = len(valueParts[integerIndex]) + } + tokenIntegerSpace[deposit.Token] = len(valueParts[integerIndex]) + } + + tokens := make([]string, 0, len(batchInfo.DepositsInfo)) + for _, deposit := range batchInfo.DepositsInfo { + spaceRequired := strings.Repeat(" ", maxTokenLen-len(deposit.Token)+maxIntegerValueLen-tokenIntegerSpace[deposit.Token]) + tokenInfo := fmt.Sprintf(" %s: %s%s", deposit.Token, spaceRequired, deposit.DenominatedAmountString) + + tokens = append(tokens, tokenInfo) + } + + return strings.Join(tokens, "\n") +} + +// ConvertPartialMigrationStringToMap converts the partial migration string to its map representation +func ConvertPartialMigrationStringToMap(partialMigration string) (map[string]*big.Float, error) { + partsSeparator := "," + tokenAmountSeparator := ":" + parts := strings.Split(partialMigration, partsSeparator) + + partialMap := make(map[string]*big.Float) + for _, part := range parts { + part = strings.Trim(part, " \t\n") + splt := strings.Split(part, tokenAmountSeparator) + if len(splt) != 2 { + return nil, fmt.Errorf("%w at token %s, invalid format", errInvalidPartialMigrationString, part) + } + + amount, ok := big.NewFloat(0).SetString(splt[1]) + if !ok { + return nil, fmt.Errorf("%w at token %s, not a number", errInvalidPartialMigrationString, part) + } + + token := splt[0] + if partialMap[token] == nil { + partialMap[token] = big.NewFloat(0).Set(amount) + continue + } + + partialMap[token].Add(partialMap[token], amount) + } + + return partialMap, nil +} diff --git a/executors/ethereum/common_test.go b/executors/ethereum/common_test.go new file mode 100644 index 00000000..12b75a1f --- /dev/null +++ b/executors/ethereum/common_test.go @@ -0,0 +1,133 @@ +package ethereum + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTokensBalancesDisplayString(t *testing.T) { + t.Parallel() + + batchInfo := &BatchInfo{ + DepositsInfo: []*DepositInfo{ + { + Token: "ETHUSDC-220753", + DenominatedAmountString: "5900401.957669", + }, + { + Token: "ETHUTK-8cdf7a", + DenominatedAmountString: "224564287.8192652", + }, + { + Token: "ETHUSDT-9c73c6", + DenominatedAmountString: "542188.933704", + }, + { + Token: "ETHBUSD-450923", + DenominatedAmountString: "22294.352736330155", + }, + { + Token: "ETHHMT-18538a", + DenominatedAmountString: "435", + }, + { + Token: "ETHCGG-ee4e0c", + DenominatedAmountString: "1594290.750967581", + }, + { + Token: "ETHINFRA-60a3bf2", + DenominatedAmountString: "141172.59598039952", + }, + { + Token: "ETHWBTC-74e282", + DenominatedAmountString: "39.46386326", + }, + { + Token: "ETHWETH-e1c126", + DenominatedAmountString: "664.1972941951753", + }, + { + Token: "ETHWSDAI-572803", + DenominatedAmountString: "5431.516086574386", + }, + { + Token: "ETHWDAI-bd65f9", + DenominatedAmountString: "118591.44846500318", + }, + { + Token: "ETHUMB-291202", + DenominatedAmountString: "4065258.3239772925", + }, + }, + } + + expectedString := + ` ETHUSDC-220753: 5900401.957669 + ETHUTK-8cdf7a: 224564287.8192652 + ETHUSDT-9c73c6: 542188.933704 + ETHBUSD-450923: 22294.352736330155 + ETHHMT-18538a: 435 + ETHCGG-ee4e0c: 1594290.750967581 + ETHINFRA-60a3bf2: 141172.59598039952 + ETHWBTC-74e282: 39.46386326 + ETHWETH-e1c126: 664.1972941951753 + ETHWSDAI-572803: 5431.516086574386 + ETHWDAI-bd65f9: 118591.44846500318 + ETHUMB-291202: 4065258.3239772925` + + assert.Equal(t, expectedString, TokensBalancesDisplayString(batchInfo)) +} + +func TestConvertPartialMigrationStringToMap(t *testing.T) { + t.Parallel() + + t.Run("invalid part should error", func(t *testing.T) { + t.Parallel() + + str := "k,f" + results, err := ConvertPartialMigrationStringToMap(str) + assert.Nil(t, results) + assert.ErrorIs(t, err, errInvalidPartialMigrationString) + assert.Contains(t, err.Error(), "at token k, invalid format") + + str = "k:1:2,f" + results, err = ConvertPartialMigrationStringToMap(str) + assert.Nil(t, results) + assert.ErrorIs(t, err, errInvalidPartialMigrationString) + assert.Contains(t, err.Error(), "at token k:1:2, invalid format") + }) + t.Run("amount is empty should error", func(t *testing.T) { + t.Parallel() + + str := "k:,f:1" + results, err := ConvertPartialMigrationStringToMap(str) + assert.Nil(t, results) + assert.ErrorIs(t, err, errInvalidPartialMigrationString) + assert.Contains(t, err.Error(), "at token k:, not a number") + + str = "k:1d2,f" + results, err = ConvertPartialMigrationStringToMap(str) + assert.Nil(t, results) + assert.ErrorIs(t, err, errInvalidPartialMigrationString) + assert.Contains(t, err.Error(), "at token k:1d2, not a number") + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + str := "k:1,f:1,k:2.2,g:0.001,h:0" + results, err := ConvertPartialMigrationStringToMap(str) + assert.Nil(t, err) + + expectedResults := map[string]*big.Float{ + "k": big.NewFloat(3.2), + "f": big.NewFloat(1), + "g": big.NewFloat(0.001), + "h": big.NewFloat(0), + } + + assert.Nil(t, err) + assert.Equal(t, expectedResults, results) + }) +} diff --git a/executors/ethereum/errors.go b/executors/ethereum/errors.go new file mode 100644 index 00000000..d1855ce4 --- /dev/null +++ b/executors/ethereum/errors.go @@ -0,0 +1,18 @@ +package ethereum + +import "errors" + +var ( + errEmptyTokensList = errors.New("empty tokens list") + errNilMvxDataGetter = errors.New("nil MultiversX data getter") + errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") + errWrongERC20AddressResponse = errors.New("wrong ERC20 address response") + errNilLogger = errors.New("nil logger") + errNilCryptoHandler = errors.New("nil crypto handler") + errNilEthereumChainWrapper = errors.New("nil Ethereum chain wrapper") + errQuorumNotReached = errors.New("quorum not reached") + errInvalidSignature = errors.New("invalid signature") + errMultisigContractPaused = errors.New("multisig contract paused") + errNilGasHandler = errors.New("nil gas handler") + errInvalidPartialMigrationString = errors.New("invalid partial migration string") +) diff --git a/executors/ethereum/interface.go b/executors/ethereum/interface.go new file mode 100644 index 00000000..14f9e537 --- /dev/null +++ b/executors/ethereum/interface.go @@ -0,0 +1,64 @@ +package ethereum + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// TokensMapper can convert a token bytes from one chain to another +type TokensMapper interface { + ConvertToken(ctx context.Context, sourceBytes []byte) ([]byte, error) + IsInterfaceNil() bool +} + +// Erc20ContractsHolder defines the Ethereum ERC20 contract operations +type Erc20ContractsHolder interface { + BalanceOf(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) + Decimals(ctx context.Context, address common.Address) (uint8, error) + IsInterfaceNil() bool +} + +// SafeContractWrapper defines the operations for the safe contract +type SafeContractWrapper interface { + DepositsCount(opts *bind.CallOpts) (uint64, error) + BatchesCount(opts *bind.CallOpts) (uint64, error) +} + +// MvxDataGetter defines the operations for the data getter operating on MultiversX chain +type MvxDataGetter interface { + GetAllKnownTokens(ctx context.Context) ([][]byte, error) + GetERC20AddressForTokenId(ctx context.Context, tokenId []byte) ([][]byte, error) + IsInterfaceNil() bool +} + +// EthereumChainWrapper defines the operations of the Ethereum wrapper +type EthereumChainWrapper interface { + ExecuteTransfer(opts *bind.TransactOpts, tokens []common.Address, + recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, + signatures [][]byte) (*types.Transaction, error) + ChainID(ctx context.Context) (*big.Int, error) + BlockNumber(ctx context.Context) (uint64, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) + Quorum(ctx context.Context) (*big.Int, error) + GetRelayers(ctx context.Context) ([]common.Address, error) + WasBatchExecuted(ctx context.Context, batchNonce *big.Int) (bool, error) + IsPaused(ctx context.Context) (bool, error) +} + +// CryptoHandler defines the operations for a component that expose some crypto primitives +type CryptoHandler interface { + Sign(msgHash common.Hash) ([]byte, error) + GetAddress() common.Address + CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) + IsInterfaceNil() bool +} + +// GasHandler defines the component able to fetch the current gas price +type GasHandler interface { + GetCurrentGasPrice() (*big.Int, error) + IsInterfaceNil() bool +} diff --git a/executors/ethereum/migrationBatchCreator.go b/executors/ethereum/migrationBatchCreator.go new file mode 100644 index 00000000..9e88271f --- /dev/null +++ b/executors/ethereum/migrationBatchCreator.go @@ -0,0 +1,316 @@ +package ethereum + +import ( + "context" + "fmt" + "math/big" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" + "github.com/multiversx/mx-chain-core-go/core/check" + logger "github.com/multiversx/mx-chain-logger-go" +) + +var zero = big.NewInt(0) + +const timeBetweenBatchIDChecks = time.Millisecond * 100 + +// ArgsMigrationBatchCreator is the argument for the NewMigrationBatchCreator constructor +type ArgsMigrationBatchCreator struct { + MvxDataGetter MvxDataGetter + Erc20ContractsHolder Erc20ContractsHolder + SafeContractAddress common.Address + EthereumChainWrapper EthereumChainWrapper + Logger logger.Logger +} + +type migrationBatchCreator struct { + mvxDataGetter MvxDataGetter + erc20ContractsHolder Erc20ContractsHolder + safeContractAddress common.Address + ethereumChainWrapper EthereumChainWrapper + logger logger.Logger +} + +// NewMigrationBatchCreator creates a new instance of type migrationBatchCreator that is able to generate the migration batch output file +func NewMigrationBatchCreator(args ArgsMigrationBatchCreator) (*migrationBatchCreator, error) { + if check.IfNil(args.MvxDataGetter) { + return nil, errNilMvxDataGetter + } + if check.IfNil(args.Erc20ContractsHolder) { + return nil, errNilErc20ContractsHolder + } + if check.IfNilReflect(args.EthereumChainWrapper) { + return nil, errNilEthereumChainWrapper + } + if check.IfNil(args.Logger) { + return nil, errNilLogger + } + + return &migrationBatchCreator{ + mvxDataGetter: args.MvxDataGetter, + erc20ContractsHolder: args.Erc20ContractsHolder, + safeContractAddress: args.SafeContractAddress, + logger: args.Logger, + ethereumChainWrapper: args.EthereumChainWrapper, + }, nil +} + +// CreateBatchInfo creates an instance of type BatchInfo +func (creator *migrationBatchCreator) CreateBatchInfo(ctx context.Context, newSafeAddress common.Address, partialMigration map[string]*big.Float) (*BatchInfo, error) { + creator.logger.Info("started the batch creation process...") + + depositStart := uint64(0) // deposits inside a batch are not tracked, we can start from 0 + + creator.logger.Info("will try to find a usable batch ID, please wait, this might take a while...") + + startTime := time.Now() + freeBatchID, err := creator.findAnUsableBatchID(ctx, timeBetweenBatchIDChecks) + endTime := time.Now() + if err != nil { + return nil, err + } + + creator.logger.Info("fetched Ethereum contracts state", + "free batch ID", freeBatchID, "time took", endTime.Sub(startTime)) + + if partialMigration == nil { + partialMigration = make(map[string]*big.Float) + } + + tokensList, err := creator.getTokensList(ctx, partialMigration) + if err != nil { + return nil, err + } + + creator.logger.Info("fetched known tokens", "tokens", strings.Join(tokensList, ", ")) + + deposits, err := creator.fetchERC20ContractsAddresses(ctx, tokensList, depositStart) + if err != nil { + return nil, err + } + + creator.logger.Info("fetched ERC20 contract addresses") + + err = creator.fetchBalances(ctx, deposits, partialMigration) + if err != nil { + return nil, err + } + + creator.logger.Info("fetched balances contract addresses") + + return creator.assembleBatchInfo(freeBatchID, deposits, newSafeAddress) +} + +func (creator *migrationBatchCreator) findAnUsableBatchID(ctx context.Context, timeBetweenChecks time.Duration) (uint64, error) { + highBatchID := uint64(100000) + lowBatchID := uint64(1) + increaseHigh := uint64(100000) + + batchesUsedMap := make(map[uint64]bool) + for { + err := creator.checkAvailableBatch(ctx, highBatchID, batchesUsedMap, timeBetweenChecks) + if err != nil { + return 0, err + } + + err = creator.checkAvailableBatch(ctx, lowBatchID, batchesUsedMap, timeBetweenChecks) + if err != nil { + return 0, err + } + + if !batchesUsedMap[lowBatchID] { + // beginning of the interval optimization + return lowBatchID, nil + } + + if batchesUsedMap[highBatchID] && batchesUsedMap[lowBatchID] { + // high was too low + highBatchID += increaseHigh + continue + } + + mid := (highBatchID + lowBatchID) / 2 + if mid == lowBatchID { + // high and low are so close that their middle value between them is actually the low value, return high + return highBatchID, nil + } + + err = creator.checkAvailableBatch(ctx, mid, batchesUsedMap, timeBetweenChecks) + if err != nil { + return 0, err + } + + if batchesUsedMap[mid] { + // the middle value was set, bring the low value to it and restart the checking process + lowBatchID = mid + } else { + // the middle value was not set, bring the high value to it and restart the checking process + highBatchID = mid + } + } +} + +func (creator *migrationBatchCreator) checkAvailableBatch( + ctx context.Context, + batchID uint64, + batchesUsedMap map[uint64]bool, + timeBetweenChecks time.Duration, +) error { + _, checked := batchesUsedMap[batchID] + if checked { + return nil + } + + time.Sleep(timeBetweenChecks) + wasExecuted, err := creator.ethereumChainWrapper.WasBatchExecuted(ctx, big.NewInt(0).SetUint64(batchID)) + if err != nil { + return err + } + + batchesUsedMap[batchID] = wasExecuted + return nil +} + +func (creator *migrationBatchCreator) getTokensList(ctx context.Context, partialMigration map[string]*big.Float) ([]string, error) { + tokens, err := creator.mvxDataGetter.GetAllKnownTokens(ctx) + if err != nil { + return nil, err + } + if len(tokens) == 0 { + return nil, fmt.Errorf("%w when calling the getAllKnownTokens function on the safe contract", errEmptyTokensList) + } + + stringTokens := make([]string, 0, len(tokens)) + for _, token := range tokens { + if len(partialMigration) > 1 && partialMigration[string(token)] == nil { + // partial migration was set, but for the current token in this deposit a value was not given + // skip this deposit + continue + } + + stringTokens = append(stringTokens, string(token)) + } + + return stringTokens, nil +} + +func (creator *migrationBatchCreator) fetchERC20ContractsAddresses(ctx context.Context, tokensList []string, lastDepositNonce uint64) ([]*DepositInfo, error) { + deposits := make([]*DepositInfo, 0, len(tokensList)) + for idx, token := range tokensList { + response, err := creator.mvxDataGetter.GetERC20AddressForTokenId(ctx, []byte(token)) + if err != nil { + return nil, err + } + if len(response) != 1 { + return nil, fmt.Errorf("%w when querying the safe contract for token %s", + errWrongERC20AddressResponse, token) + } + + deposit := &DepositInfo{ + DepositNonce: lastDepositNonce + uint64(1+idx), + Token: token, + ContractAddressString: common.BytesToAddress(response[0]).String(), + ContractAddress: common.BytesToAddress(response[0]), + AmountString: "", + } + + deposits = append(deposits, deposit) + } + + return deposits, nil +} + +func (creator *migrationBatchCreator) fetchBalances(ctx context.Context, deposits []*DepositInfo, partialMigration map[string]*big.Float) error { + for _, deposit := range deposits { + balance, err := creator.erc20ContractsHolder.BalanceOf(ctx, deposit.ContractAddress, creator.safeContractAddress) + if err != nil { + return fmt.Errorf("%w for address %s in ERC20 contract %s", err, creator.safeContractAddress.String(), deposit.ContractAddress.String()) + } + + decimals, err := creator.erc20ContractsHolder.Decimals(ctx, deposit.ContractAddress) + if err != nil { + return fmt.Errorf("%w for in ERC20 contract %s", err, deposit.ContractAddress.String()) + } + deposit.Decimals = decimals + + trimAmount := partialMigration[deposit.Token] + if trimAmount != nil { + denominatedTrimAmount := big.NewFloat(0).Set(trimAmount) + multiplier := big.NewInt(10) + multiplier.Exp(multiplier, big.NewInt(int64(deposit.Decimals)), nil) + denominatedTrimAmount.Mul(denominatedTrimAmount, big.NewFloat(0).SetInt(multiplier)) + + newBalance := big.NewInt(0) + denominatedTrimAmount.Int(newBalance) + if balance.Cmp(newBalance) > 0 { + creator.logger.Warn("applied denominated value", "balance", balance.String(), "new value to consider", newBalance.String()) + balance = newBalance + } else { + creator.logger.Warn("can not apply denominated value as the balance is under the provided value, will use the whole balance", "balance", balance.String()) + } + } + + deposit.Amount = balance + deposit.AmountString = balance.String() + + divider := big.NewInt(10) + divider.Exp(divider, big.NewInt(int64(decimals)), nil) + + deposit.DenominatedAmount = big.NewFloat(0).SetInt(balance) + deposit.DenominatedAmount.Quo(deposit.DenominatedAmount, big.NewFloat(0).SetInt(divider)) + deposit.DenominatedAmountString = deposit.DenominatedAmount.Text('f', -1) + } + + return nil +} + +func (creator *migrationBatchCreator) assembleBatchInfo(usableBatchID uint64, deposits []*DepositInfo, newSafeAddress common.Address) (*BatchInfo, error) { + batchInfo := &BatchInfo{ + OldSafeContractAddress: creator.safeContractAddress.String(), + NewSafeContractAddress: newSafeAddress.String(), + BatchID: usableBatchID, + DepositsInfo: make([]*DepositInfo, 0, len(deposits)), + } + + for _, deposit := range deposits { + if deposit.Amount.Cmp(zero) <= 0 { + continue + } + + batchInfo.DepositsInfo = append(batchInfo.DepositsInfo, deposit) + } + + var err error + batchInfo.MessageHash, err = creator.computeMessageHash(batchInfo) + if err != nil { + return nil, err + } + + return batchInfo, nil +} + +func (creator *migrationBatchCreator) computeMessageHash(batch *BatchInfo) (common.Hash, error) { + tokens := make([]common.Address, 0, len(batch.DepositsInfo)) + recipients := make([]common.Address, 0, len(batch.DepositsInfo)) + amounts := make([]*big.Int, 0, len(batch.DepositsInfo)) + nonces := make([]*big.Int, 0, len(batch.DepositsInfo)) + for _, deposit := range batch.DepositsInfo { + tokens = append(tokens, deposit.ContractAddress) + recipients = append(recipients, common.HexToAddress(batch.NewSafeContractAddress)) + amounts = append(amounts, deposit.Amount) + nonces = append(nonces, big.NewInt(0).SetUint64(deposit.DepositNonce)) + } + + args := &batchProcessor.ArgListsBatch{ + EthTokens: tokens, + Recipients: recipients, + Amounts: amounts, + Nonces: nonces, + } + + return ethereum.GenerateMessageHash(args, batch.BatchID) +} diff --git a/executors/ethereum/migrationBatchCreator_test.go b/executors/ethereum/migrationBatchCreator_test.go new file mode 100644 index 00000000..277aa319 --- /dev/null +++ b/executors/ethereum/migrationBatchCreator_test.go @@ -0,0 +1,576 @@ +package ethereum + +import ( + "bytes" + "context" + "errors" + "fmt" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" + "github.com/multiversx/mx-chain-go/testscommon" + "github.com/stretchr/testify/assert" +) + +var safeContractAddress = common.HexToAddress(strings.Repeat("9", 40)) +var tkn1Erc20Address = bytes.Repeat([]byte("1"), 20) +var tkn2Erc20Address = bytes.Repeat([]byte("2"), 20) +var tkn3Erc20Address = bytes.Repeat([]byte("3"), 20) +var tkn4Erc20Address = bytes.Repeat([]byte("4"), 20) +var balanceOfTkn1 = big.NewInt(19) +var balanceOfTkn2 = big.NewInt(38) +var balanceOfTkn3 = big.NewInt(138) +var balanceOfTkn4 = big.NewInt(1137) +var expectedErr = errors.New("expected error") + +func createMockArgsForMigrationBatchCreator() ArgsMigrationBatchCreator { + return ArgsMigrationBatchCreator{ + MvxDataGetter: &bridge.DataGetterStub{ + GetAllKnownTokensCalled: func(ctx context.Context) ([][]byte, error) { + return [][]byte{ + []byte("tkn1"), + []byte("tkn2"), + []byte("tkn3"), + []byte("tkn4"), + }, nil + }, + GetERC20AddressForTokenIdCalled: func(ctx context.Context, tokenId []byte) ([][]byte, error) { + return [][]byte{[]byte("erc 20 address")}, nil + }, + }, + Erc20ContractsHolder: &bridge.ERC20ContractsHolderStub{}, + SafeContractAddress: safeContractAddress, + Logger: &testscommon.LoggerStub{}, + EthereumChainWrapper: &bridge.EthereumClientWrapperStub{}, + } +} + +func TestNewMigrationBatchCreator(t *testing.T) { + t.Parallel() + + t.Run("nil mvx data getter should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.MvxDataGetter = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilMvxDataGetter, err) + }) + t.Run("nil erc20 contracts holder should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.Erc20ContractsHolder = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilErc20ContractsHolder, err) + }) + t.Run("nil logger should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.Logger = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilLogger, err) + }) + t.Run("nil Ethereum chain wrapper should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.EthereumChainWrapper = nil + + creator, err := NewMigrationBatchCreator(args) + assert.Nil(t, creator) + assert.Equal(t, errNilEthereumChainWrapper, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + + creator, err := NewMigrationBatchCreator(args) + assert.NotNil(t, creator) + assert.Nil(t, err) + }) +} + +func TestFindAnUsableBatchID(t *testing.T) { + unreachableBatchID := uint64(math.MaxUint64) + + t.Run("was batch used errors, should error", func(t *testing.T) { + t.Parallel() + + result, err := testFindAnUsableBatchID(t, 1367, 1) + assert.Nil(t, result) + assert.ErrorIs(t, err, expectedErr) + assert.Contains(t, err.Error(), "on batch 1") + + result, err = testFindAnUsableBatchID(t, 1367, 100000) + assert.Nil(t, result) + assert.ErrorIs(t, err, expectedErr) + assert.Contains(t, err.Error(), "on batch 100000") + + result, err = testFindAnUsableBatchID(t, 1367, 50000) + assert.Nil(t, result) + assert.ErrorIs(t, err, expectedErr) + assert.Contains(t, err.Error(), "on batch 50000") + }) + t.Run("should resolve in an optimum number of steps", func(t *testing.T) { + t.Parallel() + + result, err := testFindAnUsableBatchID(t, 1367, unreachableBatchID) + assert.Nil(t, err) + expectedMap := map[uint64]int{ + 1: 1, // initial low + 100000: 1, // initial high + 50000: 1, // (1 + 100000) / 2 = 50000 + 25000: 1, // (1 + 50000) / 2 = 25000 + 12500: 1, // (1 + 25000) / 2 = 12500 + 6250: 1, // (1 + 12500) / 2 = 6250 + 3125: 1, // (1 + 6250) / 2 = 3125 + 1563: 1, // (1 + 3125) / 2 = 1563 + 782: 1, // (1 + 1563) / 2 = 782 + 1172: 1, // (782 + 1563) / 2 = 1172 + 1367: 1, // (1172 + 1563) / 2 = 1367 + 1269: 1, // (1172 + 1367) / 2 = 1269 + 1318: 1, // (1269 + 1367) / 2 = 1318 + 1342: 1, // (1318 + 1367) / 2 = 1342 + 1354: 1, // (1342 + 1367) / 2 = 1354 + 1360: 1, // (1354 + 1367) / 2 = 1360 + 1363: 1, // (1360 + 1367) / 2 = 1363 + 1365: 1, // (1363 + 1367) / 2 = 1365 + 1366: 1, // (1365 + 1367) / 2 = 1366 + } + + assert.Equal(t, expectedMap, result) + }) + t.Run("should resolve in an optimum number of steps if initial higher is not enough", func(t *testing.T) { + t.Parallel() + + result, err := testFindAnUsableBatchID(t, 175000, unreachableBatchID) + assert.Nil(t, err) + expectedMap := map[uint64]int{ + 1: 1, // initial low + 100000: 1, // initial high + 200000: 1, // added 100000 to the initial high + 150000: 1, // (100000 + 200000) / 2 = 150000 + 175000: 1, // (150000 + 200000) / 2 = 175000 + 162500: 1, // (150000 + 175000) / 2 = 162500 + 168750: 1, // (162500 + 175000) / 2 = 168750 + 171875: 1, // (168750 + 175000) / 2 = 171875 + 173437: 1, // (171875 + 175000) / 2 = 173437 + 174218: 1, // (173437 + 175000) / 2 = 174218 + 174609: 1, // (174218 + 175000) / 2 = 174609 + 174804: 1, // (174609 + 175000) / 2 = 174804 + 174902: 1, // (174804 + 175000) / 2 = 174902 + 174951: 1, // (174902 + 175000) / 2 = 174951 + 174975: 1, // (174951 + 175000) / 2 = 174975 + 174987: 1, // (174975 + 175000) / 2 = 174987 + 174993: 1, // (174987 + 175000) / 2 = 174993 + 174996: 1, // (174993 + 175000) / 2 = 174996 + 174998: 1, // (174996 + 175000) / 2 = 174998 + 174999: 1, // (174998 + 175000) / 2 = 174999 + } + + assert.Equal(t, expectedMap, result) + }) + t.Run("should resolve in an optimum number of steps on 1", func(t *testing.T) { + t.Parallel() + + result, err := testFindAnUsableBatchID(t, 1, unreachableBatchID) + assert.Nil(t, err) + expectedMap := map[uint64]int{ + 1: 1, // initial low + 100000: 1, // initial high + } + + assert.Equal(t, expectedMap, result) + }) + t.Run("should resolve in an optimum number of steps on 2", func(t *testing.T) { + t.Parallel() + + result, err := testFindAnUsableBatchID(t, 2, unreachableBatchID) + assert.Nil(t, err) + expectedMap := map[uint64]int{ + 1: 1, // initial low + 100000: 1, // initial high + 50000: 1, // (1 + 100000) / 2 = 50000 + 25000: 1, // (1 + 50000) / 2 = 25000 + 12500: 1, // (1 + 25000) / 2 = 12500 + 6250: 1, // (1 + 12500) / 2 = 6250 + 3125: 1, // (1 + 6250) / 2 = 3125 + 1563: 1, // (1 + 3125) / 2 = 1563 + 782: 1, // (1 + 1563) / 2 = 782 + 391: 1, // (1 + 782) / 2 = 391 + 196: 1, // (1 + 391) / 2 = 196 + 98: 1, // (1 + 196) / 2 = 98 + 49: 1, // (1 + 98) / 2 = 49 + 25: 1, // (1 + 49) / 2 = 25 + 13: 1, // (1 + 25) / 2 = 13 + 7: 1, // (1 + 13) / 2 = 7 + 4: 1, // (1 + 7) / 2 = 4 + 2: 1, // (1 + 4) / 2 = 2 + } + + assert.Equal(t, expectedMap, result) + }) + t.Run("should resolve in an optimum number of steps on 100000", func(t *testing.T) { + t.Parallel() + + result, err := testFindAnUsableBatchID(t, 100000, unreachableBatchID) + assert.Nil(t, err) + expectedMap := map[uint64]int{ + 1: 1, // initial low + 100000: 1, // initial high + 50000: 1, // (1 + 100000) / 2 = 50000 + 75000: 1, // (50000 + 100000) / 2 = 75000 + 87500: 1, // (75000 + 100000) / 2 = 87500 + 93750: 1, // (87500 + 100000) / 2 = 93750 + 96875: 1, // (93750 + 100000) / 2 = 96875 + 98437: 1, // (96875 + 100000) / 2 = 98437 + 99218: 1, // (98437 + 100000) / 2 = 99218 + 99609: 1, // (99218 + 100000) / 2 = 99609 + 99804: 1, // (99609 + 100000) / 2 = 99804 + 99902: 1, // (99804 + 100000) / 2 = 99902 + 99951: 1, // (99902 + 100000) / 2 = 99951 + 99975: 1, // (99951 + 100000) / 2 = 99975 + 99987: 1, // (99975 + 100000) / 2 = 99987 + 99993: 1, // (99987 + 100000) / 2 = 99993 + 99996: 1, // (99993 + 100000) / 2 = 99996 + 99998: 1, // (99996 + 100000) / 2 = 99998 + 99999: 1, // (99998 + 100000) / 2 = 99999 + } + + assert.Equal(t, expectedMap, result) + }) +} + +func testFindAnUsableBatchID(t *testing.T, firstFreeBatchId uint64, errorBatchID uint64) (map[uint64]int, error) { + args := createMockArgsForMigrationBatchCreator() + checkedMap := make(map[uint64]int) + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + batchNonceUint64 := batchNonce.Uint64() + + if batchNonceUint64 == errorBatchID { + return false, fmt.Errorf("%w on batch %d", expectedErr, batchNonceUint64) + } + + checkedMap[batchNonceUint64]++ + return batchNonceUint64 < firstFreeBatchId, nil + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batchID, err := creator.findAnUsableBatchID(context.Background(), 0) + if err != nil { + return nil, err + } + + assert.Equal(t, firstFreeBatchId, batchID) + + return checkedMap, nil +} + +func TestMigrationBatchCreator_CreateBatchInfo(t *testing.T) { + t.Parallel() + + newSafeContractAddress := common.HexToAddress(strings.Repeat("8", 40)) + firstFreeBatchId := uint64(1367) + t.Run("findAnUsableBatchID errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + return false, expectedErr + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, nil) + assert.Equal(t, expectedErr, err) + assert.Nil(t, batch) + }) + t.Run("get all known tokens errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + return batchNonce.Uint64() < firstFreeBatchId, nil + }, + } + args.MvxDataGetter = &bridge.DataGetterStub{ + GetAllKnownTokensCalled: func(ctx context.Context) ([][]byte, error) { + return nil, expectedErr + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, nil) + assert.Equal(t, expectedErr, err) + assert.Nil(t, batch) + }) + t.Run("get all known tokens returns 0 tokens should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + return batchNonce.Uint64() < firstFreeBatchId, nil + }, + } + args.MvxDataGetter = &bridge.DataGetterStub{ + GetAllKnownTokensCalled: func(ctx context.Context) ([][]byte, error) { + return make([][]byte, 0), nil + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, nil) + assert.ErrorIs(t, err, errEmptyTokensList) + assert.Nil(t, batch) + }) + t.Run("GetERC20AddressForTokenId errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + return batchNonce.Uint64() < firstFreeBatchId, nil + }, + } + args.MvxDataGetter.(*bridge.DataGetterStub).GetERC20AddressForTokenIdCalled = func(ctx context.Context, sourceBytes []byte) ([][]byte, error) { + return nil, expectedErr + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, nil) + assert.Equal(t, expectedErr, err) + assert.Nil(t, batch) + }) + t.Run("GetERC20AddressForTokenId returns empty list should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + return batchNonce.Uint64() < firstFreeBatchId, nil + }, + } + args.MvxDataGetter.(*bridge.DataGetterStub).GetERC20AddressForTokenIdCalled = func(ctx context.Context, sourceBytes []byte) ([][]byte, error) { + return make([][]byte, 0), nil + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, nil) + assert.ErrorIs(t, err, errWrongERC20AddressResponse) + assert.Nil(t, batch) + }) + t.Run("BalanceOf errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + return batchNonce.Uint64() < firstFreeBatchId, nil + }, + } + args.Erc20ContractsHolder = &bridge.ERC20ContractsHolderStub{ + BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { + return nil, expectedErr + }, + } + + creator, _ := NewMigrationBatchCreator(args) + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, nil) + assert.ErrorIs(t, err, expectedErr) + assert.Nil(t, batch) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsForMigrationBatchCreator() + args.MvxDataGetter.(*bridge.DataGetterStub).GetERC20AddressForTokenIdCalled = func(ctx context.Context, sourceBytes []byte) ([][]byte, error) { + if string(sourceBytes) == "tkn1" { + return [][]byte{tkn1Erc20Address}, nil + } + if string(sourceBytes) == "tkn2" { + return [][]byte{tkn2Erc20Address}, nil + } + if string(sourceBytes) == "tkn3" { + return [][]byte{tkn3Erc20Address}, nil + } + if string(sourceBytes) == "tkn4" { + return [][]byte{tkn4Erc20Address}, nil + } + + return nil, fmt.Errorf("unexpected source bytes") + } + args.Erc20ContractsHolder = &bridge.ERC20ContractsHolderStub{ + BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { + assert.Equal(t, address.String(), safeContractAddress.String()) + + if string(erc20Address.Bytes()) == string(tkn1Erc20Address) { + return balanceOfTkn1, nil + } + if string(erc20Address.Bytes()) == string(tkn2Erc20Address) { + return balanceOfTkn2, nil + } + if string(erc20Address.Bytes()) == string(tkn3Erc20Address) { + return balanceOfTkn3, nil + } + if string(erc20Address.Bytes()) == string(tkn4Erc20Address) { + return balanceOfTkn4, nil + } + + return nil, fmt.Errorf("unexpected ERC20 contract address") + }, + DecimalsCalled: func(ctx context.Context, erc20Address common.Address) (uint8, error) { + if string(erc20Address.Bytes()) == string(tkn1Erc20Address) { + return 3, nil + } + if string(erc20Address.Bytes()) == string(tkn2Erc20Address) { + return 18, nil + } + if string(erc20Address.Bytes()) == string(tkn2Erc20Address) { + return 0, nil + } + if string(erc20Address.Bytes()) == string(tkn4Erc20Address) { + return 1, nil + } + + return 0, nil + }, + } + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + WasBatchExecutedCalled: func(ctx context.Context, batchNonce *big.Int) (bool, error) { + return batchNonce.Uint64() < firstFreeBatchId, nil + }, + } + creator, _ := NewMigrationBatchCreator(args) + + t.Run("without migration map", func(t *testing.T) { + expectedBatch := &BatchInfo{ + OldSafeContractAddress: safeContractAddress.String(), + NewSafeContractAddress: newSafeContractAddress.String(), + BatchID: firstFreeBatchId, + MessageHash: common.HexToHash("0xa0d36274c96845ee51e76980df39c44cdabfa41b85238457cab8834ad8410447"), + DepositsInfo: []*DepositInfo{ + { + DepositNonce: 1, + Token: "tkn1", + ContractAddressString: common.BytesToAddress(tkn1Erc20Address).String(), + Decimals: 3, + ContractAddress: common.BytesToAddress(tkn1Erc20Address), + Amount: big.NewInt(19), + AmountString: "19", + DenominatedAmountString: "0.019", + }, + { + DepositNonce: 2, + Token: "tkn2", + ContractAddressString: common.BytesToAddress(tkn2Erc20Address).String(), + Decimals: 18, + ContractAddress: common.BytesToAddress(tkn2Erc20Address), + Amount: big.NewInt(38), + AmountString: "38", + DenominatedAmountString: "0.000000000000000038", + }, + { + DepositNonce: 3, + Token: "tkn3", + ContractAddressString: common.BytesToAddress(tkn3Erc20Address).String(), + Decimals: 0, + ContractAddress: common.BytesToAddress(tkn3Erc20Address), + Amount: big.NewInt(138), + AmountString: "138", + DenominatedAmountString: "138", + }, + { + DepositNonce: 4, + Token: "tkn4", + ContractAddressString: common.BytesToAddress(tkn4Erc20Address).String(), + Decimals: 1, + ContractAddress: common.BytesToAddress(tkn4Erc20Address), + Amount: big.NewInt(1137), + AmountString: "1137", + DenominatedAmountString: "113.7", + }, + }, + } + expectedBatch.DepositsInfo[0].DenominatedAmount, _ = big.NewFloat(0).SetString("0.019") + expectedBatch.DepositsInfo[1].DenominatedAmount, _ = big.NewFloat(0).SetString("0.000000000000000038") + expectedBatch.DepositsInfo[2].DenominatedAmount, _ = big.NewFloat(0).SetString("138") + expectedBatch.DepositsInfo[3].DenominatedAmount, _ = big.NewFloat(0).SetString("113.7") + + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, nil) + assert.Nil(t, err) + assert.Equal(t, expectedBatch, batch) + }) + t.Run("with trim", func(t *testing.T) { + expectedBatch := &BatchInfo{ + OldSafeContractAddress: safeContractAddress.String(), + NewSafeContractAddress: newSafeContractAddress.String(), + BatchID: firstFreeBatchId, + MessageHash: common.HexToHash("0xb726ee06a2fd99ef8e78cf97dc25522260796df572cd3967a6e750c3a1201276"), + DepositsInfo: []*DepositInfo{ + { + DepositNonce: 1, + Token: "tkn1", + ContractAddressString: common.BytesToAddress(tkn1Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn1Erc20Address), + Amount: big.NewInt(17), + AmountString: "17", + DenominatedAmountString: "0.017", + Decimals: 3, + }, + { + DepositNonce: 2, + Token: "tkn2", + ContractAddressString: common.BytesToAddress(tkn2Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn2Erc20Address), + Amount: big.NewInt(20), + AmountString: "20", + DenominatedAmountString: "0.00000000000000002", + Decimals: 18, + }, + { + DepositNonce: 3, + Token: "tkn3", + ContractAddressString: common.BytesToAddress(tkn3Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn3Erc20Address), + Amount: big.NewInt(120), + AmountString: "120", + DenominatedAmountString: "120", + Decimals: 0, + }, + }, + } + expectedBatch.DepositsInfo[0].DenominatedAmount, _ = big.NewFloat(0).SetString("0.017") + expectedBatch.DepositsInfo[1].DenominatedAmount, _ = big.NewFloat(0).SetString("0.000000000000000020") + expectedBatch.DepositsInfo[2].DenominatedAmount, _ = big.NewFloat(0).SetString("120") + + partialMap := map[string]*big.Float{ + "tkn1": big.NewFloat(0.017), + "tkn3": big.NewFloat(120), + } + partialMap["tkn2"], _ = big.NewFloat(0).SetString("0.000000000000000020") + + batch, err := creator.CreateBatchInfo(context.Background(), newSafeContractAddress, partialMap) + assert.Nil(t, err) + assert.Equal(t, expectedBatch, batch) + }) + }) + +} diff --git a/executors/ethereum/migrationBatchExecutor.go b/executors/ethereum/migrationBatchExecutor.go new file mode 100644 index 00000000..adddc767 --- /dev/null +++ b/executors/ethereum/migrationBatchExecutor.go @@ -0,0 +1,294 @@ +package ethereum + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/multiversx/mx-chain-core-go/core/check" + logger "github.com/multiversx/mx-chain-logger-go" +) + +const ethSignatureSize = 64 + +// ArgsMigrationBatchExecutor is the argument for the NewMigrationBatchExecutor constructor +type ArgsMigrationBatchExecutor struct { + EthereumChainWrapper EthereumChainWrapper + CryptoHandler CryptoHandler + Batch BatchInfo + Signatures []SignatureInfo + Logger logger.Logger + GasHandler GasHandler + TransferGasLimitBase uint64 + TransferGasLimitForEach uint64 +} + +type migrationBatchExecutor struct { + ethereumChainWrapper EthereumChainWrapper + cryptoHandler CryptoHandler + batch BatchInfo + signatures []SignatureInfo + logger logger.Logger + gasHandler GasHandler + transferGasLimitBase uint64 + transferGasLimitForEach uint64 +} + +// NewMigrationBatchExecutor creates a new instance of type migrationBatchCreator that is able to execute the multisig transfer +func NewMigrationBatchExecutor(args ArgsMigrationBatchExecutor) (*migrationBatchExecutor, error) { + if check.IfNilReflect(args.EthereumChainWrapper) { + return nil, errNilEthereumChainWrapper + } + if check.IfNil(args.CryptoHandler) { + return nil, errNilCryptoHandler + } + if check.IfNil(args.Logger) { + return nil, errNilLogger + } + if check.IfNil(args.GasHandler) { + return nil, errNilGasHandler + } + + return &migrationBatchExecutor{ + ethereumChainWrapper: args.EthereumChainWrapper, + cryptoHandler: args.CryptoHandler, + batch: args.Batch, + signatures: args.Signatures, + logger: args.Logger, + gasHandler: args.GasHandler, + transferGasLimitBase: args.TransferGasLimitBase, + transferGasLimitForEach: args.TransferGasLimitForEach, + }, nil +} + +// ExecuteTransfer will try to execute the transfer +func (executor *migrationBatchExecutor) ExecuteTransfer(ctx context.Context) error { + isPaused, err := executor.ethereumChainWrapper.IsPaused(ctx) + if err != nil { + return fmt.Errorf("%w in executor.ExecuteTransfer", err) + } + if isPaused { + return fmt.Errorf("%w in executor.ExecuteTransfer", errMultisigContractPaused) + } + + relayers, err := executor.ethereumChainWrapper.GetRelayers(ctx) + if err != nil { + return err + } + + quorum, err := executor.ethereumChainWrapper.Quorum(ctx) + if err != nil { + return err + } + + signatures, err := executor.checkRelayersSigsAndQuorum(relayers, quorum) + if err != nil { + return err + } + + nonce, err := executor.getNonce(ctx, executor.cryptoHandler.GetAddress()) + if err != nil { + return err + } + + chainId, err := executor.ethereumChainWrapper.ChainID(ctx) + if err != nil { + return err + } + + auth, err := executor.cryptoHandler.CreateKeyedTransactor(chainId) + if err != nil { + return err + } + + gasPrice, err := executor.gasHandler.GetCurrentGasPrice() + if err != nil { + return err + } + + tokens, recipients, amounts, depositNonces, batchNonce := executor.extractArgumentsFromBatch() + + auth.Nonce = big.NewInt(nonce) + auth.Value = big.NewInt(0) + auth.GasLimit = executor.transferGasLimitBase + uint64(len(tokens))*executor.transferGasLimitForEach + auth.Context = ctx + auth.GasPrice = gasPrice + + tx, err := executor.ethereumChainWrapper.ExecuteTransfer(auth, tokens, recipients, amounts, depositNonces, batchNonce, signatures) + if err != nil { + return err + } + + txHash := tx.Hash().String() + executor.logger.Info("Executed transfer transaction", "batchID", executor.batch.BatchID, "hash", txHash) + + return nil +} + +func (executor *migrationBatchExecutor) getNonce(ctx context.Context, fromAddress common.Address) (int64, error) { + blockNonce, err := executor.ethereumChainWrapper.BlockNumber(ctx) + if err != nil { + return 0, fmt.Errorf("%w in getNonce, BlockNumber call", err) + } + + nonce, err := executor.ethereumChainWrapper.NonceAt(ctx, fromAddress, big.NewInt(int64(blockNonce))) + + return int64(nonce), err +} + +func (executor *migrationBatchExecutor) extractArgumentsFromBatch() ( + tokens []common.Address, + recipients []common.Address, + amounts []*big.Int, + nonces []*big.Int, + batchNonce *big.Int, +) { + tokens = make([]common.Address, 0, len(executor.batch.DepositsInfo)) + recipients = make([]common.Address, 0, len(executor.batch.DepositsInfo)) + amounts = make([]*big.Int, 0, len(executor.batch.DepositsInfo)) + nonces = make([]*big.Int, 0, len(executor.batch.DepositsInfo)) + batchNonce = big.NewInt(0).SetUint64(executor.batch.BatchID) + + newSafeContractAddress := common.HexToAddress(executor.batch.NewSafeContractAddress) + for _, deposit := range executor.batch.DepositsInfo { + tokens = append(tokens, deposit.ContractAddress) + recipients = append(recipients, newSafeContractAddress) + amounts = append(amounts, deposit.Amount) + nonces = append(nonces, big.NewInt(0).SetUint64(deposit.DepositNonce)) + } + + return +} + +func (executor *migrationBatchExecutor) checkRelayersSigsAndQuorum(relayers []common.Address, quorum *big.Int) ([][]byte, error) { + sameMessageHashSignatures := executor.getSameMessageHashSignatures() + validSignatures := executor.getValidSignatures(sameMessageHashSignatures) + return executor.checkQuorum(relayers, quorum, validSignatures) +} + +func (executor *migrationBatchExecutor) getSameMessageHashSignatures() []SignatureInfo { + filtered := make([]SignatureInfo, 0, len(executor.signatures)) + expectedMessageHash := executor.batch.MessageHash.String() + for _, sigInfo := range executor.signatures { + if sigInfo.MessageHash != expectedMessageHash { + executor.logger.Warn("found a signature info that was not carried on the same message hash", + "local message hash", executor.batch.MessageHash.String(), + "address", sigInfo.Address, "message hash", sigInfo.MessageHash) + + continue + } + + filtered = append(filtered, sigInfo) + } + + return filtered +} + +func (executor *migrationBatchExecutor) getValidSignatures(provided []SignatureInfo) []SignatureInfo { + filtered := make([]SignatureInfo, 0, len(provided)) + for _, sigInfo := range provided { + hash := common.HexToHash(sigInfo.MessageHash) + sig, err := hex.DecodeString(sigInfo.Signature) + if err != nil { + executor.logger.Warn("found a non valid signature info (can not unhex the signature)", + "address", sigInfo.Address, "message hash", sigInfo.MessageHash, "signature", sigInfo.Signature, "error", err) + continue + } + + err = verifySignature(hash, sig, common.HexToAddress(sigInfo.Address)) + if err != nil { + executor.logger.Warn("found a non valid signature info", + "address", sigInfo.Address, "message hash", sigInfo.MessageHash, "signature", sigInfo.Signature, "error", err) + continue + } + + filtered = append(filtered, sigInfo) + } + + return filtered +} + +func verifySignature(messageHash common.Hash, signature []byte, address common.Address) error { + pkBytes, err := crypto.Ecrecover(messageHash.Bytes(), signature) + if err != nil { + return err + } + + pk, err := crypto.UnmarshalPubkey(pkBytes) + if err != nil { + return err + } + + addressFromPk := crypto.PubkeyToAddress(*pk) + if addressFromPk.String() != address.String() { + // we need to check that the recovered public key matched the one provided in order to make sure + // that the signature, hash and public key match + return errInvalidSignature + } + + if len(signature) > ethSignatureSize { + // signatures might contain the recovery byte + signature = signature[:ethSignatureSize] + } + + sigOk := crypto.VerifySignature(pkBytes, messageHash.Bytes(), signature) + if !sigOk { + return errInvalidSignature + } + + return nil +} + +func (executor *migrationBatchExecutor) checkQuorum(relayers []common.Address, quorum *big.Int, signatures []SignatureInfo) ([][]byte, error) { + whitelistedRelayers := make(map[common.Address]SignatureInfo) + + for _, sigInfo := range signatures { + if !isWhitelistedRelayer(sigInfo, relayers) { + executor.logger.Warn("found a non whitelisted relayer", + "address", sigInfo.Address) + continue + } + + relayerAddress := common.HexToAddress(sigInfo.Address) + _, found := whitelistedRelayers[relayerAddress] + if found { + executor.logger.Warn("found a multiple relayer sig info, ignoring", + "address", sigInfo.Address) + continue + } + + whitelistedRelayers[relayerAddress] = sigInfo + } + + validSignatures := make([][]byte, 0, len(whitelistedRelayers)) + for _, sigInfo := range whitelistedRelayers { + sig, err := hex.DecodeString(sigInfo.Signature) + if err != nil { + return nil, fmt.Errorf("internal error: %w while decoding this string %s that should have been hexed encoded", err, sigInfo.Signature) + } + + validSignatures = append(validSignatures, sig) + executor.logger.Info("valid signature recorded for whitelisted relayer", "relayer", sigInfo.Address) + } + + if uint64(len(validSignatures)) < quorum.Uint64() { + return nil, fmt.Errorf("%w: minimum %d, got %d", errQuorumNotReached, quorum.Uint64(), len(validSignatures)) + } + + return validSignatures, nil +} + +func isWhitelistedRelayer(sigInfo SignatureInfo, relayers []common.Address) bool { + relayerAddress := common.HexToAddress(sigInfo.Address) + for _, relayer := range relayers { + if bytes.Equal(relayer.Bytes(), relayerAddress.Bytes()) { + return true + } + } + + return false +} diff --git a/executors/ethereum/migrationBatchExecutor_test.go b/executors/ethereum/migrationBatchExecutor_test.go new file mode 100644 index 00000000..dcc89094 --- /dev/null +++ b/executors/ethereum/migrationBatchExecutor_test.go @@ -0,0 +1,711 @@ +package ethereum + +import ( + "context" + "crypto/ecdsa" + "crypto/rand" + "encoding/hex" + "errors" + "math/big" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var log = logger.GetOrCreate("executors/ethereum_test") + +func createMockArgsMigrationBatchExecutor() ArgsMigrationBatchExecutor { + return ArgsMigrationBatchExecutor{ + EthereumChainWrapper: &bridge.EthereumClientWrapperStub{}, + CryptoHandler: &bridge.CryptoHandlerStub{}, + Batch: BatchInfo{}, + Signatures: make([]SignatureInfo, 0), + Logger: log, + GasHandler: &testsCommon.GasHandlerStub{}, + TransferGasLimitBase: 100, + TransferGasLimitForEach: 10, + } +} + +func createPrivateKeys(tb testing.TB, num int) []*ecdsa.PrivateKey { + keys := make([]*ecdsa.PrivateKey, 0, num) + + for i := 0; i < num; i++ { + skBytes := make([]byte, 32) + _, _ = rand.Read(skBytes) + + privateKey, err := ethCrypto.HexToECDSA(hex.EncodeToString(skBytes)) + require.Nil(tb, err) + + keys = append(keys, privateKey) + } + + return keys +} + +func sign(tb testing.TB, sk *ecdsa.PrivateKey, msgHash common.Hash) []byte { + sig, err := ethCrypto.Sign(msgHash.Bytes(), sk) + require.Nil(tb, err) + + return sig +} + +func TestNewMigrationBatchExecutor(t *testing.T) { + t.Parallel() + + t.Run("nil Ethereum chain wrapper should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.EthereumChainWrapper = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilEthereumChainWrapper, err) + }) + t.Run("nil crypto handler should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.CryptoHandler = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilCryptoHandler, err) + }) + t.Run("nil logger should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Logger = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilLogger, err) + }) + t.Run("nil gas handler should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.GasHandler = nil + + executor, err := NewMigrationBatchExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilGasHandler, err) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + executor, err := NewMigrationBatchExecutor(args) + assert.NotNil(t, executor) + assert.Nil(t, err) + }) +} + +func TestMigrationBatchExecutor_checkRelayersSigsAndQuorum(t *testing.T) { + t.Parallel() + + t.Run("quorum not satisfied should error", func(t *testing.T) { + t.Parallel() + + privateKeys := createPrivateKeys(t, 3) + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + + signatures := []SignatureInfo{ + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + } + + args := createMockArgsMigrationBatchExecutor() + args.Batch = BatchInfo{ + MessageHash: testMsgHash, + } + args.Signatures = signatures + + executor, _ := NewMigrationBatchExecutor(args) + whitelistedRelayers := []common.Address{ + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + } + + verifiedSigs, err := executor.checkRelayersSigsAndQuorum(whitelistedRelayers, big.NewInt(4)) + assert.ErrorIs(t, err, errQuorumNotReached) + assert.Contains(t, err.Error(), "minimum 4, got 3") + assert.Empty(t, verifiedSigs) + }) + t.Run("should work with wrong sig info elements", func(t *testing.T) { + t.Parallel() + + privateKeys := createPrivateKeys(t, 6) + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + wrongMsgHash := common.HexToHash(strings.Repeat("2", 64)) + + correctSigForFifthElement := hex.EncodeToString(sign(t, privateKeys[5], testMsgHash)) + signatures := []SignatureInfo{ + // wrong message hash + { + Address: ethCrypto.PubkeyToAddress(privateKeys[4].PublicKey).String(), + MessageHash: wrongMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[4], wrongMsgHash)), + }, + // wrong signature: another message hash + { + Address: ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[5], wrongMsgHash)), + }, + // wrong signature: not a hex string + { + Address: ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: "not a hex string", + }, + // wrong signature: malformed signature + { + Address: ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: strings.Replace(correctSigForFifthElement, "1", "2", -1), + }, + // repeated good sig[1] + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + // good sigs + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[3].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[3], testMsgHash)), + }, + } + + args := createMockArgsMigrationBatchExecutor() + args.Batch = BatchInfo{ + MessageHash: testMsgHash, + } + args.Signatures = signatures + + executor, _ := NewMigrationBatchExecutor(args) + whitelistedRelayers := []common.Address{ + // all but private key[3] are whitelisted + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[4].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[5].PublicKey), + } + + verifiedSigs, err := executor.checkRelayersSigsAndQuorum(whitelistedRelayers, big.NewInt(3)) + assert.Nil(t, err) + assert.Equal(t, 3, len(verifiedSigs)) + }) + t.Run("should work with correct sig elements", func(t *testing.T) { + t.Parallel() + + privateKeys := createPrivateKeys(t, 3) + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + + signatures := []SignatureInfo{ + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + } + + args := createMockArgsMigrationBatchExecutor() + args.Batch = BatchInfo{ + MessageHash: testMsgHash, + } + args.Signatures = signatures + + executor, _ := NewMigrationBatchExecutor(args) + whitelistedRelayers := []common.Address{ + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + } + + verifiedSigs, err := executor.checkRelayersSigsAndQuorum(whitelistedRelayers, big.NewInt(3)) + assert.Nil(t, err) + assert.Equal(t, 3, len(verifiedSigs)) + }) +} + +func TestMigrationBatchExecutor_ExecuteTransfer(t *testing.T) { + t.Parallel() + + testMsgHash := common.HexToHash(strings.Repeat("1", 64)) + newSafeContractAddress := common.HexToAddress("A6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c") + batchInfo := BatchInfo{ + OldSafeContractAddress: "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c", + NewSafeContractAddress: newSafeContractAddress.String(), + BatchID: 4432, + MessageHash: testMsgHash, + DepositsInfo: []*DepositInfo{ + { + DepositNonce: 37, + Token: "tkn1", + ContractAddressString: common.BytesToAddress(tkn1Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn1Erc20Address), + Amount: big.NewInt(112), + AmountString: "112", + }, + { + DepositNonce: 38, + Token: "tkn2", + ContractAddressString: common.BytesToAddress(tkn2Erc20Address).String(), + ContractAddress: common.BytesToAddress(tkn2Erc20Address), + Amount: big.NewInt(113), + AmountString: "113", + }, + }, + } + + privateKeys := createPrivateKeys(t, 3) + signatures := []SignatureInfo{ + { + Address: ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[0], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[1], testMsgHash)), + }, + { + Address: ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey).String(), + MessageHash: testMsgHash.String(), + Signature: hex.EncodeToString(sign(t, privateKeys[2], testMsgHash)), + }, + } + + whitelistedRelayers := []common.Address{ + ethCrypto.PubkeyToAddress(privateKeys[0].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[1].PublicKey), + ethCrypto.PubkeyToAddress(privateKeys[2].PublicKey), + } + testBlockNumber := uint64(1000000) + senderNonce := uint64(3377) + testChainId := big.NewInt(2222) + testGasPrice := big.NewInt(112233) + + expectedTokens := []common.Address{ + common.BytesToAddress(tkn1Erc20Address), + common.BytesToAddress(tkn2Erc20Address), + } + expectedRecipients := []common.Address{ + newSafeContractAddress, + newSafeContractAddress, + } + expectedAmounts := []*big.Int{ + big.NewInt(112), + big.NewInt(113), + } + expectedNonces := []*big.Int{ + big.NewInt(37), + big.NewInt(38), + } + expectedSignatures := make([][]byte, 0, len(signatures)) + for _, sigInfo := range signatures { + sig, err := hex.DecodeString(sigInfo.Signature) + require.Nil(t, err) + expectedSignatures = append(expectedSignatures, sig) + } + expectedErr := errors.New("expected error") + + t.Run("is paused query errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + IsPausedCalled: func(ctx context.Context) (bool, error) { + return false, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("is paused should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + IsPausedCalled: func(ctx context.Context) (bool, error) { + return true, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, errMultisigContractPaused) + }) + t.Run("get relayers errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return nil, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.Equal(t, expectedErr, err) + }) + t.Run("get quorum errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return nil, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.Equal(t, expectedErr, err) + }) + t.Run("checking the signatures and relayers errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures // no whitelisted relayers + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, errQuorumNotReached) + }) + t.Run("get block errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + return 0, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("get nonce errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + NonceAtCalled: func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + return 0, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("chain ID errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ChainIDCalled: func(ctx context.Context) (*big.Int, error) { + return nil, expectedErr + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("create keyed transactor errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return nil, expectedErr + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("get gas price errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Fail(t, "should have not called execute transfer") + + return nil, nil + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return &bind.TransactOpts{}, nil + }, + } + args.GasHandler = &testsCommon.GasHandlerStub{ + GetCurrentGasPriceCalled: func() (*big.Int, error) { + return nil, expectedErr + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("execute transfer errors should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + return nil, expectedErr + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + return &bind.TransactOpts{}, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + err := executor.ExecuteTransfer(context.Background()) + assert.ErrorIs(t, err, expectedErr) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsMigrationBatchExecutor() + args.Batch = batchInfo + args.Signatures = signatures + executeWasCalled := false + args.EthereumChainWrapper = &bridge.EthereumClientWrapperStub{ + GetRelayersCalled: func(ctx context.Context) ([]common.Address, error) { + return whitelistedRelayers, nil + }, + BlockNumberCalled: func(ctx context.Context) (uint64, error) { + return testBlockNumber, nil + }, + NonceAtCalled: func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + assert.Equal(t, big.NewInt(0).SetUint64(testBlockNumber), blockNumber) + return senderNonce, nil + }, + ChainIDCalled: func(ctx context.Context) (*big.Int, error) { + return testChainId, nil + }, + QuorumCalled: func(ctx context.Context) (*big.Int, error) { + return big.NewInt(3), nil + }, + ExecuteTransferCalled: func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) { + assert.Equal(t, big.NewInt(0).SetUint64(senderNonce), opts.Nonce) + assert.Equal(t, big.NewInt(0), opts.Value) + assert.Equal(t, uint64(100+10+10), opts.GasLimit) // base + 2 deposits + assert.Equal(t, testGasPrice, opts.GasPrice) + assert.Equal(t, expectedTokens, tokens) + assert.Equal(t, expectedRecipients, recipients) + assert.Equal(t, expectedAmounts, amounts) + assert.Equal(t, expectedNonces, nonces) + assert.Equal(t, big.NewInt(4432), batchNonce) + assert.ElementsMatch(t, expectedSignatures, signatures) + executeWasCalled = true + + txData := &types.LegacyTx{ + Nonce: 0, + Data: []byte("mocked data"), + } + tx := types.NewTx(txData) + + return tx, nil + }, + } + args.GasHandler = &testsCommon.GasHandlerStub{ + GetCurrentGasPriceCalled: func() (*big.Int, error) { + return testGasPrice, nil + }, + } + args.CryptoHandler = &bridge.CryptoHandlerStub{ + CreateKeyedTransactorCalled: func(chainId *big.Int) (*bind.TransactOpts, error) { + assert.Equal(t, testChainId, chainId) + return &bind.TransactOpts{}, nil + }, + } + + executor, _ := NewMigrationBatchExecutor(args) + + err := executor.ExecuteTransfer(context.Background()) + assert.Nil(t, err) + assert.True(t, executeWasCalled) + }) +} diff --git a/executors/ethereum/signatures.go b/executors/ethereum/signatures.go new file mode 100644 index 00000000..54fadf75 --- /dev/null +++ b/executors/ethereum/signatures.go @@ -0,0 +1,63 @@ +package ethereum + +import ( + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + + logger "github.com/multiversx/mx-chain-logger-go" +) + +const filesPattern = "0x*.json" + +// LoadAllSignatures can load all valid signatures from the specified directory +func LoadAllSignatures(logger logger.Logger, path string) []SignatureInfo { + filesContents, err := getAllFilesContents(path) + if err != nil { + logger.Warn(err.Error()) + return make([]SignatureInfo, 0) + } + + signatures := make([]SignatureInfo, 0, len(filesContents)) + for _, buff := range filesContents { + sigInfo := &SignatureInfo{} + err = json.Unmarshal(buff, sigInfo) + if err != nil { + logger.Warn("error unmarshalling to json", "error", err) + continue + } + + signatures = append(signatures, *sigInfo) + } + + return signatures +} + +func getAllFilesContents(dirPath string) ([][]byte, error) { + dirInfo, err := os.ReadDir(dirPath) + if err != nil { + return nil, fmt.Errorf("%w while fetching %s directory contents", err, dirPath) + } + + data := make([][]byte, 0, len(dirInfo)) + for _, di := range dirInfo { + if di.IsDir() { + continue + } + matched, errMatched := filepath.Match(filesPattern, di.Name()) + if errMatched != nil || !matched { + continue + } + + buff, errRead := os.ReadFile(path.Join(dirPath, di.Name())) + if errRead != nil { + continue + } + + data = append(data, buff) + } + + return data, nil +} diff --git a/executors/ethereum/signatures_test.go b/executors/ethereum/signatures_test.go new file mode 100644 index 00000000..de284867 --- /dev/null +++ b/executors/ethereum/signatures_test.go @@ -0,0 +1,29 @@ +package ethereum + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLoadAllSignatures(t *testing.T) { + t.Parallel() + + dirPath := "testdata" + sigs := LoadAllSignatures(log, dirPath) + + expectedSigs := []SignatureInfo{ + { + Address: "0x3FE464Ac5aa562F7948322F92020F2b668D543d8", + MessageHash: "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + Signature: "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201", + }, + { + Address: "0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + MessageHash: "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + Signature: "111222333", + }, + } + + assert.Equal(t, expectedSigs, sigs) +} diff --git a/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json new file mode 100755 index 00000000..9cc62245 --- /dev/null +++ b/executors/ethereum/testdata/0x3FE464Ac5aa562F7948322F92020F2b668D543d8-2024-09-05-15-34-44.json @@ -0,0 +1,5 @@ +{ + "Address": "0x3FE464Ac5aa562F7948322F92020F2b668D543d8", + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "Signature": "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201" +} diff --git a/executors/ethereum/testdata/0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json new file mode 100755 index 00000000..b446e750 --- /dev/null +++ b/executors/ethereum/testdata/0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c-2024-09-05-15-34-44.json @@ -0,0 +1,5 @@ +{ + "Address": "0xA6504Cc508889bbDBd4B748aFf6EA6b5D0d2684c", + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "Signature": "111222333" +} diff --git a/executors/ethereum/testdata/0xDirectory/dummy b/executors/ethereum/testdata/0xDirectory/dummy new file mode 100644 index 00000000..e977405b --- /dev/null +++ b/executors/ethereum/testdata/0xDirectory/dummy @@ -0,0 +1 @@ +dummy, empty file diff --git a/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json b/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json new file mode 100755 index 00000000..eab6c718 --- /dev/null +++ b/executors/ethereum/testdata/0xbad-2024-09-05-15-34-44.json @@ -0,0 +1,4 @@ +{ + "Address": "0x3F", + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "Signature": "74a91b07c796d1fcb18517994f4b71fe5f1c10317e95c609eabac9e7dbfc517c3e9c402585774a7129e4b5bbfade40647afc52bb38cb2a4b63163cbe2577eee201 diff --git a/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json b/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json new file mode 100755 index 00000000..450dae06 --- /dev/null +++ b/executors/ethereum/testdata/migration-2024-09-05-15-34-44.json @@ -0,0 +1,14 @@ +{ + "OldSafeContractAddress": "0x92A26975433A61CF1134802586aa669bAB8B69f3", + "NewSafeContractAddress": "0x1Ff78EB04d44a803E73c44FEf8790c5cAbD14596", + "BatchID": 3548, + "MessageHash": "0xc5b805c73d01e35e10a27a4cab86f096c976f0910ae23f5c6b307a823f0c49fb", + "DepositsInfo": [ + { + "DepositNonce": 4652, + "Token": "ETHUSDC-220753", + "ContractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "Amount": "7086984513581" + } + ] +} diff --git a/executors/multiversx/errors.go b/executors/multiversx/errors.go new file mode 100644 index 00000000..4b6339e9 --- /dev/null +++ b/executors/multiversx/errors.go @@ -0,0 +1,18 @@ +package multiversx + +import "errors" + +var ( + errInvalidNumberOfResponseLines = errors.New("invalid number of responses") + errNilProxy = errors.New("nil proxy") + errNilCodec = errors.New("nil codec") + errNilFilter = errors.New("nil filter") + errNilLogger = errors.New("nil logger") + errNilNonceTxHandler = errors.New("nil nonce transaction handler") + errNilPrivateKey = errors.New("nil private key") + errNilSingleSigner = errors.New("nil single signer") + errInvalidValue = errors.New("invalid value") + errNilCloseAppChannel = errors.New("nil close application channel") + errTransactionFailed = errors.New("transaction failed") + errGasLimitIsLessThanAbsoluteMinimum = errors.New("provided gas limit is less than absolute minimum required") +) diff --git a/executors/multiversx/filters/errors.go b/executors/multiversx/filters/errors.go new file mode 100644 index 00000000..c2966b4a --- /dev/null +++ b/executors/multiversx/filters/errors.go @@ -0,0 +1,10 @@ +package filters + +import "errors" + +var ( + errNilLogger = errors.New("nil logger") + errNoItemsAllowed = errors.New("no items allowed") + errUnsupportedMarker = errors.New("unsupported marker") + errMissingEthPrefix = errors.New("missing Ethereum address prefix") +) diff --git a/executors/multiversx/filters/pendingOperationFilter.go b/executors/multiversx/filters/pendingOperationFilter.go new file mode 100644 index 00000000..d767abaf --- /dev/null +++ b/executors/multiversx/filters/pendingOperationFilter.go @@ -0,0 +1,220 @@ +package filters + +import ( + "fmt" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/parsers" + "github.com/multiversx/mx-chain-core-go/core/check" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-sdk-go/data" +) + +const ( + wildcardString = "*" + emptyString = "" + ethAddressPrefix = "0x" +) + +var ethWildcardString = "" + +func init() { + var ethAddressWildcard = common.Address{} + ethAddressWildcard.SetBytes([]byte(wildcardString)) + ethWildcardString = ethAddressWildcard.String() +} + +type pendingOperationFilter struct { + allowedEthAddresses []string + deniedEthAddresses []string + allowedMvxAddresses []string + deniedMvxAddresses []string + allowedTokens []string + deniedTokens []string +} + +// NewPendingOperationFilter creates a new instance of type pendingOperationFilter +func NewPendingOperationFilter(cfg config.PendingOperationsFilterConfig, log logger.Logger) (*pendingOperationFilter, error) { + if check.IfNil(log) { + return nil, errNilLogger + } + if len(cfg.AllowedMvxAddresses)+len(cfg.AllowedEthAddresses)+len(cfg.AllowedTokens) == 0 { + return nil, errNoItemsAllowed + } + + filter := &pendingOperationFilter{} + err := filter.parseConfigs(cfg) + if err != nil { + return nil, err + } + + err = filter.checkLists() + if err != nil { + return nil, err + } + + log.Info("NewPendingOperationFilter config options", + "DeniedEthAddresses", strings.Join(filter.deniedEthAddresses, ", "), + "DeniedMvxAddresses", strings.Join(filter.deniedMvxAddresses, ", "), + "DeniedTokens", strings.Join(filter.deniedTokens, ", "), + "AllowedEthAddresses", strings.Join(filter.allowedEthAddresses, ", "), + "AllowedMvxAddresses", strings.Join(filter.allowedMvxAddresses, ", "), + "AllowedTokens", strings.Join(filter.allowedTokens, ", "), + ) + + return filter, nil +} + +func (filter *pendingOperationFilter) parseConfigs(cfg config.PendingOperationsFilterConfig) error { + var err error + + // denied lists do not support wildcard items + filter.deniedEthAddresses, err = parseList(cfg.DeniedEthAddresses, wildcardString) + if err != nil { + return fmt.Errorf("%w in list DeniedEthAddresses", err) + } + + filter.deniedMvxAddresses, err = parseList(cfg.DeniedMvxAddresses, wildcardString) + if err != nil { + return fmt.Errorf("%w in list DeniedMvxAddresses", err) + } + + filter.deniedTokens, err = parseList(cfg.DeniedTokens, wildcardString) + if err != nil { + return fmt.Errorf("%w in list DeniedTokens", err) + } + + // allowed lists do not support empty items + filter.allowedEthAddresses, err = parseList(cfg.AllowedEthAddresses, emptyString) + if err != nil { + return fmt.Errorf("%w in list AllowedEthAddresses", err) + } + + filter.allowedMvxAddresses, err = parseList(cfg.AllowedMvxAddresses, emptyString) + if err != nil { + return fmt.Errorf("%w in list AllowedMvxAddresses", err) + } + + filter.allowedTokens, err = parseList(cfg.AllowedTokens, emptyString) + if err != nil { + return fmt.Errorf("%w in list AllowedTokens", err) + } + + return nil +} + +func parseList(list []string, unsupportedMarker string) ([]string, error) { + newList := make([]string, 0, len(list)) + for index, item := range list { + item = strings.ToLower(item) + item = strings.Trim(item, "\r\n \t") + if item == unsupportedMarker { + return nil, fmt.Errorf("%w %s on item at index %d", errUnsupportedMarker, unsupportedMarker, index) + } + + newList = append(newList, item) + } + + return newList, nil +} + +func (filter *pendingOperationFilter) checkLists() error { + err := filter.checkList(filter.allowedEthAddresses, checkEthItemValid) + if err != nil { + return fmt.Errorf("%w in list AllowedEthAddresses", err) + } + + err = filter.checkList(filter.deniedEthAddresses, checkEthItemValid) + if err != nil { + return fmt.Errorf("%w in list DeniedEthAddresses", err) + } + + err = filter.checkList(filter.allowedMvxAddresses, checkMvxItemValid) + if err != nil { + return fmt.Errorf("%w in list AllowedMvxAddresses", err) + } + + err = filter.checkList(filter.deniedMvxAddresses, checkMvxItemValid) + if err != nil { + return fmt.Errorf("%w in list DeniedMvxAddresses", err) + } + + return nil +} + +func (filter *pendingOperationFilter) checkList(list []string, checkItem func(item string) error) error { + for index, item := range list { + if item == wildcardString { + continue + } + + err := checkItem(item) + if err != nil { + return fmt.Errorf("%w on item at index %d", err, index) + } + } + + return nil +} + +func checkMvxItemValid(item string) error { + _, errNewAddr := data.NewAddressFromBech32String(item) + return errNewAddr +} + +func checkEthItemValid(item string) error { + if !strings.HasPrefix(item, ethAddressPrefix) { + return fmt.Errorf("%w (missing %s prefix)", errMissingEthPrefix, ethAddressPrefix) + } + + return nil +} + +// ShouldExecute returns true if the To, From or token are not denied and allowed +func (filter *pendingOperationFilter) ShouldExecute(callData parsers.ProxySCCompleteCallData) bool { + if check.IfNil(callData.To) { + return false + } + + toAddress, err := callData.To.AddressAsBech32String() + if err != nil { + return false + } + + isSpecificallyDenied := filter.stringExistsInList(callData.From.String(), filter.deniedEthAddresses, ethWildcardString) || + filter.stringExistsInList(toAddress, filter.deniedMvxAddresses, wildcardString) || + filter.stringExistsInList(callData.Token, filter.deniedTokens, wildcardString) + if isSpecificallyDenied { + return false + } + + isAllowed := filter.stringExistsInList(callData.From.String(), filter.allowedEthAddresses, ethWildcardString) || + filter.stringExistsInList(toAddress, filter.allowedMvxAddresses, wildcardString) || + filter.stringExistsInList(callData.Token, filter.allowedTokens, wildcardString) + + return isAllowed +} + +func (filter *pendingOperationFilter) stringExistsInList(needle string, haystack []string, wildcardMarker string) bool { + needle = strings.ToLower(needle) + wildcardMarker = strings.ToLower(wildcardMarker) + + for _, item := range haystack { + if item == wildcardMarker { + return true + } + + if item == needle { + return true + } + } + + return false +} + +// IsInterfaceNil returns true if there is no value under the interface +func (filter *pendingOperationFilter) IsInterfaceNil() bool { + return filter == nil +} diff --git a/executors/multiversx/filters/pendingOperationFilter_test.go b/executors/multiversx/filters/pendingOperationFilter_test.go new file mode 100644 index 00000000..5708e26c --- /dev/null +++ b/executors/multiversx/filters/pendingOperationFilter_test.go @@ -0,0 +1,356 @@ +package filters + +import ( + "encoding/hex" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/parsers" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/assert" +) + +const ethTestAddress1 = "0x880ec53af800b5cd051531672ef4fc4de233bd5d" +const ethTestAddress2 = "0x880ebbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" +const mvxTestAddress1 = "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e" +const mvxTestAddress2 = "erd1qqqqqqqqqqqqqpgqptqsx2llrwh4phaf42lwwxez2hzeulxwanaqg7kgky" + +var testLog = logger.GetOrCreate("filters") +var ethTestAddress1Bytes, _ = hex.DecodeString(ethTestAddress1[2:]) + +func createTestConfig() config.PendingOperationsFilterConfig { + return config.PendingOperationsFilterConfig{ + DeniedEthAddresses: nil, + AllowedEthAddresses: []string{"*"}, + + DeniedMvxAddresses: nil, + AllowedMvxAddresses: []string{"*"}, + + DeniedTokens: nil, + AllowedTokens: []string{"*"}, + } +} + +func TestNewPendingOperationFilter(t *testing.T) { + t.Parallel() + + t.Run("nil logger should error", func(t *testing.T) { + t.Parallel() + + filter, err := NewPendingOperationFilter(createTestConfig(), nil) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errNilLogger) + }) + t.Run("empty config should error", func(t *testing.T) { + t.Parallel() + + filter, err := NewPendingOperationFilter(config.PendingOperationsFilterConfig{}, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errNoItemsAllowed) + }) + t.Run("denied eth list contains wildcard should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedEthAddresses = []string{" * "} + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errUnsupportedMarker) + assert.Contains(t, err.Error(), "on item at index 0 in list DeniedEthAddresses") + }) + t.Run("denied mvx list contains wildcard should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedMvxAddresses = []string{" * "} + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errUnsupportedMarker) + assert.Contains(t, err.Error(), "on item at index 0 in list DeniedMvxAddresses") + }) + t.Run("denied tokens list contains wildcard should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedTokens = []string{" * "} + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errUnsupportedMarker) + assert.Contains(t, err.Error(), "on item at index 0 in list DeniedTokens") + }) + t.Run("allowed eth list contains empty string should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedEthAddresses = append(cfg.AllowedEthAddresses, " ") + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errUnsupportedMarker) + assert.Contains(t, err.Error(), "on item at index 1 in list AllowedEthAddresses") + }) + t.Run("allowed mvx list contains empty string should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedMvxAddresses = append(cfg.AllowedMvxAddresses, " ") + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errUnsupportedMarker) + assert.Contains(t, err.Error(), "on item at index 1 in list AllowedMvxAddresses") + }) + t.Run("allowed tokens list contains empty string should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedTokens = append(cfg.AllowedTokens, " ") + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errUnsupportedMarker) + assert.Contains(t, err.Error(), "on item at index 1 in list AllowedTokens") + }) + t.Run("invalid address in AllowedEthAddresses should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedEthAddresses = append(cfg.AllowedEthAddresses, "invalid address") + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errMissingEthPrefix) + assert.Contains(t, err.Error(), "on item at index 1 in list AllowedEthAddresses") + }) + t.Run("invalid address in DeniedEthAddresses should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedEthAddresses = append(cfg.DeniedEthAddresses, "invalid address") + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.ErrorIs(t, err, errMissingEthPrefix) + assert.Contains(t, err.Error(), "on item at index 0 in list DeniedEthAddresses") + }) + t.Run("invalid address in AllowedMvxAddresses should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedMvxAddresses = append(cfg.AllowedMvxAddresses, "invalid address") + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "on item at index 1 in list AllowedMvxAddresses") + }) + t.Run("invalid address in DeniedMvxAddresses should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedMvxAddresses = append(cfg.DeniedMvxAddresses, "invalid address") + + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.Nil(t, filter) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "on item at index 0 in list DeniedMvxAddresses") + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedEthAddresses = append(cfg.AllowedMvxAddresses, ethTestAddress1) + cfg.DeniedEthAddresses = append(cfg.DeniedEthAddresses, ethTestAddress1) + cfg.AllowedMvxAddresses = append(cfg.AllowedMvxAddresses, mvxTestAddress1) + cfg.DeniedMvxAddresses = append(cfg.DeniedMvxAddresses, mvxTestAddress1) + filter, err := NewPendingOperationFilter(cfg, testLog) + assert.NotNil(t, filter) + assert.Nil(t, err) + }) +} + +func TestPendingOperationFilter_IsInterfaceNil(t *testing.T) { + t.Parallel() + + var instance *pendingOperationFilter + assert.True(t, instance.IsInterfaceNil()) + + instance = &pendingOperationFilter{} + assert.False(t, instance.IsInterfaceNil()) +} + +func TestPendingOperationFilter_ShouldExecute(t *testing.T) { + t.Parallel() + + t.Run("nil callData.To should return false", func(t *testing.T) { + t.Parallel() + + callData := parsers.ProxySCCompleteCallData{ + To: nil, + } + + cfg := createTestConfig() + filter, _ := NewPendingOperationFilter(cfg, testLog) + + assert.False(t, filter.ShouldExecute(callData)) + }) + t.Run("callData.To is not a valid Mvx address should return false", func(t *testing.T) { + t.Parallel() + + callData := parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes([]byte{0x1, 0x2}), + } + + cfg := createTestConfig() + filter, _ := NewPendingOperationFilter(cfg, testLog) + + assert.False(t, filter.ShouldExecute(callData)) + }) + t.Run("eth address", func(t *testing.T) { + t.Parallel() + + callData := parsers.ProxySCCompleteCallData{ + From: common.BytesToAddress(ethTestAddress1Bytes), + } + callData.To, _ = data.NewAddressFromBech32String(mvxTestAddress1) + t.Run("is denied should return false", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedEthAddresses = []string{ethTestAddress1} + cfg.AllowedEthAddresses = []string{ethTestAddress1} + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + + cfg.AllowedEthAddresses = []string{"*"} + filter, _ = NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + }) + t.Run("is not denied but allowed should return true", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedEthAddresses = []string{ethTestAddress1} + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.True(t, filter.ShouldExecute(callData)) + + cfg.AllowedEthAddresses = []string{"*"} + filter, _ = NewPendingOperationFilter(cfg, testLog) + assert.True(t, filter.ShouldExecute(callData)) + }) + t.Run("is not denied but not allowed should return false", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedEthAddresses = []string{ethTestAddress2} + cfg.AllowedTokens = nil + cfg.AllowedMvxAddresses = nil + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + }) + }) + t.Run("mvx address", func(t *testing.T) { + t.Parallel() + + callData := parsers.ProxySCCompleteCallData{ + From: common.BytesToAddress(ethTestAddress1Bytes), + } + callData.To, _ = data.NewAddressFromBech32String(mvxTestAddress1) + t.Run("is denied should return false", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedMvxAddresses = []string{mvxTestAddress1} + cfg.AllowedMvxAddresses = []string{mvxTestAddress1} + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + + cfg.AllowedMvxAddresses = []string{"*"} + filter, _ = NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + }) + t.Run("is not denied but allowed should return true", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedMvxAddresses = []string{mvxTestAddress1} + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.True(t, filter.ShouldExecute(callData)) + + cfg.AllowedMvxAddresses = []string{"*"} + filter, _ = NewPendingOperationFilter(cfg, testLog) + assert.True(t, filter.ShouldExecute(callData)) + }) + t.Run("is not denied but not allowed should return false", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedMvxAddresses = []string{mvxTestAddress2} + cfg.AllowedTokens = nil + cfg.AllowedEthAddresses = nil + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + }) + }) + t.Run("tokens", func(t *testing.T) { + t.Parallel() + + token1 := "tkn1" + token2 := "tkn2" + callData := parsers.ProxySCCompleteCallData{ + From: common.BytesToAddress(ethTestAddress1Bytes), + Token: token1, + } + callData.To, _ = data.NewAddressFromBech32String(mvxTestAddress1) + + t.Run("is denied should return false", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.DeniedTokens = []string{token1} + cfg.AllowedTokens = []string{token1} + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + + cfg.AllowedTokens = []string{"*"} + filter, _ = NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + }) + t.Run("is not denied but allowed should return true", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedTokens = []string{token1} + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.True(t, filter.ShouldExecute(callData)) + + cfg.AllowedTokens = []string{"*"} + filter, _ = NewPendingOperationFilter(cfg, testLog) + assert.True(t, filter.ShouldExecute(callData)) + }) + t.Run("is not denied but not allowed should return false", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfig() + cfg.AllowedTokens = []string{token2} + cfg.AllowedMvxAddresses = nil + cfg.AllowedEthAddresses = nil + + filter, _ := NewPendingOperationFilter(cfg, testLog) + assert.False(t, filter.ShouldExecute(callData)) + }) + }) +} diff --git a/executors/multiversx/interface.go b/executors/multiversx/interface.go new file mode 100644 index 00000000..c83f1cda --- /dev/null +++ b/executors/multiversx/interface.go @@ -0,0 +1,47 @@ +package multiversx + +import ( + "context" + + "github.com/multiversx/mx-bridge-eth-go/parsers" + "github.com/multiversx/mx-chain-core-go/data/api" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/data" +) + +// Proxy defines the behavior of a proxy able to serve MultiversX blockchain requests +type Proxy interface { + GetNetworkConfig(ctx context.Context) (*data.NetworkConfig, error) + SendTransaction(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) + SendTransactions(ctx context.Context, txs []*transaction.FrontendTransaction) ([]string, error) + ExecuteVMQuery(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) + GetAccount(ctx context.Context, address core.AddressHandler) (*data.Account, error) + GetNetworkStatus(ctx context.Context, shardID uint32) (*data.NetworkStatus, error) + GetShardOfAddress(ctx context.Context, bech32Address string) (uint32, error) + GetESDTTokenData(ctx context.Context, address core.AddressHandler, tokenIdentifier string, queryOptions api.AccountQueryOptions) (*data.ESDTFungibleTokenData, error) + GetTransactionInfoWithResults(ctx context.Context, hash string) (*data.TransactionInfo, error) + ProcessTransactionStatus(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) + IsInterfaceNil() bool +} + +// NonceTransactionsHandler represents the interface able to handle the current nonce and the transactions resend mechanism +type NonceTransactionsHandler interface { + ApplyNonceAndGasPrice(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error + SendTransaction(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) + Close() error + IsInterfaceNil() bool +} + +// ScCallsExecuteFilter defines the operations supported by a filter that allows selective executions of batches +type ScCallsExecuteFilter interface { + ShouldExecute(callData parsers.ProxySCCompleteCallData) bool + IsInterfaceNil() bool +} + +// Codec defines the operations implemented by a MultiversX codec +type Codec interface { + DecodeProxySCCompleteCallData(buff []byte) (parsers.ProxySCCompleteCallData, error) + ExtractGasLimitFromRawCallData(buff []byte) (uint64, error) + IsInterfaceNil() bool +} diff --git a/executors/multiversx/module/interface.go b/executors/multiversx/module/interface.go new file mode 100644 index 00000000..69fab4b3 --- /dev/null +++ b/executors/multiversx/module/interface.go @@ -0,0 +1,27 @@ +package module + +import ( + "context" + + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-sdk-go/core" +) + +type nonceTransactionsHandler interface { + ApplyNonceAndGasPrice(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error + SendTransaction(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) + Close() error + IsInterfaceNil() bool +} + +type pollingHandler interface { + StartProcessingLoop() error + Close() error + IsInterfaceNil() bool +} + +type executor interface { + Execute(ctx context.Context) error + GetNumSentTransaction() uint32 + IsInterfaceNil() bool +} diff --git a/executors/multiversx/module/scCallsModule.go b/executors/multiversx/module/scCallsModule.go new file mode 100644 index 00000000..19d2ff1b --- /dev/null +++ b/executors/multiversx/module/scCallsModule.go @@ -0,0 +1,130 @@ +package module + +import ( + "time" + + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/executors/multiversx" + "github.com/multiversx/mx-bridge-eth-go/executors/multiversx/filters" + "github.com/multiversx/mx-bridge-eth-go/parsers" + "github.com/multiversx/mx-chain-crypto-go/signing" + "github.com/multiversx/mx-chain-crypto-go/signing/ed25519" + "github.com/multiversx/mx-chain-crypto-go/signing/ed25519/singlesig" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-sdk-go/blockchain" + sdkCore "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/core/polling" + "github.com/multiversx/mx-sdk-go/interactors" + "github.com/multiversx/mx-sdk-go/interactors/nonceHandlerV2" +) + +var suite = ed25519.NewEd25519() +var keyGen = signing.NewKeyGenerator(suite) +var singleSigner = &singlesig.Ed25519Signer{} + +type scCallsModule struct { + nonceTxsHandler nonceTransactionsHandler + pollingHandler pollingHandler + executorInstance executor +} + +// NewScCallsModule creates a starts a new scCallsModule instance +func NewScCallsModule(cfg config.ScCallsModuleConfig, log logger.Logger, chCloseApp chan struct{}) (*scCallsModule, error) { + filter, err := filters.NewPendingOperationFilter(cfg.Filter, log) + if err != nil { + return nil, err + } + + argsProxy := blockchain.ArgsProxy{ + ProxyURL: cfg.NetworkAddress, + SameScState: false, + ShouldBeSynced: false, + FinalityCheck: cfg.ProxyFinalityCheck, + AllowedDeltaToFinal: cfg.ProxyMaxNoncesDelta, + CacheExpirationTime: time.Second * time.Duration(cfg.ProxyCacherExpirationSeconds), + EntityType: sdkCore.RestAPIEntityType(cfg.ProxyRestAPIEntityType), + } + + proxy, err := blockchain.NewProxy(argsProxy) + if err != nil { + return nil, err + } + + module := &scCallsModule{} + + argNonceHandler := nonceHandlerV2.ArgsNonceTransactionsHandlerV2{ + Proxy: proxy, + IntervalToResend: time.Second * time.Duration(cfg.IntervalToResendTxsInSeconds), + } + module.nonceTxsHandler, err = nonceHandlerV2.NewNonceTransactionHandlerV2(argNonceHandler) + if err != nil { + return nil, err + } + + wallet := interactors.NewWallet() + multiversXPrivateKeyBytes, err := wallet.LoadPrivateKeyFromPemFile(cfg.PrivateKeyFile) + if err != nil { + return nil, err + } + + privateKey, err := keyGen.PrivateKeyFromByteArray(multiversXPrivateKeyBytes) + if err != nil { + return nil, err + } + + argsExecutor := multiversx.ArgsScCallExecutor{ + ScProxyBech32Address: cfg.ScProxyBech32Address, + Proxy: proxy, + Codec: &parsers.MultiversxCodec{}, + Filter: filter, + Log: log, + ExtraGasToExecute: cfg.ExtraGasToExecute, + MaxGasLimitToUse: cfg.MaxGasLimitToUse, + GasLimitForOutOfGasTransactions: cfg.GasLimitForOutOfGasTransactions, + NonceTxHandler: module.nonceTxsHandler, + PrivateKey: privateKey, + SingleSigner: singleSigner, + CloseAppChan: chCloseApp, + TransactionChecks: cfg.TransactionChecks, + } + module.executorInstance, err = multiversx.NewScCallExecutor(argsExecutor) + if err != nil { + return nil, err + } + + argsPollingHandler := polling.ArgsPollingHandler{ + Log: log, + Name: "MultiversX SC calls", + PollingInterval: time.Duration(cfg.PollingIntervalInMillis) * time.Millisecond, + PollingWhenError: time.Duration(cfg.PollingIntervalInMillis) * time.Millisecond, + Executor: module.executorInstance, + } + + module.pollingHandler, err = polling.NewPollingHandler(argsPollingHandler) + if err != nil { + return nil, err + } + + err = module.pollingHandler.StartProcessingLoop() + if err != nil { + return nil, err + } + + return module, nil +} + +// GetNumSentTransaction returns the total sent transactions +func (module *scCallsModule) GetNumSentTransaction() uint32 { + return module.executorInstance.GetNumSentTransaction() +} + +// Close closes any components started +func (module *scCallsModule) Close() error { + errPollingHandler := module.pollingHandler.Close() + errNonceTxsHandler := module.nonceTxsHandler.Close() + + if errPollingHandler != nil { + return errPollingHandler + } + return errNonceTxsHandler +} diff --git a/executors/multiversx/module/scCallsModule_test.go b/executors/multiversx/module/scCallsModule_test.go new file mode 100644 index 00000000..246e25ea --- /dev/null +++ b/executors/multiversx/module/scCallsModule_test.go @@ -0,0 +1,124 @@ +package module + +import ( + "testing" + + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + sdkCore "github.com/multiversx/mx-sdk-go/core" + "github.com/stretchr/testify/assert" +) + +func createTestConfigs() config.ScCallsModuleConfig { + return config.ScCallsModuleConfig{ + ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqgftcwj09u0nhmskrw7xxqcqh8qmzwyexd8ss7ftcxx", + ExtraGasToExecute: 6000000, + MaxGasLimitToUse: 249999999, + GasLimitForOutOfGasTransactions: 30000000, + NetworkAddress: "http://127.0.0.1:8079", + ProxyMaxNoncesDelta: 5, + ProxyFinalityCheck: false, + ProxyCacherExpirationSeconds: 60, + ProxyRestAPIEntityType: string(sdkCore.ObserverNode), + IntervalToResendTxsInSeconds: 1, + PrivateKeyFile: "testdata/grace.pem", + PollingIntervalInMillis: 10000, + Filter: config.PendingOperationsFilterConfig{ + DeniedEthAddresses: nil, + AllowedEthAddresses: []string{"*"}, + DeniedMvxAddresses: nil, + AllowedMvxAddresses: []string{"*"}, + DeniedTokens: nil, + AllowedTokens: []string{"*"}, + }, + } +} + +func TestNewScCallsModule(t *testing.T) { + t.Parallel() + + t.Run("invalid filter config should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfigs() + cfg.Filter.DeniedTokens = []string{"*"} + + module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "unsupported marker * on item at index 0 in list DeniedTokens") + assert.Nil(t, module) + }) + t.Run("invalid proxy cacher interval expiration should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfigs() + cfg.ProxyCacherExpirationSeconds = 0 + + module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid caching duration, provided: 0s, minimum: 1s") + assert.Nil(t, module) + }) + t.Run("invalid resend interval should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfigs() + cfg.IntervalToResendTxsInSeconds = 0 + + module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid value for intervalToResend in NewNonceTransactionHandlerV2") + assert.Nil(t, module) + }) + t.Run("invalid private key file should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfigs() + cfg.PrivateKeyFile = "" + + module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) + assert.NotNil(t, err) + assert.Nil(t, module) + }) + t.Run("invalid polling interval should error", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfigs() + cfg.PollingIntervalInMillis = 0 + + module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "invalid value for PollingInterval") + assert.Nil(t, module) + }) + t.Run("should work with nil close app chan", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfigs() + module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, nil) + assert.Nil(t, err) + assert.NotNil(t, module) + + assert.Zero(t, module.GetNumSentTransaction()) + + err = module.Close() + assert.Nil(t, err) + }) + t.Run("should work with nil close app chan", func(t *testing.T) { + t.Parallel() + + cfg := createTestConfigs() + cfg.TransactionChecks.CheckTransactionResults = true + cfg.TransactionChecks.TimeInSecondsBetweenChecks = 1 + cfg.TransactionChecks.ExecutionTimeoutInSeconds = 1 + cfg.TransactionChecks.CloseAppOnError = true + module, err := NewScCallsModule(cfg, &testsCommon.LoggerStub{}, make(chan struct{}, 1)) + assert.Nil(t, err) + assert.NotNil(t, module) + + assert.Zero(t, module.GetNumSentTransaction()) + + err = module.Close() + assert.Nil(t, err) + }) +} diff --git a/executors/multiversx/module/testdata/grace.pem b/executors/multiversx/module/testdata/grace.pem new file mode 100644 index 00000000..90acc33e --- /dev/null +++ b/executors/multiversx/module/testdata/grace.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- +ODY5Yzk4ZGYwYjExZmRhMDgwNGQ3ODM1NGVjYTQzMWY4N2E2ZTkxMGZhZTE0MDgx +ZjczOGMzMTZhMjVlYTQwOTFlOGE4YjZiNDlkZTViN2JlMTBhYWExNThhNWE2YTRh +YmI0YjU2Y2MwOGY1MjRiYjVlNmNkNWYyMTFhZDNlMTM= +-----END PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- diff --git a/executors/multiversx/scCallsExecutor.go b/executors/multiversx/scCallsExecutor.go new file mode 100644 index 00000000..a573c1c3 --- /dev/null +++ b/executors/multiversx/scCallsExecutor.go @@ -0,0 +1,480 @@ +package multiversx + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + "sync/atomic" + "time" + + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/errors" + "github.com/multiversx/mx-bridge-eth-go/parsers" + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-chain-core-go/data/transaction" + crypto "github.com/multiversx/mx-chain-crypto-go" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-sdk-go/builders" + "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/data" +) + +const ( + getPendingTransactionsFunction = "getPendingTransactions" + okCodeAfterExecution = "ok" + scProxyCallFunction = "execute" + minCheckValues = 1 + transactionNotFoundErrString = "transaction not found" + minGasToExecuteSCCalls = 2010000 // the absolut minimum gas limit to do a SC call + contractMaxGasLimit = 249999999 +) + +// ArgsScCallExecutor represents the DTO struct for creating a new instance of type scCallExecutor +type ArgsScCallExecutor struct { + ScProxyBech32Address string + Proxy Proxy + Codec Codec + Filter ScCallsExecuteFilter + Log logger.Logger + ExtraGasToExecute uint64 + MaxGasLimitToUse uint64 + GasLimitForOutOfGasTransactions uint64 + NonceTxHandler NonceTransactionsHandler + PrivateKey crypto.PrivateKey + SingleSigner crypto.SingleSigner + TransactionChecks config.TransactionChecksConfig + CloseAppChan chan struct{} +} + +type scCallExecutor struct { + scProxyBech32Address string + proxy Proxy + codec Codec + filter ScCallsExecuteFilter + log logger.Logger + extraGasToExecute uint64 + maxGasLimitToUse uint64 + gasLimitForOutOfGasTransactions uint64 + nonceTxHandler NonceTransactionsHandler + privateKey crypto.PrivateKey + singleSigner crypto.SingleSigner + senderAddress core.AddressHandler + numSentTransactions uint32 + checkTransactionResults bool + timeBetweenChecks time.Duration + executionTimeout time.Duration + closeAppOnError bool + extraDelayOnError time.Duration + closeAppChan chan struct{} +} + +// NewScCallExecutor creates a new instance of type scCallExecutor +func NewScCallExecutor(args ArgsScCallExecutor) (*scCallExecutor, error) { + err := checkArgs(args) + if err != nil { + return nil, err + } + + publicKey := args.PrivateKey.GeneratePublic() + publicKeyBytes, err := publicKey.ToByteArray() + if err != nil { + return nil, err + } + senderAddress := data.NewAddressFromBytes(publicKeyBytes) + + return &scCallExecutor{ + scProxyBech32Address: args.ScProxyBech32Address, + proxy: args.Proxy, + codec: args.Codec, + filter: args.Filter, + log: args.Log, + extraGasToExecute: args.ExtraGasToExecute, + maxGasLimitToUse: args.MaxGasLimitToUse, + gasLimitForOutOfGasTransactions: args.GasLimitForOutOfGasTransactions, + nonceTxHandler: args.NonceTxHandler, + privateKey: args.PrivateKey, + singleSigner: args.SingleSigner, + senderAddress: senderAddress, + checkTransactionResults: args.TransactionChecks.CheckTransactionResults, + timeBetweenChecks: time.Second * time.Duration(args.TransactionChecks.TimeInSecondsBetweenChecks), + executionTimeout: time.Second * time.Duration(args.TransactionChecks.ExecutionTimeoutInSeconds), + closeAppOnError: args.TransactionChecks.CloseAppOnError, + extraDelayOnError: time.Second * time.Duration(args.TransactionChecks.ExtraDelayInSecondsOnError), + closeAppChan: args.CloseAppChan, + }, nil +} + +func checkArgs(args ArgsScCallExecutor) error { + if check.IfNil(args.Proxy) { + return errNilProxy + } + if check.IfNil(args.Codec) { + return errNilCodec + } + if check.IfNil(args.Filter) { + return errNilFilter + } + if check.IfNil(args.Log) { + return errNilLogger + } + if check.IfNil(args.NonceTxHandler) { + return errNilNonceTxHandler + } + if check.IfNil(args.PrivateKey) { + return errNilPrivateKey + } + if check.IfNil(args.SingleSigner) { + return errNilSingleSigner + } + if args.MaxGasLimitToUse < minGasToExecuteSCCalls { + return fmt.Errorf("%w for MaxGasLimitToUse: provided: %d, absolute minimum required: %d", errGasLimitIsLessThanAbsoluteMinimum, args.MaxGasLimitToUse, minGasToExecuteSCCalls) + } + if args.GasLimitForOutOfGasTransactions < minGasToExecuteSCCalls { + return fmt.Errorf("%w for GasLimitForOutOfGasTransactions: provided: %d, absolute minimum required: %d", errGasLimitIsLessThanAbsoluteMinimum, args.GasLimitForOutOfGasTransactions, minGasToExecuteSCCalls) + } + err := checkTransactionChecksConfig(args) + if err != nil { + return err + } + + _, err = data.NewAddressFromBech32String(args.ScProxyBech32Address) + + return err +} + +func checkTransactionChecksConfig(args ArgsScCallExecutor) error { + if !args.TransactionChecks.CheckTransactionResults { + args.Log.Warn("transaction checks are disabled! This can lead to funds being drained in case of a repetitive error") + return nil + } + + if args.TransactionChecks.TimeInSecondsBetweenChecks < minCheckValues { + return fmt.Errorf("%w for TransactionChecks.TimeInSecondsBetweenChecks, minimum: %d, got: %d", + errInvalidValue, minCheckValues, args.TransactionChecks.TimeInSecondsBetweenChecks) + } + if args.TransactionChecks.ExecutionTimeoutInSeconds < minCheckValues { + return fmt.Errorf("%w for TransactionChecks.ExecutionTimeoutInSeconds, minimum: %d, got: %d", + errInvalidValue, minCheckValues, args.TransactionChecks.ExecutionTimeoutInSeconds) + } + if args.CloseAppChan == nil && args.TransactionChecks.CloseAppOnError { + return fmt.Errorf("%w while the TransactionChecks.CloseAppOnError is set to true", errNilCloseAppChannel) + } + + return nil +} + +// Execute will execute one step: get all pending operations, call the filter and send execution transactions +func (executor *scCallExecutor) Execute(ctx context.Context) error { + pendingOperations, err := executor.getPendingOperations(ctx) + if err != nil { + return err + } + + filteredPendingOperations := executor.filterOperations(pendingOperations) + + return executor.executeOperations(ctx, filteredPendingOperations) +} + +func (executor *scCallExecutor) getPendingOperations(ctx context.Context) (map[uint64]parsers.ProxySCCompleteCallData, error) { + request := &data.VmValueRequest{ + Address: executor.scProxyBech32Address, + FuncName: getPendingTransactionsFunction, + } + + response, err := executor.proxy.ExecuteVMQuery(ctx, request) + if err != nil { + executor.log.Error("got error on VMQuery", "FuncName", request.FuncName, + "Args", request.Args, "SC address", request.Address, "Caller", request.CallerAddr, "error", err) + return nil, err + } + if response.Data.ReturnCode != okCodeAfterExecution { + return nil, errors.NewQueryResponseError( + response.Data.ReturnCode, + response.Data.ReturnMessage, + request.FuncName, + request.Address, + request.Args..., + ) + } + + return executor.parseResponse(response) +} + +func (executor *scCallExecutor) parseResponse(response *data.VmValuesResponseData) (map[uint64]parsers.ProxySCCompleteCallData, error) { + numResponseLines := len(response.Data.ReturnData) + if numResponseLines%2 != 0 { + return nil, fmt.Errorf("%w: expected an even number, got %d", errInvalidNumberOfResponseLines, numResponseLines) + } + + result := make(map[uint64]parsers.ProxySCCompleteCallData, numResponseLines/2) + + for i := 0; i < numResponseLines; i += 2 { + pendingOperationID := big.NewInt(0).SetBytes(response.Data.ReturnData[i]) + callData, err := executor.codec.DecodeProxySCCompleteCallData(response.Data.ReturnData[i+1]) + if err != nil { + return nil, fmt.Errorf("%w for ReturnData at index %d", err, i+1) + } + + result[pendingOperationID.Uint64()] = callData + } + + return result, nil +} + +func (executor *scCallExecutor) filterOperations(pendingOperations map[uint64]parsers.ProxySCCompleteCallData) map[uint64]parsers.ProxySCCompleteCallData { + result := make(map[uint64]parsers.ProxySCCompleteCallData) + for id, callData := range pendingOperations { + if executor.filter.ShouldExecute(callData) { + result[id] = callData + } + } + + executor.log.Debug("scCallExecutor.filterOperations", "input pending ops", len(pendingOperations), "result pending ops", len(result)) + + return result +} + +func (executor *scCallExecutor) executeOperations(ctx context.Context, pendingOperations map[uint64]parsers.ProxySCCompleteCallData) error { + networkConfig, err := executor.proxy.GetNetworkConfig(ctx) + if err != nil { + return fmt.Errorf("%w while fetching network configs", err) + } + + for id, callData := range pendingOperations { + workingCtx, cancel := context.WithTimeout(ctx, executor.executionTimeout) + + executor.log.Debug("scCallExecutor.executeOperations", "executing ID", id, "call data", callData, + "maximum timeout", executor.executionTimeout) + err = executor.executeOperation(workingCtx, id, callData, networkConfig) + cancel() + + if err != nil { + return fmt.Errorf("%w for call data: %s", err, callData) + } + } + + return nil +} + +func (executor *scCallExecutor) executeOperation( + ctx context.Context, + id uint64, + callData parsers.ProxySCCompleteCallData, + networkConfig *data.NetworkConfig, +) error { + txBuilder := builders.NewTxDataBuilder() + txBuilder.Function(scProxyCallFunction).ArgInt64(int64(id)) + + dataBytes, err := txBuilder.ToDataBytes() + if err != nil { + return err + } + + bech32Address, err := executor.senderAddress.AddressAsBech32String() + if err != nil { + return err + } + + gasLimit, err := executor.codec.ExtractGasLimitFromRawCallData(callData.RawCallData) + if err != nil { + executor.log.Warn("scCallExecutor.executeOperation found a non-parsable raw call data", + "raw call data", callData.RawCallData, "error", err) + gasLimit = 0 + } + + tx := &transaction.FrontendTransaction{ + ChainID: networkConfig.ChainID, + Version: networkConfig.MinTransactionVersion, + GasLimit: gasLimit + executor.extraGasToExecute, + Data: dataBytes, + Sender: bech32Address, + Receiver: executor.scProxyBech32Address, + Value: "0", + } + + to, _ := callData.To.AddressAsBech32String() + if tx.GasLimit > contractMaxGasLimit { + // the contract will refund this transaction, so we will use less gas to preserve funds + executor.log.Warn("setting a lower gas limit for this transaction because it will be refunded", + "computed gas limit", tx.GasLimit, + "max allowed", executor.maxGasLimitToUse, + "data", dataBytes, + "from", callData.From.Hex(), + "to", to, + "token", callData.Token, + "amount", callData.Amount, + "nonce", callData.Nonce, + ) + tx.GasLimit = executor.gasLimitForOutOfGasTransactions + } + + if tx.GasLimit > executor.maxGasLimitToUse { + executor.log.Warn("can not execute transaction because the provided gas limit on the SC call exceeds "+ + "the maximum gas limit allowance for this executor, WILL SKIP the execution", + "computed gas limit", tx.GasLimit, + "max allowed", executor.maxGasLimitToUse, + "data", dataBytes, + "from", callData.From.Hex(), + "to", to, + "token", callData.Token, + "amount", callData.Amount, + "nonce", callData.Nonce, + ) + + return nil + } + + err = executor.nonceTxHandler.ApplyNonceAndGasPrice(ctx, executor.senderAddress, tx) + if err != nil { + return err + } + + err = executor.signTransactionWithPrivateKey(tx) + if err != nil { + return err + } + + hash, err := executor.nonceTxHandler.SendTransaction(ctx, tx) + if err != nil { + return err + } + + executor.log.Info("scCallExecutor.executeOperation: sent transaction from executor", + "hash", hash, + "tx ID", id, + "call data", callData.String(), + "extra gas", executor.extraGasToExecute, + "sender", bech32Address, + "to", to) + + atomic.AddUint32(&executor.numSentTransactions, 1) + + return executor.handleResults(ctx, hash) +} + +func (executor *scCallExecutor) handleResults(ctx context.Context, hash string) error { + if !executor.checkTransactionResults { + return nil + } + + err := executor.checkResultsUntilDone(ctx, hash) + executor.waitForExtraDelay(ctx, err) + return err +} + +// signTransactionWithPrivateKey signs a transaction with the client's private key +func (executor *scCallExecutor) signTransactionWithPrivateKey(tx *transaction.FrontendTransaction) error { + tx.Signature = "" + bytes, err := json.Marshal(&tx) + if err != nil { + return err + } + + signature, err := executor.singleSigner.Sign(executor.privateKey, bytes) + if err != nil { + return err + } + + tx.Signature = hex.EncodeToString(signature) + + return nil +} + +func (executor *scCallExecutor) checkResultsUntilDone(ctx context.Context, hash string) error { + timer := time.NewTimer(executor.timeBetweenChecks) + defer timer.Stop() + + for { + timer.Reset(executor.timeBetweenChecks) + + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + err, shouldStop := executor.checkResults(ctx, hash) + if shouldStop { + executor.handleError(ctx, err) + return err + } + } + } +} + +func (executor *scCallExecutor) checkResults(ctx context.Context, hash string) (error, bool) { + txStatus, err := executor.proxy.ProcessTransactionStatus(ctx, hash) + if err != nil { + if err.Error() == transactionNotFoundErrString { + return nil, false + } + + return err, true + } + + if txStatus == transaction.TxStatusSuccess { + return nil, true + } + if txStatus == transaction.TxStatusPending { + return nil, false + } + + executor.logFullTransaction(ctx, hash) + return fmt.Errorf("%w for tx hash %s", errTransactionFailed, hash), true +} + +func (executor *scCallExecutor) handleError(ctx context.Context, err error) { + if err == nil { + return + } + if !executor.closeAppOnError { + return + } + + go func() { + // wait here until we could write in the close app chan + // ... or the context expired (application might close) + select { + case <-ctx.Done(): + case executor.closeAppChan <- struct{}{}: + } + }() +} + +func (executor *scCallExecutor) logFullTransaction(ctx context.Context, hash string) { + txData, err := executor.proxy.GetTransactionInfoWithResults(ctx, hash) + if err != nil { + executor.log.Error("error getting the transaction for display", "error", err) + return + } + + txDataString, err := json.MarshalIndent(txData.Data.Transaction, "", " ") + if err != nil { + executor.log.Error("error preparing transaction for display", "error", err) + return + } + + executor.log.Error("transaction failed", "hash", hash, "full transaction details", string(txDataString)) +} + +func (executor *scCallExecutor) waitForExtraDelay(ctx context.Context, err error) { + if err == nil { + return + } + + timer := time.NewTimer(executor.extraDelayOnError) + select { + case <-ctx.Done(): + case <-timer.C: + } +} + +// GetNumSentTransaction returns the total sent transactions +func (executor *scCallExecutor) GetNumSentTransaction() uint32 { + return atomic.LoadUint32(&executor.numSentTransactions) +} + +// IsInterfaceNil returns true if there is no value under the interface +func (executor *scCallExecutor) IsInterfaceNil() bool { + return executor == nil +} diff --git a/executors/multiversx/scCallsExecutor_test.go b/executors/multiversx/scCallsExecutor_test.go new file mode 100644 index 00000000..bc9e8b30 --- /dev/null +++ b/executors/multiversx/scCallsExecutor_test.go @@ -0,0 +1,1130 @@ +package multiversx + +import ( + "bytes" + "context" + "encoding/hex" + "errors" + "fmt" + "math/big" + "sync/atomic" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/parsers" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + testCrypto "github.com/multiversx/mx-bridge-eth-go/testsCommon/crypto" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/interactors" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-core-go/data/vm" + crypto "github.com/multiversx/mx-chain-crypto-go" + "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/assert" +) + +var testCodec = &testsCommon.TestMultiversXCodec{} + +func createMockArgsScCallExecutor() ArgsScCallExecutor { + return ArgsScCallExecutor{ + ScProxyBech32Address: "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", + Proxy: &interactors.ProxyStub{}, + Codec: &testsCommon.MultiversxCodecStub{}, + Filter: &testsCommon.ScCallsExecuteFilterStub{}, + Log: &testsCommon.LoggerStub{}, + ExtraGasToExecute: 100, + MaxGasLimitToUse: minGasToExecuteSCCalls, + GasLimitForOutOfGasTransactions: minGasToExecuteSCCalls, + NonceTxHandler: &testsCommon.TxNonceHandlerV2Stub{}, + PrivateKey: testCrypto.NewPrivateKeyMock(), + SingleSigner: &testCrypto.SingleSignerStub{}, + CloseAppChan: make(chan struct{}), + } +} + +func createMockCheckConfigs() config.TransactionChecksConfig { + return config.TransactionChecksConfig{ + CheckTransactionResults: true, + TimeInSecondsBetweenChecks: 6, + ExecutionTimeoutInSeconds: 120, + CloseAppOnError: true, + ExtraDelayInSecondsOnError: 120, + } +} + +func createTestProxySCCompleteCallData(token string) parsers.ProxySCCompleteCallData { + callData := parsers.ProxySCCompleteCallData{ + RawCallData: testCodec.EncodeCallDataWithLenAndMarker( + parsers.CallData{ + Type: 1, + Function: "callMe", + GasLimit: 5000000, + Arguments: []string{"arg1", "arg2"}, + }), + From: common.Address{}, + Token: token, + Amount: big.NewInt(37), + Nonce: 1, + } + callData.To, _ = data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqnf2w270lhxhlj57jvthxw4tqsunrwnq0anaqm4d4fn") + + return callData +} + +func TestNewScCallExecutor(t *testing.T) { + t.Parallel() + + t.Run("nil proxy should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.Proxy = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilProxy, err) + }) + t.Run("nil codec should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.Codec = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilCodec, err) + }) + t.Run("nil filter should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.Filter = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilFilter, err) + }) + t.Run("nil logger should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.Log = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilLogger, err) + }) + t.Run("nil nonce tx handler should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.NonceTxHandler = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilNonceTxHandler, err) + }) + t.Run("nil private key should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.PrivateKey = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilPrivateKey, err) + }) + t.Run("nil single signer should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.SingleSigner = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.Equal(t, errNilSingleSigner, err) + }) + t.Run("invalid sc proxy bech32 address should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.ScProxyBech32Address = "not a valid bech32 address" + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.NotNil(t, err) + }) + t.Run("invalid value for TimeInSecondsBetweenChecks should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 0 + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.ErrorIs(t, err, errInvalidValue) + assert.Contains(t, err.Error(), "for TransactionChecks.TimeInSecondsBetweenChecks, minimum: 1, got: 0") + }) + t.Run("invalid value for ExecutionTimeoutInSeconds should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.ExecutionTimeoutInSeconds = 0 + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.ErrorIs(t, err, errInvalidValue) + assert.Contains(t, err.Error(), "for TransactionChecks.ExecutionTimeoutInSeconds, minimum: 1, got: 0") + }) + t.Run("nil close app chan should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionChecks = createMockCheckConfigs() + args.CloseAppChan = nil + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.ErrorIs(t, err, errNilCloseAppChannel) + assert.Contains(t, err.Error(), "while the TransactionChecks.CloseAppOnError is set to true") + }) + t.Run("invalid MaxGasLimitToUse should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionChecks = createMockCheckConfigs() + args.MaxGasLimitToUse = minGasToExecuteSCCalls - 1 + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.ErrorIs(t, err, errGasLimitIsLessThanAbsoluteMinimum) + assert.Contains(t, err.Error(), "provided: 2009999, absolute minimum required: 2010000") + assert.Contains(t, err.Error(), "MaxGasLimitToUse") + }) + t.Run("invalid GasLimitForOutOfGasTransactions should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionChecks = createMockCheckConfigs() + args.GasLimitForOutOfGasTransactions = minGasToExecuteSCCalls - 1 + + executor, err := NewScCallExecutor(args) + assert.Nil(t, executor) + assert.ErrorIs(t, err, errGasLimitIsLessThanAbsoluteMinimum) + assert.Contains(t, err.Error(), "provided: 2009999, absolute minimum required: 2010000") + assert.Contains(t, err.Error(), "GasLimitForOutOfGasTransactions") + }) + t.Run("should work without transaction checks", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + + executor, err := NewScCallExecutor(args) + assert.NotNil(t, executor) + assert.Nil(t, err) + }) + t.Run("should work with transaction checks", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.TransactionChecks = createMockCheckConfigs() + + executor, err := NewScCallExecutor(args) + assert.NotNil(t, executor) + assert.Nil(t, err) + }) +} + +func TestScCallExecutor_IsInterfaceNil(t *testing.T) { + t.Parallel() + + var instance *scCallExecutor + assert.True(t, instance.IsInterfaceNil()) + + instance = &scCallExecutor{} + assert.False(t, instance.IsInterfaceNil()) +} + +func TestScCallExecutor_Execute(t *testing.T) { + t.Parallel() + + runError := errors.New("run error") + expectedError := errors.New("expected error") + + argsForErrors := createMockArgsScCallExecutor() + argsForErrors.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + assert.Fail(t, "should have not called ApplyNonceAndGasPriceCalled") + return runError + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + assert.Fail(t, "should have not called SendTransactionCalled") + return "", runError + }, + } + + t.Run("get pending errors, should error", func(t *testing.T) { + t.Parallel() + + args := argsForErrors // value copy + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return nil, expectedError + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.Equal(t, expectedError, err) + assert.Zero(t, executor.GetNumSentTransaction()) + }) + t.Run("get pending returns a not ok status, should error", func(t *testing.T) { + t.Parallel() + + args := argsForErrors // value copy + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: "NOT OK", + }, + }, nil + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "got response code 'NOT OK'") + assert.Zero(t, executor.GetNumSentTransaction()) + }) + t.Run("get pending returns an odd number of lines, should error", func(t *testing.T) { + t.Parallel() + + args := argsForErrors // value copy + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + }, + }, + }, nil + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.ErrorIs(t, err, errInvalidNumberOfResponseLines) + assert.Contains(t, err.Error(), "expected an even number, got 1") + assert.Zero(t, executor.GetNumSentTransaction()) + }) + t.Run("decoder errors, should error", func(t *testing.T) { + t.Parallel() + + args := argsForErrors // value copy + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + {0x03, 0x04}, + }, + }, + }, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + assert.Equal(t, []byte{0x03, 0x04}, buff) + + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, expectedError + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.ErrorIs(t, err, expectedError) + assert.Zero(t, executor.GetNumSentTransaction()) + }) + t.Run("get network configs errors, should error", func(t *testing.T) { + t.Parallel() + + args := argsForErrors // value copy + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + {0x03, 0x04}, + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return nil, expectedError + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + assert.Equal(t, []byte{0x03, 0x04}, buff) + + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, nil + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.ErrorIs(t, err, expectedError) + assert.Zero(t, executor.GetNumSentTransaction()) + }) + t.Run("ApplyNonceAndGasPrice errors, should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + return expectedError + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + assert.Fail(t, "should have not called SendTransactionCalled") + return "", runError + }, + } + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + {0x03, 0x04}, + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return &data.NetworkConfig{}, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + assert.Equal(t, []byte{0x03, 0x04}, buff) + + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, nil + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.ErrorIs(t, err, expectedError) + assert.Zero(t, executor.GetNumSentTransaction()) + }) + t.Run("Sign errors, should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + return nil + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + assert.Fail(t, "should have not called SendTransactionCalled") + return "", runError + }, + } + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + {0x03, 0x04}, + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return &data.NetworkConfig{}, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + assert.Equal(t, []byte{0x03, 0x04}, buff) + + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, nil + }, + } + args.SingleSigner = &testCrypto.SingleSignerStub{ + SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { + return nil, expectedError + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.ErrorIs(t, err, expectedError) + assert.Zero(t, executor.GetNumSentTransaction()) + }) + t.Run("SendTransaction errors, should error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + return nil + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + return "", expectedError + }, + } + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + {0x03, 0x04}, + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return &data.NetworkConfig{}, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + assert.Equal(t, []byte{0x03, 0x04}, buff) + + return parsers.ProxySCCompleteCallData{ + RawCallData: []byte("dummy"), + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, nil + }, + ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { + assert.Equal(t, "dummy", string(buff)) + return 1000000, nil + }, + } + + executor, _ := NewScCallExecutor(args) + err := executor.Execute(context.Background()) + assert.ErrorIs(t, err, expectedError) + assert.Equal(t, uint32(0), executor.GetNumSentTransaction()) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.MaxGasLimitToUse = 250000000 + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 + txHash := "tx hash" + processTransactionStatusCalled := false + + nonceCounter := uint64(100) + sendWasCalled := false + + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + assert.Equal(t, args.ScProxyBech32Address, vmRequest.Address) + assert.Equal(t, getPendingTransactionsFunction, vmRequest.FuncName) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + []byte("ProxySCCompleteCallData 1"), + {0x02}, + []byte("ProxySCCompleteCallData 2"), + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return &data.NetworkConfig{ + ChainID: "TEST", + MinTransactionVersion: 111, + }, nil + }, + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + assert.Equal(t, txHash, hexTxHash) + processTransactionStatusCalled = true + + return transaction.TxStatusSuccess, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + if string(buff) == "ProxySCCompleteCallData 1" { + return createTestProxySCCompleteCallData("tkn1"), nil + } + if string(buff) == "ProxySCCompleteCallData 2" { + return createTestProxySCCompleteCallData("tkn2"), nil + } + + return parsers.ProxySCCompleteCallData{ + To: data.NewAddressFromBytes(bytes.Repeat([]byte{1}, 32)), + }, errors.New("wrong buffer") + }, + ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { + return 5000000, nil + }, + } + args.Filter = &testsCommon.ScCallsExecuteFilterStub{ + ShouldExecuteCalled: func(callData parsers.ProxySCCompleteCallData) bool { + return callData.Token == "tkn2" + }, + } + args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + tx.Nonce = nonceCounter + tx.GasPrice = 101010 + nonceCounter++ + return nil + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + assert.Equal(t, "TEST", tx.ChainID) + assert.Equal(t, uint32(111), tx.Version) + assert.Equal(t, args.ExtraGasToExecute+5000000, tx.GasLimit) + assert.Equal(t, nonceCounter-1, tx.Nonce) + assert.Equal(t, uint64(101010), tx.GasPrice) + assert.Equal(t, hex.EncodeToString([]byte("sig")), tx.Signature) + _, err := data.NewAddressFromBech32String(tx.Sender) + assert.Nil(t, err) + assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", tx.Receiver) + assert.Equal(t, "0", tx.Value) + + // only the second pending operation gor through the filter + expectedData := scProxyCallFunction + "@02" + assert.Equal(t, expectedData, string(tx.Data)) + + sendWasCalled = true + + return txHash, nil + }, + } + args.SingleSigner = &testCrypto.SingleSignerStub{ + SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { + return []byte("sig"), nil + }, + } + + executor, _ := NewScCallExecutor(args) + + err := executor.Execute(context.Background()) + assert.Nil(t, err) + assert.True(t, sendWasCalled) + assert.Equal(t, uint32(1), executor.GetNumSentTransaction()) + assert.True(t, processTransactionStatusCalled) + }) + t.Run("should work even if the gas limit decode errors", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + + nonceCounter := uint64(100) + sendWasCalled := false + + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + assert.Equal(t, args.ScProxyBech32Address, vmRequest.Address) + assert.Equal(t, getPendingTransactionsFunction, vmRequest.FuncName) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + []byte("ProxySCCompleteCallData 1"), + {0x02}, + []byte("ProxySCCompleteCallData 2"), + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return &data.NetworkConfig{ + ChainID: "TEST", + MinTransactionVersion: 111, + }, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + if string(buff) == "ProxySCCompleteCallData 1" { + return createTestProxySCCompleteCallData("tkn1"), nil + } + if string(buff) == "ProxySCCompleteCallData 2" { + return createTestProxySCCompleteCallData("tkn2"), nil + } + + return parsers.ProxySCCompleteCallData{}, errors.New("wrong buffer") + }, + ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { + return 0, expectedError + }, + } + args.Filter = &testsCommon.ScCallsExecuteFilterStub{ + ShouldExecuteCalled: func(callData parsers.ProxySCCompleteCallData) bool { + return callData.Token == "tkn2" + }, + } + args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + tx.Nonce = nonceCounter + tx.GasPrice = 101010 + nonceCounter++ + return nil + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + assert.Equal(t, "TEST", tx.ChainID) + assert.Equal(t, uint32(111), tx.Version) + assert.Equal(t, args.ExtraGasToExecute, tx.GasLimit) // no 5000000 added gas limit because it wasn't extracted + assert.Equal(t, nonceCounter-1, tx.Nonce) + assert.Equal(t, uint64(101010), tx.GasPrice) + assert.Equal(t, hex.EncodeToString([]byte("sig")), tx.Signature) + _, err := data.NewAddressFromBech32String(tx.Sender) + assert.Nil(t, err) + assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", tx.Receiver) + assert.Equal(t, "0", tx.Value) + + // only the second pending operation gor through the filter + expectedData := scProxyCallFunction + "@02" + assert.Equal(t, expectedData, string(tx.Data)) + + sendWasCalled = true + + return "", nil + }, + } + args.SingleSigner = &testCrypto.SingleSignerStub{ + SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { + return []byte("sig"), nil + }, + } + + executor, _ := NewScCallExecutor(args) + + err := executor.Execute(context.Background()) + assert.Nil(t, err) + assert.True(t, sendWasCalled) + assert.Equal(t, uint32(1), executor.GetNumSentTransaction()) + }) + t.Run("should work if the gas limit is above the contract threshold", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + + nonceCounter := uint64(100) + sendWasCalled := false + + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + assert.Equal(t, args.ScProxyBech32Address, vmRequest.Address) + assert.Equal(t, getPendingTransactionsFunction, vmRequest.FuncName) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + []byte("ProxySCCompleteCallData 1"), + {0x02}, + []byte("ProxySCCompleteCallData 2"), + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return &data.NetworkConfig{ + ChainID: "TEST", + MinTransactionVersion: 111, + }, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + if string(buff) == "ProxySCCompleteCallData 1" { + return createTestProxySCCompleteCallData("tkn1"), nil + } + if string(buff) == "ProxySCCompleteCallData 2" { + return createTestProxySCCompleteCallData("tkn2"), nil + } + + return parsers.ProxySCCompleteCallData{}, errors.New("wrong buffer") + }, + ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { + return contractMaxGasLimit + 1, nil + }, + } + args.Filter = &testsCommon.ScCallsExecuteFilterStub{ + ShouldExecuteCalled: func(callData parsers.ProxySCCompleteCallData) bool { + return callData.Token == "tkn2" + }, + } + args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + tx.Nonce = nonceCounter + tx.GasPrice = 101010 + nonceCounter++ + return nil + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + assert.Equal(t, "TEST", tx.ChainID) + assert.Equal(t, uint32(111), tx.Version) + assert.Equal(t, args.GasLimitForOutOfGasTransactions, tx.GasLimit) // the gas limit was replaced + assert.Equal(t, nonceCounter-1, tx.Nonce) + assert.Equal(t, uint64(101010), tx.GasPrice) + assert.Equal(t, hex.EncodeToString([]byte("sig")), tx.Signature) + _, err := data.NewAddressFromBech32String(tx.Sender) + assert.Nil(t, err) + assert.Equal(t, "erd1qqqqqqqqqqqqqpgqk839entmk46ykukvhpn90g6knskju3dtanaq20f66e", tx.Receiver) + assert.Equal(t, "0", tx.Value) + + // only the second pending operation gor through the filter + expectedData := scProxyCallFunction + "@02" + assert.Equal(t, expectedData, string(tx.Data)) + + sendWasCalled = true + + return "", nil + }, + } + args.SingleSigner = &testCrypto.SingleSignerStub{ + SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { + return []byte("sig"), nil + }, + } + + executor, _ := NewScCallExecutor(args) + + err := executor.Execute(context.Background()) + assert.Nil(t, err) + assert.True(t, sendWasCalled) + assert.Equal(t, uint32(1), executor.GetNumSentTransaction()) + }) + t.Run("should skip execution if the gas limit exceeds the maximum allowed", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + + args.Proxy = &interactors.ProxyStub{ + ExecuteVMQueryCalled: func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) { + assert.Equal(t, args.ScProxyBech32Address, vmRequest.Address) + assert.Equal(t, getPendingTransactionsFunction, vmRequest.FuncName) + + return &data.VmValuesResponseData{ + Data: &vm.VMOutputApi{ + ReturnCode: okCodeAfterExecution, + ReturnData: [][]byte{ + {0x01}, + []byte("ProxySCCompleteCallData 1"), + {0x02}, + []byte("ProxySCCompleteCallData 2"), + }, + }, + }, nil + }, + GetNetworkConfigCalled: func(ctx context.Context) (*data.NetworkConfig, error) { + return &data.NetworkConfig{ + ChainID: "TEST", + MinTransactionVersion: 111, + }, nil + }, + } + args.Codec = &testsCommon.MultiversxCodecStub{ + DecodeProxySCCompleteCallDataCalled: func(buff []byte) (parsers.ProxySCCompleteCallData, error) { + if string(buff) == "ProxySCCompleteCallData 1" { + return createTestProxySCCompleteCallData("tkn1"), nil + } + if string(buff) == "ProxySCCompleteCallData 2" { + return createTestProxySCCompleteCallData("tkn2"), nil + } + + return parsers.ProxySCCompleteCallData{}, errors.New("wrong buffer") + }, + ExtractGasLimitFromRawCallDataCalled: func(buff []byte) (uint64, error) { + return args.MaxGasLimitToUse - args.ExtraGasToExecute + 1, nil + }, + } + args.Filter = &testsCommon.ScCallsExecuteFilterStub{ + ShouldExecuteCalled: func(callData parsers.ProxySCCompleteCallData) bool { + return callData.Token == "tkn2" + }, + } + args.NonceTxHandler = &testsCommon.TxNonceHandlerV2Stub{ + ApplyNonceAndGasPriceCalled: func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + assert.Fail(t, "should have not apply nonce") + return nil + }, + SendTransactionCalled: func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + assert.Fail(t, "should have not called send") + + return "", nil + }, + } + args.SingleSigner = &testCrypto.SingleSignerStub{ + SignCalled: func(private crypto.PrivateKey, msg []byte) ([]byte, error) { + return []byte("sig"), nil + }, + } + + executor, _ := NewScCallExecutor(args) + + err := executor.Execute(context.Background()) + assert.Nil(t, err) + assert.Equal(t, uint32(0), executor.GetNumSentTransaction()) + }) +} + +func TestScCallExecutor_handleResults(t *testing.T) { + t.Parallel() + + testHash := "test hash" + t.Run("checkTransactionResults false should not check and return nil", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + assert.Fail(t, "should have not called ProcessTransactionStatusCalled") + + return transaction.TxStatusFail, nil + }, + } + + executor, _ := NewScCallExecutor(args) + + err := executor.handleResults(context.Background(), testHash) + assert.Nil(t, err) + }) + t.Run("timeout before process transaction called", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + assert.Fail(t, "should have not called ProcessTransactionStatusCalled") + + return transaction.TxStatusFail, nil + }, + } + args.TransactionChecks = createMockCheckConfigs() + + executor, _ := NewScCallExecutor(args) + + workingCtx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + err := executor.handleResults(workingCtx, testHash) + assert.ErrorIs(t, err, context.DeadlineExceeded) + }) + t.Run("transaction not found should continuously request the status", func(t *testing.T) { + t.Parallel() + + numRequests := uint64(0) + args := createMockArgsScCallExecutor() + chDone := make(chan struct{}, 1) + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + atomic.AddUint64(&numRequests, 1) + if atomic.LoadUint64(&numRequests) > 3 { + chDone <- struct{}{} + } + + return transaction.TxStatusInvalid, errors.New("transaction not found") + }, + } + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 + + executor, _ := NewScCallExecutor(args) + + go func() { + err := executor.handleResults(context.Background(), testHash) + assert.ErrorIs(t, err, context.DeadlineExceeded) // this will be the actual error when the function finishes + }() + + select { + case <-chDone: + return + case <-time.After(time.Second * 30): + assert.Fail(t, "timeout") + } + }) + t.Run("transaction is still pending should continuously request the status", func(t *testing.T) { + t.Parallel() + + numRequests := uint64(0) + args := createMockArgsScCallExecutor() + chDone := make(chan struct{}, 1) + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + atomic.AddUint64(&numRequests, 1) + if atomic.LoadUint64(&numRequests) > 3 { + chDone <- struct{}{} + } + + return transaction.TxStatusPending, nil + }, + } + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 + + executor, _ := NewScCallExecutor(args) + + go func() { + err := executor.handleResults(context.Background(), testHash) + assert.ErrorIs(t, err, context.DeadlineExceeded) // this will be the actual error when the function finishes + }() + + select { + case <-chDone: + return + case <-time.After(time.Second * 30): + assert.Fail(t, "timeout") + } + }) + t.Run("error while requesting the status should return the error and wait", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + args := createMockArgsScCallExecutor() + args.CloseAppChan = make(chan struct{}, 1) + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + return transaction.TxStatusInvalid, expectedErr + }, + } + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 + args.TransactionChecks.ExtraDelayInSecondsOnError = 6 + + executor, _ := NewScCallExecutor(args) + + start := time.Now() + err := executor.handleResults(context.Background(), testHash) + assert.Equal(t, expectedErr, err) + end := time.Now() + + assert.GreaterOrEqual(t, end.Sub(start), time.Second*6) + select { + case <-args.CloseAppChan: + default: + assert.Fail(t, "failed to write on the close app chan") + } + }) + t.Run("error while requesting the status should not write on the close app chan, if not enabled", func(t *testing.T) { + t.Parallel() + + expectedErr := errors.New("expected error") + args := createMockArgsScCallExecutor() + args.CloseAppChan = make(chan struct{}, 1) + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + return transaction.TxStatusInvalid, expectedErr + }, + } + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 + args.TransactionChecks.ExtraDelayInSecondsOnError = 1 + args.TransactionChecks.CloseAppOnError = false + + executor, _ := NewScCallExecutor(args) + + err := executor.handleResults(context.Background(), testHash) + assert.Equal(t, expectedErr, err) + + select { + case <-args.CloseAppChan: + assert.Fail(t, "should have not written on the close chan") + default: + } + }) + t.Run("transaction failed, should get more info and signal error", func(t *testing.T) { + t.Parallel() + + args := createMockArgsScCallExecutor() + args.CloseAppChan = make(chan struct{}, 1) + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + return transaction.TxStatusFail, nil + }, + GetTransactionInfoWithResultsCalled: func(ctx context.Context, txHash string) (*data.TransactionInfo, error) { + return &data.TransactionInfo{}, nil + }, + } + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 + args.TransactionChecks.ExtraDelayInSecondsOnError = 1 + + executor, _ := NewScCallExecutor(args) + + err := executor.handleResults(context.Background(), testHash) + assert.ErrorIs(t, err, errTransactionFailed) + + select { + case <-args.CloseAppChan: + default: + assert.Fail(t, "failed to write on the close app chan") + } + }) + t.Run("transaction failed, get more info fails, should signal error and not panic", func(t *testing.T) { + t.Parallel() + + defer func() { + r := recover() + if r != nil { + assert.Fail(t, fmt.Sprintf("should have not panicked %v", r)) + } + }() + + args := createMockArgsScCallExecutor() + args.CloseAppChan = make(chan struct{}, 1) + args.Proxy = &interactors.ProxyStub{ + ProcessTransactionStatusCalled: func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + return transaction.TxStatusFail, nil + }, + GetTransactionInfoWithResultsCalled: func(ctx context.Context, txHash string) (*data.TransactionInfo, error) { + return nil, fmt.Errorf("random error") + }, + } + args.TransactionChecks = createMockCheckConfigs() + args.TransactionChecks.TimeInSecondsBetweenChecks = 1 + args.TransactionChecks.ExtraDelayInSecondsOnError = 1 + + executor, _ := NewScCallExecutor(args) + + err := executor.handleResults(context.Background(), testHash) + assert.ErrorIs(t, err, errTransactionFailed) + + select { + case <-args.CloseAppChan: + default: + assert.Fail(t, "failed to write on the close app chan") + } + }) +} diff --git a/factory/errors.go b/factory/errors.go index be86f098..ddfb0dce 100644 --- a/factory/errors.go +++ b/factory/errors.go @@ -9,7 +9,6 @@ var ( errNilStatusStorer = errors.New("nil status storer") errNilErc20ContractsHolder = errors.New("nil ERC20 contracts holder") errMissingConfig = errors.New("missing config") - errPublicKeyCast = errors.New("error casting public key to ECDSA") errInvalidValue = errors.New("invalid value") errNilMetricsHolder = errors.New("nil metrics holder") errNilStatusHandler = errors.New("nil status handler") diff --git a/factory/ethMultiversXBridgeComponents.go b/factory/ethMultiversXBridgeComponents.go index 2a5aac7a..8bbc5f98 100644 --- a/factory/ethMultiversXBridgeComponents.go +++ b/factory/ethMultiversXBridgeComponents.go @@ -2,23 +2,19 @@ package factory import ( "context" - "crypto/ecdsa" "fmt" "io" - "io/ioutil" "sync" "time" "github.com/ethereum/go-ethereum/common" - ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/disabled" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps/ethToMultiversX" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/steps/multiversxToEth" "github.com/multiversx/mx-bridge-eth-go/bridges/ethMultiversX/topology" "github.com/multiversx/mx-bridge-eth-go/clients" - batchValidatorManagement "github.com/multiversx/mx-bridge-eth-go/clients/batchValidator" - batchManagementFactory "github.com/multiversx/mx-bridge-eth-go/clients/batchValidator/factory" + balanceValidatorManagement "github.com/multiversx/mx-bridge-eth-go/clients/balanceValidator" "github.com/multiversx/mx-bridge-eth-go/clients/chain" "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" "github.com/multiversx/mx-bridge-eth-go/clients/gasManagement" @@ -81,6 +77,7 @@ type ethMultiversXBridgeComponents struct { ethClient ethmultiversx.EthereumClient evmCompatibleChain chain.Chain multiversXMultisigContractAddress sdkCore.AddressHandler + multiversXSafeContractAddress sdkCore.AddressHandler multiversXRelayerPrivateKey crypto.PrivateKey multiversXRelayerAddress sdkCore.AddressHandler ethereumRelayerAddress common.Address @@ -259,6 +256,11 @@ func (components *ethMultiversXBridgeComponents) createMultiversXKeysAndAddresse return fmt.Errorf("%w for chainConfigs.MultisigContractAddress", err) } + components.multiversXSafeContractAddress, err = data.NewAddressFromBech32String(chainConfigs.SafeContractAddress) + if err != nil { + return fmt.Errorf("%w for chainConfigs.SafeContractAddress", err) + } + return nil } @@ -266,6 +268,7 @@ func (components *ethMultiversXBridgeComponents) createDataGetter() error { multiversXDataGetterLogId := components.evmCompatibleChain.MultiversXDataGetterLogId() argsMXClientDataGetter := multiversx.ArgsMXClientDataGetter{ MultisigContractAddress: components.multiversXMultisigContractAddress, + SafeContractAddress: components.multiversXSafeContractAddress, RelayerAddress: components.multiversXRelayerAddress, Proxy: components.proxy, Log: core.NewLoggerWithIdentifier(logger.GetOrCreate(multiversXDataGetterLogId), multiversXDataGetterLogId), @@ -291,11 +294,12 @@ func (components *ethMultiversXBridgeComponents) createMultiversXClient(args Arg Log: core.NewLoggerWithIdentifier(logger.GetOrCreate(multiversXClientLogId), multiversXClientLogId), RelayerPrivateKey: components.multiversXRelayerPrivateKey, MultisigContractAddress: components.multiversXMultisigContractAddress, + SafeContractAddress: components.multiversXSafeContractAddress, IntervalToResendTxsInSeconds: chainConfigs.IntervalToResendTxsInSeconds, TokensMapper: tokensMapper, RoleProvider: components.multiversXRoleProvider, StatusHandler: args.MultiversXClientStatusHandler, - AllowDelta: uint64(chainConfigs.ProxyMaxNoncesDelta), + ClientAvailabilityAllowDelta: chainConfigs.ClientAvailabilityAllowDelta, } components.multiversXClient, err = multiversx.NewClient(clientArgs) @@ -324,6 +328,8 @@ func (components *ethMultiversXBridgeComponents) createEthereumClient(args ArgsE return err } + components.addClosableComponent(gs) + antifloodComponents, err := components.createAntifloodComponents(args.Configs.GeneralConfig.P2P.AntifloodConfig) if err != nil { return err @@ -357,22 +363,12 @@ func (components *ethMultiversXBridgeComponents) createEthereumClient(args ArgsE return err } - privateKeyBytes, err := ioutil.ReadFile(ethereumConfigs.PrivateKeyFile) - if err != nil { - return err - } - privateKeyString := converters.TrimWhiteSpaceCharacters(string(privateKeyBytes)) - privateKey, err := ethCrypto.HexToECDSA(privateKeyString) + cryptoHandler, err := ethereum.NewCryptoHandler(ethereumConfigs.PrivateKeyFile) if err != nil { return err } - publicKey := privateKey.Public() - publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) - if !ok { - return errPublicKeyCast - } - components.ethereumRelayerAddress = ethCrypto.PubkeyToAddress(*publicKeyECDSA) + components.ethereumRelayerAddress = cryptoHandler.GetAddress() tokensMapper, err := mappers.NewErc20ToMultiversXMapper(components.mxDataGetter) if err != nil { @@ -390,19 +386,21 @@ func (components *ethMultiversXBridgeComponents) createEthereumClient(args ArgsE ethClientLogId := components.evmCompatibleChain.EvmCompatibleChainClientLogId() argsEthClient := ethereum.ArgsEthereumClient{ - ClientWrapper: args.ClientWrapper, - Erc20ContractsHandler: args.Erc20ContractsHolder, - Log: core.NewLoggerWithIdentifier(logger.GetOrCreate(ethClientLogId), ethClientLogId), - AddressConverter: components.addressConverter, - Broadcaster: components.broadcaster, - PrivateKey: privateKey, - TokensMapper: tokensMapper, - SignatureHolder: signaturesHolder, - SafeContractAddress: safeContractAddress, - GasHandler: gs, - TransferGasLimitBase: ethereumConfigs.GasLimitBase, - TransferGasLimitForEach: ethereumConfigs.GasLimitForEach, - AllowDelta: ethereumConfigs.MaxBlocksDelta, + ClientWrapper: args.ClientWrapper, + Erc20ContractsHandler: args.Erc20ContractsHolder, + Log: core.NewLoggerWithIdentifier(logger.GetOrCreate(ethClientLogId), ethClientLogId), + AddressConverter: components.addressConverter, + Broadcaster: components.broadcaster, + CryptoHandler: cryptoHandler, + TokensMapper: tokensMapper, + SignatureHolder: signaturesHolder, + SafeContractAddress: safeContractAddress, + GasHandler: gs, + TransferGasLimitBase: ethereumConfigs.GasLimitBase, + TransferGasLimitForEach: ethereumConfigs.GasLimitForEach, + ClientAvailabilityAllowDelta: ethereumConfigs.ClientAvailabilityAllowDelta, + EventsBlockRangeFrom: ethereumConfigs.EventsBlockRangeFrom, + EventsBlockRangeTo: ethereumConfigs.EventsBlockRangeTo, } components.ethClient, err = ethereum.NewEthereumClient(argsEthClient) @@ -516,7 +514,7 @@ func (components *ethMultiversXBridgeComponents) createEthereumToMultiversXBridg timeForTransferExecution := time.Second * time.Duration(args.Configs.GeneralConfig.Eth.IntervalToWaitForTransferInSeconds) - batchValidator, err := components.createBatchValidator(components.evmCompatibleChain, chain.MultiversX, args.Configs.GeneralConfig.BatchValidator) + balanceValidator, err := components.createBalanceValidator() if err != nil { return err } @@ -529,7 +527,7 @@ func (components *ethMultiversXBridgeComponents) createEthereumToMultiversXBridg StatusHandler: components.ethToMultiversXStatusHandler, TimeForWaitOnEthereum: timeForTransferExecution, SignaturesHolder: disabled.NewDisabledSignaturesHolder(), - BatchValidator: batchValidator, + BalanceValidator: balanceValidator, MaxQuorumRetriesOnEthereum: args.Configs.GeneralConfig.Eth.MaxRetriesOnQuorumReached, MaxQuorumRetriesOnMultiversX: args.Configs.GeneralConfig.MultiversX.MaxRetriesOnQuorumReached, MaxRestriesOnWasProposed: args.Configs.GeneralConfig.MultiversX.MaxRetriesOnWasTransferProposed, @@ -584,7 +582,7 @@ func (components *ethMultiversXBridgeComponents) createMultiversXToEthereumBridg timeForWaitOnEthereum := time.Second * time.Duration(args.Configs.GeneralConfig.Eth.IntervalToWaitForTransferInSeconds) - batchValidator, err := components.createBatchValidator(chain.MultiversX, components.evmCompatibleChain, args.Configs.GeneralConfig.BatchValidator) + balanceValidator, err := components.createBalanceValidator() if err != nil { return err } @@ -597,7 +595,7 @@ func (components *ethMultiversXBridgeComponents) createMultiversXToEthereumBridg StatusHandler: components.multiversXToEthStatusHandler, TimeForWaitOnEthereum: timeForWaitOnEthereum, SignaturesHolder: components.ethToMultiversXSignaturesHolder, - BatchValidator: batchValidator, + BalanceValidator: balanceValidator, MaxQuorumRetriesOnEthereum: args.Configs.GeneralConfig.Eth.MaxRetriesOnQuorumReached, MaxQuorumRetriesOnMultiversX: args.Configs.GeneralConfig.MultiversX.MaxRetriesOnQuorumReached, MaxRestriesOnWasProposed: args.Configs.GeneralConfig.MultiversX.MaxRetriesOnWasTransferProposed, @@ -649,24 +647,21 @@ func (components *ethMultiversXBridgeComponents) Start() error { return err } - go components.startBroadcastJoinRetriesLoop() + var ctx context.Context + ctx, components.cancelFunc = context.WithCancel(context.Background()) + go components.startBroadcastJoinRetriesLoop(ctx) return nil } -func (components *ethMultiversXBridgeComponents) createBatchValidator(sourceChain chain.Chain, destinationChain chain.Chain, args config.BatchValidatorConfig) (clients.BatchValidator, error) { - argsBatchValidator := batchValidatorManagement.ArgsBatchValidator{ - SourceChain: sourceChain, - DestinationChain: destinationChain, - RequestURL: args.URL, - RequestTime: time.Second * time.Duration(args.RequestTimeInSeconds), +func (components *ethMultiversXBridgeComponents) createBalanceValidator() (ethmultiversx.BalanceValidator, error) { + argsBalanceValidator := balanceValidatorManagement.ArgsBalanceValidator{ + Log: components.baseLogger, + MultiversXClient: components.multiversXClient, + EthereumClient: components.ethClient, } - batchValidator, err := batchManagementFactory.CreateBatchValidator(argsBatchValidator, args.Enabled) - if err != nil { - return nil, err - } - return batchValidator, err + return balanceValidatorManagement.NewBalanceValidator(argsBalanceValidator) } func (components *ethMultiversXBridgeComponents) createEthereumToMultiversXStateMachine() error { @@ -762,12 +757,10 @@ func (components *ethMultiversXBridgeComponents) createAntifloodComponents(antif return antiFloodComponents, nil } -func (components *ethMultiversXBridgeComponents) startBroadcastJoinRetriesLoop() { +func (components *ethMultiversXBridgeComponents) startBroadcastJoinRetriesLoop(ctx context.Context) { broadcastTimer := time.NewTimer(components.timeBeforeRepeatJoin) defer broadcastTimer.Stop() - var ctx context.Context - ctx, components.cancelFunc = context.WithCancel(context.Background()) for { broadcastTimer.Reset(components.timeBeforeRepeatJoin) diff --git a/factory/ethMultiversXBridgeComponents_test.go b/factory/ethMultiversXBridgeComponents_test.go index ebd446eb..1477b1f0 100644 --- a/factory/ethMultiversXBridgeComponents_test.go +++ b/factory/ethMultiversXBridgeComponents_test.go @@ -52,17 +52,24 @@ func createMockEthMultiversXBridgeArgs() ArgsEthereumToMultiversXBridge { }, MaxRetriesOnQuorumReached: 1, IntervalToWaitForTransferInSeconds: 1, - MaxBlocksDelta: 10, + ClientAvailabilityAllowDelta: 10, }, MultiversX: config.MultiversXConfig{ PrivateKeyFile: "testdata/grace.pem", IntervalToResendTxsInSeconds: 60, NetworkAddress: "http://127.0.0.1:8079", MultisigContractAddress: "erd1qqqqqqqqqqqqqpgqgftcwj09u0nhmskrw7xxqcqh8qmzwyexd8ss7ftcxx", + SafeContractAddress: "erd1qqqqqqqqqqqqqpgqgftcwj09u0nhmskrw7xxqcqh8qmzwyexd8ss7ftcxx", GasMap: testsCommon.CreateTestMultiversXGasMap(), MaxRetriesOnQuorumReached: 1, MaxRetriesOnWasTransferProposed: 1, - ProxyMaxNoncesDelta: 5, + ClientAvailabilityAllowDelta: 10, + Proxy: config.ProxyConfig{ + CacherExpirationSeconds: 600, + RestAPIEntityType: "observer", + MaxNoncesDelta: 10, + FinalityCheck: true, + }, }, Relayer: config.ConfigRelayer{ RoleProvider: config.RoleProviderConfig{ @@ -251,7 +258,7 @@ func TestNewEthMultiversXBridgeComponents(t *testing.T) { components, err := NewEthMultiversXBridgeComponents(args) require.Nil(t, err) require.NotNil(t, components) - require.Equal(t, 6, len(components.closableHandlers)) + require.Equal(t, 7, len(components.closableHandlers)) require.False(t, check.IfNil(components.ethToMultiversXStatusHandler)) require.False(t, check.IfNil(components.multiversXToEthStatusHandler)) }) @@ -266,7 +273,7 @@ func TestEthMultiversXBridgeComponents_StartAndCloseShouldWork(t *testing.T) { err = components.Start() assert.Nil(t, err) - assert.Equal(t, 6, len(components.closableHandlers)) + assert.Equal(t, 7, len(components.closableHandlers)) time.Sleep(time.Second * 2) // allow go routines to start @@ -418,6 +425,7 @@ func TestEthMultiversXBridgeComponents_RelayerAddresses(t *testing.T) { args := createMockEthMultiversXBridgeArgs() components, _ := NewEthMultiversXBridgeComponents(args) - assert.Equal(t, "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede", components.MultiversXRelayerAddress().AddressAsBech32String()) + bech32Address, _ := components.MultiversXRelayerAddress().AddressAsBech32String() + assert.Equal(t, "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede", bech32Address) assert.Equal(t, "0x3FE464Ac5aa562F7948322F92020F2b668D543d8", components.EthereumRelayerAddress().String()) } diff --git a/factory/storageFactory.go b/factory/storageFactory.go index 41cd9e8c..02073eb5 100644 --- a/factory/storageFactory.go +++ b/factory/storageFactory.go @@ -11,11 +11,18 @@ import ( // CreateUnitStorer based on the config and the working directory func CreateUnitStorer(config config.StorageConfig, workingDir string) (core.Storer, error) { + dbConfigHandler := factory.NewDBConfigHandler(config.DB) + persisterCreator, err := factory.NewPersisterFactory(dbConfigHandler) + if err != nil { + return nil, err + } + statusMetricsDbConfig := factory.GetDBFromConfig(config.DB) dbPath := path.Join(workingDir, config.DB.FilePath) statusMetricsDbConfig.FilePath = dbPath return storageunit.NewStorageUnitFromConf( factory.GetCacherFromConfig(config.Cache), - statusMetricsDbConfig) + statusMetricsDbConfig, + persisterCreator) } diff --git a/factory/storageFactory_test.go b/factory/storageFactory_test.go index 1de89944..c15734f9 100644 --- a/factory/storageFactory_test.go +++ b/factory/storageFactory_test.go @@ -1,8 +1,6 @@ package factory import ( - "io/ioutil" - "os" "path" "testing" "time" @@ -11,7 +9,6 @@ import ( "github.com/multiversx/mx-chain-go/config" logger "github.com/multiversx/mx-chain-logger-go" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) var log = logger.GetOrCreate("factory_test") @@ -19,22 +16,10 @@ var log = logger.GetOrCreate("factory_test") func TestCreateUnitStorer(t *testing.T) { t.Parallel() - workingDir, err := ioutil.TempDir("", "") - require.Nil(t, err) + workingDir := t.TempDir() log.Info("created temporary directory", "directory", workingDir) - defer func() { - err = os.RemoveAll(workingDir) - require.Nil(t, err) - - if err == nil { - log.Info("removed temporary directory", "directory", workingDir) - } else { - log.Error("error while removing temporary directory", "directory", workingDir, "error", err) - } - }() - cfg := config.StorageConfig{ Cache: config.CacheConfig{ Name: "StatusMetricsStorage", diff --git a/factory/testdata/grace.pem b/factory/testdata/grace.pem index 7b0836a5..90acc33e 100644 --- a/factory/testdata/grace.pem +++ b/factory/testdata/grace.pem @@ -2,4 +2,4 @@ ODY5Yzk4ZGYwYjExZmRhMDgwNGQ3ODM1NGVjYTQzMWY4N2E2ZTkxMGZhZTE0MDgx ZjczOGMzMTZhMjVlYTQwOTFlOGE4YjZiNDlkZTViN2JlMTBhYWExNThhNWE2YTRh YmI0YjU2Y2MwOGY1MjRiYjVlNmNkNWYyMTFhZDNlMTM= ------END PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- \ No newline at end of file +-----END PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- diff --git a/factory/webServer.go b/factory/webServer.go index 56823a67..84e40924 100644 --- a/factory/webServer.go +++ b/factory/webServer.go @@ -25,7 +25,7 @@ func StartWebServer(configs config.Configs, metricsHolder core.MetricsHolder) (i httpServerArgs := gin.ArgsNewWebServer{ Facade: relayerFacade, ApiConfig: configs.ApiRoutesConfig, - AntiFloodConfig: configs.GeneralConfig.Antiflood, + AntiFloodConfig: configs.GeneralConfig.WebAntiflood, } httpServerWrapper, err := gin.NewWebServerHandler(httpServerArgs) diff --git a/go.mod b/go.mod index 38b0ea0b..34960fd0 100644 --- a/go.mod +++ b/go.mod @@ -1,169 +1,231 @@ module github.com/multiversx/mx-bridge-eth-go -go 1.17 +go 1.20 require ( github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 - github.com/ethereum/go-ethereum v1.10.26 + github.com/ethereum/go-ethereum v1.13.15 github.com/gin-contrib/cors v1.4.0 github.com/gin-contrib/pprof v1.4.0 - github.com/gin-gonic/gin v1.8.1 - github.com/multiversx/mx-chain-core-go v1.1.33 - github.com/multiversx/mx-chain-crypto-go v1.2.5 - github.com/multiversx/mx-chain-go v1.4.15 - github.com/multiversx/mx-chain-logger-go v1.0.11 - github.com/multiversx/mx-chain-p2p-go v1.0.10 - github.com/multiversx/mx-sdk-go v1.3.2 - github.com/stretchr/testify v1.8.1 + github.com/gin-gonic/gin v1.9.1 + github.com/multiversx/mx-chain-communication-go v1.0.14 + github.com/multiversx/mx-chain-core-go v1.2.20 + github.com/multiversx/mx-chain-crypto-go v1.2.11 + github.com/multiversx/mx-chain-go v1.7.13-patch2 + github.com/multiversx/mx-chain-logger-go v1.0.14 + github.com/multiversx/mx-sdk-go v1.4.1 + github.com/pelletier/go-toml v1.9.3 + github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.10 ) require ( - github.com/beevik/ntp v0.3.0 // indirect - github.com/benbjohnson/clock v1.3.0 // indirect + github.com/DataDog/zstd v1.4.5 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/TwiN/go-color v1.1.0 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect + github.com/beevik/ntp v1.3.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcutil v1.1.3 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/cheekybits/genny v1.0.0 // indirect - github.com/containerd/cgroups v1.0.4 // indirect - github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/cockroachdb/errors v1.8.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect + github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 // indirect + github.com/cockroachdb/redact v1.0.8 // indirect + github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/containerd/cgroups v1.1.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect - github.com/deckarep/golang-set v1.8.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/denisbrodbeck/machineid v1.0.1 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/go-elasticsearch/v7 v7.12.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/fjl/memsize v0.0.2 // indirect github.com/flynn/noise v1.0.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.10.0 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/goccy/go-json v0.9.7 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/gops v0.3.18 // indirect + github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect - github.com/huin/goupnp v1.0.3 // indirect - github.com/ipfs/go-cid v0.3.2 // indirect - github.com/ipfs/go-datastore v0.5.1 // indirect - github.com/ipfs/go-ipfs-util v0.0.2 // indirect - github.com/ipfs/go-ipns v0.2.0 // indirect + github.com/hashicorp/golang-lru v0.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect + github.com/herumi/bls-go-binary v1.28.2 // indirect + github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/huin/goupnp v1.3.0 // indirect + github.com/ipfs/boxo v0.8.1 // indirect + github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-datastore v0.6.0 // indirect github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect - github.com/ipld/go-ipld-prime v0.19.0 // indirect + github.com/ipld/go-ipld-prime v0.20.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/jtolds/gls v4.20.0+incompatible // indirect - github.com/klauspost/compress v1.15.1 // indirect - github.com/klauspost/cpuid/v2 v2.1.0 // indirect - github.com/koron/go-ssdp v0.0.3 // indirect - github.com/leodido/go-urn v1.2.1 // indirect + github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/koron/go-ssdp v0.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p v0.22.0 // indirect - github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect - github.com/libp2p/go-libp2p-core v0.20.0 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.18.0 // indirect - github.com/libp2p/go-libp2p-kbucket v0.4.7 // indirect - github.com/libp2p/go-libp2p-pubsub v0.8.1 // indirect + github.com/libp2p/go-libp2p v0.28.2 // indirect + github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.23.0 // indirect + github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect + github.com/libp2p/go-libp2p-pubsub v0.9.3 // indirect github.com/libp2p/go-libp2p-record v0.2.0 // indirect - github.com/libp2p/go-msgio v0.2.0 // indirect - github.com/libp2p/go-nat v0.1.0 // indirect - github.com/libp2p/go-netroute v0.2.0 // indirect - github.com/libp2p/go-openssl v0.1.0 // indirect - github.com/libp2p/go-reuseport v0.2.0 // indirect - github.com/libp2p/go-yamux/v3 v3.1.2 // indirect - github.com/lucas-clemente/quic-go v0.28.1 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-nat v0.2.0 // indirect + github.com/libp2p/go-netroute v0.2.1 // indirect + github.com/libp2p/go-reuseport v0.3.0 // indirect + github.com/libp2p/go-yamux/v4 v4.0.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/mattn/go-pointer v0.0.1 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/miekg/dns v1.1.50 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/miekg/dns v1.1.54 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/pointerstructure v1.2.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/multiformats/go-base32 v0.0.4 // indirect - github.com/multiformats/go-base36 v0.1.0 // indirect - github.com/multiformats/go-multiaddr v0.6.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect + github.com/multiformats/go-multiaddr v0.9.0 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multibase v0.1.1 // indirect - github.com/multiformats/go-multicodec v0.6.0 // indirect - github.com/multiformats/go-multihash v0.2.1 // indirect - github.com/multiformats/go-multistream v0.3.3 // indirect - github.com/multiformats/go-varint v0.0.6 // indirect + github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect + github.com/multiformats/go-multistream v0.4.1 // indirect + github.com/multiformats/go-varint v0.0.7 // indirect github.com/multiversx/concurrent-map v0.1.4 // indirect - github.com/multiversx/mx-chain-storage-go v1.0.7 // indirect - github.com/multiversx/mx-chain-vm-common-go v1.3.37 // indirect - github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect + github.com/multiversx/mx-chain-es-indexer-go v1.4.21 // indirect + github.com/multiversx/mx-chain-scenario-go v1.4.3 // indirect + github.com/multiversx/mx-chain-storage-go v1.0.15 // indirect + github.com/multiversx/mx-chain-vm-common-go v1.5.12 // indirect + github.com/multiversx/mx-chain-vm-go v1.5.29 // indirect + github.com/multiversx/mx-chain-vm-v1_2-go v1.2.67 // indirect + github.com/multiversx/mx-chain-vm-v1_3-go v1.3.68 // indirect + github.com/multiversx/mx-chain-vm-v1_4-go v1.4.97 // indirect + github.com/multiversx/mx-components-big-int v1.0.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/onsi/ginkgo/v2 v2.11.0 // indirect + github.com/onsi/gomega v1.27.10 // indirect github.com/opencontainers/runtime-spec v1.0.2 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pborman/uuid v1.2.1 // indirect - github.com/pelletier/go-toml v1.9.3 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect - github.com/prometheus/client_golang v1.12.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qtls-go1-19 v0.3.3 // indirect + github.com/quic-go/qtls-go1-20 v0.2.3 // indirect + github.com/quic-go/quic-go v0.33.0 // indirect + github.com/quic-go/webtransport-go v0.5.3 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect - github.com/rjeczalik/notify v0.9.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/cors v1.7.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/smartystreets/assertions v1.13.1 // indirect - github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect - github.com/tklauser/go-sysconf v0.3.5 // indirect - github.com/tklauser/numcpus v0.2.2 // indirect + github.com/status-im/keycard-go v0.2.0 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect + github.com/tidwall/gjson v1.14.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect - github.com/ugorji/go/codec v1.2.7 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/urfave/cli/v2 v2.27.1 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opencensus.io v0.23.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - go.uber.org/zap v1.22.0 // indirect - golang.org/x/crypto v0.3.0 // indirect - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/net v0.5.0 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect - golang.org/x/tools v0.1.12 // indirect - google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/fx v1.19.2 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.24.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.15.0 // indirect + gonum.org/v1/gonum v0.11.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/go-playground/validator.v8 v8.18.2 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.1.7 // indirect + lukechampine.com/blake3 v1.2.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 1934b749..2baca09c 100644 --- a/go.sum +++ b/go.sum @@ -2,102 +2,52 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= +github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= -github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/TwiN/go-color v1.1.0 h1:yhLAHgjp2iAxmNjDiVb6Z073NE65yoaPlcki1Q22yyQ= +github.com/TwiN/go-color v1.1.0/go.mod h1:aKVf4e1mD4ai2FtPifkDPP5iyoCwiK08YGzGwerjKo0= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= -github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= -github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= -github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= -github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= -github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw= -github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= +github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= +github.com/beevik/ntp v1.3.0 h1:/w5VhpW5BGKS37vFm1p9oVk/t4HnnkKZAZIubHM6F7Q= +github.com/beevik/ntp v1.3.0/go.mod h1:vD6h1um4kzXpqmLTuu0cCLcC+NfvC0IC+ltmEDA8E78= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= -github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= @@ -109,7 +59,6 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOF github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= @@ -119,86 +68,87 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= +github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= +github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= -github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= -github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/elastic/go-elasticsearch/v7 v7.12.0 h1:j4tvcMrZJLp39L2NYvBb7f+lHKPqPHSL3nvB8+/DV+s= github.com/elastic/go-elasticsearch/v7 v7.12.0/go.mod h1:OJ4wdbtDNk5g503kvlHLyErCgQwwzmDtaFC4XyOxXA4= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= @@ -207,118 +157,108 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= -github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= +github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= +github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= +github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs= github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg= github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -328,340 +268,216 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/gops v0.3.18 h1:my259V+172PVFmduS2RAsq4FKH+HjKqdh7pLr17Ot8c= github.com/google/gops v0.3.18/go.mod h1:Pfp8hWGIFdV/7rY9/O/U5WgdjYQXf/GiEK4NVuVd2ZE= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= +github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= -github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= +github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/herumi/bls-go-binary v1.0.0 h1:PRPF6vPd35zyDy+tp86HwNnGdufCH2lZL0wZGxYvkRs= -github.com/herumi/bls-go-binary v1.0.0/go.mod h1:O4Vp1AfR4raRGwFeQpr9X/PQtncEicMoOe6BQt1oX0Y= +github.com/herumi/bls-go-binary v1.28.2 h1:F0AezsC0M1a9aZjk7g0l2hMb1F56Xtpfku97pDndNZE= +github.com/herumi/bls-go-binary v1.28.2/go.mod h1:O4Vp1AfR4raRGwFeQpr9X/PQtncEicMoOe6BQt1oX0Y= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= -github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= -github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro= -github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= -github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= -github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= -github.com/ipfs/go-datastore v0.5.1 h1:WkRhLuISI+XPD0uk3OskB0fYFSyqK8Ob5ZYew9Qa1nQ= -github.com/ipfs/go-datastore v0.5.1/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= +github.com/ipfs/boxo v0.8.1 h1:3DkKBCK+3rdEB5t77WDShUXXhktYwH99mkAsgajsKrU= +github.com/ipfs/boxo v0.8.1/go.mod h1:xJ2hVb4La5WyD7GvKYE0lq2g1rmQZoCD2K4WNrV6aZI= +github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= +github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= +github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= -github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= -github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= -github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= -github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= -github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= -github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipns v0.2.0 h1:BgmNtQhqOw5XEZ8RAfWEpK4DhqaYiuP6h71MhIp7xXU= -github.com/ipfs/go-ipns v0.2.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= -github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= -github.com/ipfs/go-log/v2 v2.5.0/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipld/go-ipld-prime v0.9.0/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= -github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04= -github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4= +github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= +github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= +github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= +github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= -github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= -github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= -github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= -github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= +github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= +github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= +github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A= -github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDKIzp7y4voR9CX/nvcfymLmg2UiOio= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= -github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= -github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= +github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= -github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= -github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.22.0 h1:2Tce0kHOp5zASFKJbNzRElvh0iZwdtG5uZheNW8chIw= -github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4= -github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo= -github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= -github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= -github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= -github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= -github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.6.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.19.0/go.mod h1:AkA+FUKQfYt1FLNef5fOPlo/naAWjKy/RCjkcPjqzYg= -github.com/libp2p/go-libp2p-core v0.20.0 h1:PGKM74+T+O/FaZNARNW32i90RMBHCcgd/hkum2UQ5eY= -github.com/libp2p/go-libp2p-core v0.20.0/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY= -github.com/libp2p/go-libp2p-kad-dht v0.18.0 h1:akqO3gPMwixR7qFSFq70ezRun97g5hrA/lBW9jrjUYM= -github.com/libp2p/go-libp2p-kad-dht v0.18.0/go.mod h1:Gb92MYIPm3K2pJLGn8wl0m8wiKDvHrYpg+rOd0GzzPA= -github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= -github.com/libp2p/go-libp2p-kbucket v0.4.7 h1:spZAcgxifvFZHBD8tErvppbnNiKA5uokDu3CV7axu70= -github.com/libp2p/go-libp2p-kbucket v0.4.7/go.mod h1:XyVo99AfQH0foSf176k4jY1xUJ2+jUJIZCSDm7r2YKk= -github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.8.0 h1:bzTG693TA1Ju/zKmUCQzDLSqiJnyRFVwPpuloZ/OZtI= -github.com/libp2p/go-libp2p-peerstore v0.8.0/go.mod h1:9geHWmNA3YDlQBjL/uPEJD6vpDK12aDNlUNHJ6kio/s= -github.com/libp2p/go-libp2p-pubsub v0.8.1 h1:hSw09NauFUaA0FLgQPBJp6QOy0a2n+HSkb8IeOx8OnY= -github.com/libp2p/go-libp2p-pubsub v0.8.1/go.mod h1:e4kT+DYjzPUYGZeWk4I+oxCSYTXizzXii5LDRRhjKSw= -github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= +github.com/libp2p/go-libp2p v0.28.2 h1:lO/g0ccVru6nUVHyLE7C1VRr7B2AFp9cvHhf+l+Te6w= +github.com/libp2p/go-libp2p v0.28.2/go.mod h1:fOLgCNgLiWFdmtXyQBwmuCpukaYOA+yw4rnBiScDNmI= +github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= +github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= +github.com/libp2p/go-libp2p-kad-dht v0.23.0 h1:sxE6LxLopp79eLeV695n7+c77V/Vn4AMF28AdM/XFqM= +github.com/libp2p/go-libp2p-kad-dht v0.23.0/go.mod h1:oO5N308VT2msnQI6qi5M61wzPmJYg7Tr9e16m5n7uDU= +github.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9GiSErbQQo0= +github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0= +github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= +github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= -github.com/libp2p/go-libp2p-routing-helpers v0.2.3/go.mod h1:795bh+9YeoFl99rMASoiVgHdi5bjack0N1+AFAdbvBw= -github.com/libp2p/go-libp2p-testing v0.11.0/go.mod h1:qG4sF27dfKFoK9KlVzK2y52LQKhp0VEmLjV5aDqr1Hg= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= -github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= -github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= -github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= -github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= -github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= -github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE= -github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= -github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= -github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= -github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-yamux/v3 v3.1.2 h1:lNEy28MBk1HavUAlzKgShp+F6mn/ea1nDYWftZhFW9Q= -github.com/libp2p/go-yamux/v3 v3.1.2/go.mod h1:jeLEQgLXqE2YqX1ilAClIfCMDY+0uXQUKmmb/qp0gT4= -github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= -github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU= -github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= +github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= +github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= +github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= +github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= +github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= +github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= +github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= -github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= -github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= -github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU= -github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= -github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= +github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= +github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -669,143 +485,119 @@ github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKo github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base32 v0.0.4 h1:+qMh4a2f37b4xTNs6mqitDinryCI+tfO2dRVMN9mjSE= -github.com/multiformats/go-base32 v0.0.4/go.mod h1:jNLFzjPZtp3aIARHbJRZIaPuspdH0J6q39uUM5pnABM= -github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= +github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= -github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= -github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= -github.com/multiformats/go-multiaddr v0.4.1/go.mod h1:3afI9HfVW8csiF8UZqtpYRiDyew8pRX7qLIGHu9FLuM= -github.com/multiformats/go-multiaddr v0.6.0 h1:qMnoOPj2s8xxPU5kZ57Cqdr0hHhARz7mFsPMIiYNqzg= -github.com/multiformats/go-multiaddr v0.6.0/go.mod h1:F4IpaKZuPP360tOMn2Tpyu0At8w23aRyVqeK0DbFeGM= +github.com/multiformats/go-multiaddr v0.9.0 h1:3h4V1LHIk5w4hJHekMKWALPXErDfz/sggzwC/NcqbDQ= +github.com/multiformats/go-multiaddr v0.9.0/go.mod h1:mI67Lb1EeTOYb8GQfL/7wpIZwc46ElrvzhYnoJOmTT0= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= -github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= -github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= -github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= -github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues= -github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE= -github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= -github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= +github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= +github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= +github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= +github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= -github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= -github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= -github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= -github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= +github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= +github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= +github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= +github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/multiversx/concurrent-map v0.1.4 h1:hdnbM8VE4b0KYJaGY5yJS2aNIW9TFFsUYwbO0993uPI= github.com/multiversx/concurrent-map v0.1.4/go.mod h1:8cWFRJDOrWHOTNSqgYCUvwT7c7eFQ4U2vKMOp4A/9+o= -github.com/multiversx/mx-chain-core-go v1.1.30/go.mod h1:8gGEQv6BWuuJwhd25qqhCOZbBSv9mk+hLeKvinSaSMk= -github.com/multiversx/mx-chain-core-go v1.1.31/go.mod h1:8gGEQv6BWuuJwhd25qqhCOZbBSv9mk+hLeKvinSaSMk= -github.com/multiversx/mx-chain-core-go v1.1.33 h1:qk+TlaOhHpu+9VncL3yowjY4KU8uJ0oSdPfU7SgVDnk= -github.com/multiversx/mx-chain-core-go v1.1.33/go.mod h1:8gGEQv6BWuuJwhd25qqhCOZbBSv9mk+hLeKvinSaSMk= -github.com/multiversx/mx-chain-crypto-go v1.2.5 h1:tuq3BUNMhKud5DQbZi9DiVAAHUXypizy8zPH0NpTGZk= -github.com/multiversx/mx-chain-crypto-go v1.2.5/go.mod h1:teqhNyWEqfMPgNn8sgWXlgtJ1a36jGCnhs/tRpXW6r4= -github.com/multiversx/mx-chain-es-indexer-go v1.3.14/go.mod h1:IV42GfhkqQ5vVO0OzGaF/ejp8TQrLkNo4LSB3TPnVhg= -github.com/multiversx/mx-chain-go v1.4.15 h1:uQgAW+O493dEh/Tf5AfUkjp3xCoraTLyaOKMIi38rP4= -github.com/multiversx/mx-chain-go v1.4.15/go.mod h1:KxHY/qUXQCcjloGEsk5i69YJ21y0c//C354aPcDSPBk= -github.com/multiversx/mx-chain-logger-go v1.0.11 h1:DFsHa+sc5fKwhDR50I8uBM99RTDTEW68ESyr5ALRDwE= -github.com/multiversx/mx-chain-logger-go v1.0.11/go.mod h1:1srDkP0DQucWQ+rYfaq0BX2qLnULsUdRPADpYUTM6dA= -github.com/multiversx/mx-chain-p2p-go v1.0.10 h1:CYCuI0SP8Pt9K0TcJjUyxK7ByvWi2FXNUihy0iCEVIA= -github.com/multiversx/mx-chain-p2p-go v1.0.10/go.mod h1:j9Ueo2ptCnL7TQvQg6KS/KWAoJEJpjkPgE5ZTaqEAn4= -github.com/multiversx/mx-chain-storage-go v1.0.7 h1:UqLo/OLTD3IHiE/TB/SEdNRV1GG2f1R6vIP5ehHwCNw= -github.com/multiversx/mx-chain-storage-go v1.0.7/go.mod h1:gtKoV32Cg2Uy8deHzF8Ud0qAl0zv92FvWgPSYIP0Zmg= -github.com/multiversx/mx-chain-vm-common-go v1.3.34/go.mod h1:sZ2COLCxvf2GxAAJHGmGqWybObLtFuk2tZUyGqnMXE8= -github.com/multiversx/mx-chain-vm-common-go v1.3.36/go.mod h1:sZ2COLCxvf2GxAAJHGmGqWybObLtFuk2tZUyGqnMXE8= -github.com/multiversx/mx-chain-vm-common-go v1.3.37 h1:KeK6JCjeNUOHC5Z12/CTQIa8Z1at0dnnL9hY1LNrHS8= -github.com/multiversx/mx-chain-vm-common-go v1.3.37/go.mod h1:sZ2COLCxvf2GxAAJHGmGqWybObLtFuk2tZUyGqnMXE8= -github.com/multiversx/mx-chain-vm-v1_2-go v1.2.50/go.mod h1:e3uYdgoKzs3puaznbmSjDcRisJc5Do4tpg7VqyYwoek= -github.com/multiversx/mx-chain-vm-v1_3-go v1.3.51/go.mod h1:oKj32V2nkd+KGNOL6emnwVkDRPpciwHHDqBmeorcL8k= -github.com/multiversx/mx-chain-vm-v1_4-go v1.4.77/go.mod h1:3IaAOHc1JfxL5ywQZIrcaHQu5+CVdZNDaoY64NGOtUE= -github.com/multiversx/mx-components-big-int v0.1.1/go.mod h1:0QrcFdfeLgJ/am10HGBeH0G0DNF+0Qx1E4DS/iozQls= -github.com/multiversx/mx-sdk-go v1.3.2 h1:eMp2YszrNoPSBQWpVFL/w+dRdMeZsTVzYq4teCdk/8Q= -github.com/multiversx/mx-sdk-go v1.3.2/go.mod h1:xVDCcASI456+TQDYGPdaD8GLzLhDVVXfnlrANSiRmz8= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/multiversx/mx-chain-communication-go v1.0.14 h1:YhAUDjBBpc5h5W0A7LHLXUMIMeCgwgGvkqfAPbFqsno= +github.com/multiversx/mx-chain-communication-go v1.0.14/go.mod h1:qYCqgk0h+YpcTA84jHIpCBy6UShRwmXzHSCcdfwNrkw= +github.com/multiversx/mx-chain-core-go v1.2.20 h1:jOQ10LxxUqECnuqUYeBBT6VoZcpJDdYgOvsSGtifDdI= +github.com/multiversx/mx-chain-core-go v1.2.20/go.mod h1:B5zU4MFyJezmEzCsAHE9YNULmGCm2zbPHvl9hazNxmE= +github.com/multiversx/mx-chain-crypto-go v1.2.11 h1:MNPJoiTJA5/tedYrI0N22OorbsKDESWG0SF8MCJwcJI= +github.com/multiversx/mx-chain-crypto-go v1.2.11/go.mod h1:pcZutPdfLiAFytzCU3LxU3s8cXkvpNqquyitFSfoF3o= +github.com/multiversx/mx-chain-es-indexer-go v1.4.21 h1:rzxXCkgOsqj67GRYtqzKuf9XgHwnZLTZhU90Ck3VbrE= +github.com/multiversx/mx-chain-es-indexer-go v1.4.21/go.mod h1:V9xxOBkfV7GjN4K5SODaOetoGVpQm4snibMVPCjL0Kk= +github.com/multiversx/mx-chain-go v1.7.13-patch2 h1:9eImJq7KZXG8EsXDlkA9bcw7/t9P7zshoE2elkeoXB4= +github.com/multiversx/mx-chain-go v1.7.13-patch2/go.mod h1:Edv8GTNCBicT5e+Oa+2stv8i03A0lxqwr9+/7JigSs4= +github.com/multiversx/mx-chain-logger-go v1.0.14 h1:PRMpAvXE7Nec2d//QNmbYfKVHMomOKmcN4UXurQWX9o= +github.com/multiversx/mx-chain-logger-go v1.0.14/go.mod h1:bDfHSdwqIimn7Gp8w+SH5KlDuGzJ//nlyEANAaTSc3o= +github.com/multiversx/mx-chain-scenario-go v1.4.3 h1:9xeVB8TOsolXS4YEr1CZ/VZr5Qk0X+nde8nRGnxJICo= +github.com/multiversx/mx-chain-scenario-go v1.4.3/go.mod h1:Bd7/Xs3mWM6pX/REHK5dfpf3MUfjMZ7li09cfCxg2ac= +github.com/multiversx/mx-chain-storage-go v1.0.15 h1:PDyP1uouAVjR32dFgM+7iaQBdReD/tKBJj10JbxXvaE= +github.com/multiversx/mx-chain-storage-go v1.0.15/go.mod h1:GZUK3sqf5onsWS/0ZPWjDCBjAL22FigQPUh252PAVk0= +github.com/multiversx/mx-chain-vm-common-go v1.5.12 h1:Q8F6DE7XhgHtWgg2rozSv4Tv5fE3ENkJz6mjRoAfht8= +github.com/multiversx/mx-chain-vm-common-go v1.5.12/go.mod h1:Sv6iS1okB6gy3HAsW6KHYtAxShNAfepKLtu//AURI8c= +github.com/multiversx/mx-chain-vm-go v1.5.29 h1:Ovz5/WM9KbD3YKRafdKI4RwtsNN36AGeNw81LZAhE70= +github.com/multiversx/mx-chain-vm-go v1.5.29/go.mod h1:n0SbVEAhIflreAGi7BnfWg4p4VHh4G8ArbvYQZsZsKQ= +github.com/multiversx/mx-chain-vm-v1_2-go v1.2.67 h1:W0bwj5zXM2JEeOEqfKTZE1ecuSJwTuRZZrl9oircRc0= +github.com/multiversx/mx-chain-vm-v1_2-go v1.2.67/go.mod h1:lrDQWpv1yZHlX6ZgWJsTMxxOkeoVTKLQsl1+mr50Z24= +github.com/multiversx/mx-chain-vm-v1_3-go v1.3.68 h1:px2YHay6BSVheLxb3gdZQX0enlqKzu6frngWEZRtr6g= +github.com/multiversx/mx-chain-vm-v1_3-go v1.3.68/go.mod h1:sIXRCenIR6FJtr3X/gDc60N6+v99Ai4hDsn6R5TKGnk= +github.com/multiversx/mx-chain-vm-v1_4-go v1.4.97 h1:fbYYqollxbIArcrC161Z9Qh5yJGW0Ax60m83Gz8+H1w= +github.com/multiversx/mx-chain-vm-v1_4-go v1.4.97/go.mod h1:56WJQio8SzOt3vWibaNkuGpqLlmTOGUSJqs3wMK69zw= +github.com/multiversx/mx-components-big-int v1.0.0 h1:Wkr8lSzK2nDqixOrrBa47VNuqdhV1m/aJhaP1EMaiS8= +github.com/multiversx/mx-components-big-int v1.0.0/go.mod h1:maIEMgHlNE2u78JaDD0oLzri+ShgU4okHfzP3LWGdQM= +github.com/multiversx/mx-sdk-go v1.4.1 h1:/zk7LDmnl1ovkmjkDejLRUtbjO7kmVQw8AlAyGNITBU= +github.com/multiversx/mx-sdk-go v1.4.1/go.mod h1:2kTQLFck47wtHpzdWrM3mrLlTypE5zn39JCbzN16cxs= +github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= +github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= @@ -813,74 +605,62 @@ github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= +github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= +github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= +github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= +github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= +github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= +github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= +github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v3 v3.21.2/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw= @@ -907,39 +687,28 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU= -github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -949,245 +718,177 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= +github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w= github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek= -github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= -github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8= -github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= -github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y= -github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= -github.com/warpfork/go-testmark v0.10.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= +github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= -github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= +go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0= -go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1195,276 +896,136 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1474,14 +1035,12 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1489,20 +1048,19 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= -gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -1515,19 +1073,13 @@ grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJd honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= +lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/integrationTests/mock/ethereumChainMock.go b/integrationTests/mock/ethereumChainMock.go index f47dafee..d4c751af 100644 --- a/integrationTests/mock/ethereumChainMock.go +++ b/integrationTests/mock/ethereumChainMock.go @@ -5,7 +5,9 @@ import ( "fmt" "math/big" "sync" + "sync/atomic" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -31,20 +33,34 @@ type EthereumChainMock struct { batches map[uint64]*contract.Batch deposits map[uint64][]contract.Deposit proposedTransfer *EthereumProposedTransfer - GetStatusesAfterExecutionHandler func() []byte + totalBalances map[common.Address]*big.Int + mintBalances map[common.Address]*big.Int + burnBalances map[common.Address]*big.Int + mintBurnTokens map[common.Address]bool + nativeTokens map[common.Address]bool + whitelistedTokens map[common.Address]bool + GetStatusesAfterExecutionHandler func() ([]byte, bool) ProcessFinishedHandler func() quorum int relayers []common.Address ProposeMultiTransferEsdtBatchCalled func() BalanceAtCalled func(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + FilterLogsCalled func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) + finalNonce uint64 } // NewEthereumChainMock - func NewEthereumChainMock() *EthereumChainMock { return &EthereumChainMock{ - nonces: make(map[common.Address]uint64), - batches: make(map[uint64]*contract.Batch), - deposits: make(map[uint64][]contract.Deposit), + nonces: make(map[common.Address]uint64), + batches: make(map[uint64]*contract.Batch), + deposits: make(map[uint64][]contract.Deposit), + totalBalances: make(map[common.Address]*big.Int), + mintBalances: make(map[common.Address]*big.Int), + burnBalances: make(map[common.Address]*big.Int), + mintBurnTokens: make(map[common.Address]bool), + nativeTokens: make(map[common.Address]bool), + whitelistedTokens: make(map[common.Address]bool), } } @@ -68,29 +84,37 @@ func (mock *EthereumChainMock) Name() string { } // GetBatch - -func (mock *EthereumChainMock) GetBatch(_ context.Context, batchNonce *big.Int) (contract.Batch, error) { +func (mock *EthereumChainMock) GetBatch(_ context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { mock.mutState.RLock() defer mock.mutState.RUnlock() batch, found := mock.batches[batchNonce.Uint64()] if !found { - return contract.Batch{}, fmt.Errorf("batch %d not found", batchNonce) + return contract.Batch{ + Nonce: big.NewInt(0), + }, false, nil } - return *batch, nil + finalNonce := atomic.LoadUint64(&mock.finalNonce) + isFinal := finalNonce >= batchNonce.Uint64() + + return *batch, isFinal, nil } // GetBatchDeposits - -func (mock *EthereumChainMock) GetBatchDeposits(_ context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { +func (mock *EthereumChainMock) GetBatchDeposits(_ context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { mock.mutState.RLock() defer mock.mutState.RUnlock() deposits, found := mock.deposits[batchNonce.Uint64()] if !found { - return make([]contract.Deposit, 0), fmt.Errorf("deposits for batch %d not found", batchNonce) + return make([]contract.Deposit, 0), false, nil } - return deposits, nil + finalNonce := atomic.LoadUint64(&mock.finalNonce) + isFinal := finalNonce >= batchNonce.Uint64() + + return deposits, isFinal, nil } // GetRelayers - @@ -232,8 +256,10 @@ func (mock *EthereumChainMock) SetQuorum(quorum int) { } // GetStatusesAfterExecution - -func (mock *EthereumChainMock) GetStatusesAfterExecution(_ context.Context, _ *big.Int) ([]byte, error) { - return mock.GetStatusesAfterExecutionHandler(), nil +func (mock *EthereumChainMock) GetStatusesAfterExecution(_ context.Context, _ *big.Int) ([]byte, bool, error) { + statuses, isFinal := mock.GetStatusesAfterExecutionHandler() + + return statuses, isFinal, nil } // GetLastProposedTransfer - @@ -252,12 +278,125 @@ func (mock *EthereumChainMock) BalanceAt(ctx context.Context, account common.Add return big.NewInt(0), nil } +// FilterLogs - +func (mock *EthereumChainMock) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if mock.FilterLogsCalled != nil { + return mock.FilterLogsCalled(ctx, q) + } + + return []types.Log{}, nil +} + // IsPaused - func (mock *EthereumChainMock) IsPaused(_ context.Context) (bool, error) { return false, nil } +// TotalBalances - +func (mock *EthereumChainMock) TotalBalances(_ context.Context, account common.Address) (*big.Int, error) { + mock.mutState.RLock() + defer mock.mutState.RUnlock() + + return getValueFromBalanceMap(mock.totalBalances, account), nil +} + +// UpdateTotalBalances - +func (mock *EthereumChainMock) UpdateTotalBalances(account common.Address, value *big.Int) { + mock.mutState.Lock() + mock.totalBalances[account] = value + mock.mutState.Unlock() +} + +// MintBalances - +func (mock *EthereumChainMock) MintBalances(_ context.Context, account common.Address) (*big.Int, error) { + mock.mutState.RLock() + defer mock.mutState.RUnlock() + + return getValueFromBalanceMap(mock.mintBalances, account), nil +} + +// UpdateMintBalances - +func (mock *EthereumChainMock) UpdateMintBalances(account common.Address, value *big.Int) { + mock.mutState.Lock() + mock.mintBalances[account] = value + mock.mutState.Unlock() +} + +// BurnBalances - +func (mock *EthereumChainMock) BurnBalances(_ context.Context, account common.Address) (*big.Int, error) { + mock.mutState.RLock() + defer mock.mutState.RUnlock() + + return getValueFromBalanceMap(mock.burnBalances, account), nil +} + +// UpdateBurnBalances - +func (mock *EthereumChainMock) UpdateBurnBalances(account common.Address, value *big.Int) { + mock.mutState.Lock() + mock.burnBalances[account] = value + mock.mutState.Unlock() +} + +// MintBurnTokens - +func (mock *EthereumChainMock) MintBurnTokens(_ context.Context, account common.Address) (bool, error) { + mock.mutState.RLock() + defer mock.mutState.RUnlock() + + return mock.mintBurnTokens[account], nil +} + +// UpdateMintBurnTokens - +func (mock *EthereumChainMock) UpdateMintBurnTokens(account common.Address, value bool) { + mock.mutState.Lock() + mock.mintBurnTokens[account] = value + mock.mutState.Unlock() +} + +// NativeTokens - +func (mock *EthereumChainMock) NativeTokens(_ context.Context, account common.Address) (bool, error) { + mock.mutState.RLock() + defer mock.mutState.RUnlock() + + return mock.nativeTokens[account], nil +} + +// UpdateNativeTokens - +func (mock *EthereumChainMock) UpdateNativeTokens(account common.Address, value bool) { + mock.mutState.Lock() + mock.nativeTokens[account] = value + mock.mutState.Unlock() +} + +// WhitelistedTokens - +func (mock *EthereumChainMock) WhitelistedTokens(_ context.Context, account common.Address) (bool, error) { + mock.mutState.RLock() + defer mock.mutState.RUnlock() + + return mock.whitelistedTokens[account], nil +} + +// UpdateWhitelistedTokens - +func (mock *EthereumChainMock) UpdateWhitelistedTokens(account common.Address, value bool) { + mock.mutState.Lock() + mock.whitelistedTokens[account] = value + mock.mutState.Unlock() +} + +// SetFinalNonce - +func (mock *EthereumChainMock) SetFinalNonce(nonce uint64) { + atomic.StoreUint64(&mock.finalNonce, nonce) +} + // IsInterfaceNil - func (mock *EthereumChainMock) IsInterfaceNil() bool { return mock == nil } + +func getValueFromBalanceMap(m map[common.Address]*big.Int, address common.Address) *big.Int { + value := m[address] + if value == nil { + return big.NewInt(0) + } + + return big.NewInt(0).Set(value) +} diff --git a/integrationTests/mock/mockWriter.go b/integrationTests/mock/mockWriter.go new file mode 100644 index 00000000..5db37ca7 --- /dev/null +++ b/integrationTests/mock/mockWriter.go @@ -0,0 +1,48 @@ +package mock + +import "strings" + +type mockLogObserver struct { + expectedStringInLog string + exceptStrings []string + logFoundChan chan struct{} +} + +// NewMockLogObserver returns a new instance of mockLogObserver +func NewMockLogObserver(expectedStringInLog string, exceptStrings ...string) *mockLogObserver { + return &mockLogObserver{ + expectedStringInLog: expectedStringInLog, + exceptStrings: exceptStrings, + logFoundChan: make(chan struct{}, 1), + } +} + +// Write is called by the logger +func (observer *mockLogObserver) Write(log []byte) (n int, err error) { + str := string(log) + if !strings.Contains(str, observer.expectedStringInLog) { + return 0, nil + } + if observer.stringIsExcepted(str) { + return 0, nil + } + + observer.logFoundChan <- struct{}{} + + return 0, nil +} + +func (observer *mockLogObserver) stringIsExcepted(str string) bool { + for _, exceptString := range observer.exceptStrings { + if strings.Contains(str, exceptString) { + return true + } + } + + return false +} + +// LogFoundChan returns the internal chan +func (observer *mockLogObserver) LogFoundChan() chan struct{} { + return observer.logFoundChan +} diff --git a/integrationTests/mock/multiversXChainMock.go b/integrationTests/mock/multiversXChainMock.go index 91f525ad..95a5953a 100644 --- a/integrationTests/mock/multiversXChainMock.go +++ b/integrationTests/mock/multiversXChainMock.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/integrationTests" "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/data/api" "github.com/multiversx/mx-chain-core-go/data/transaction" logger "github.com/multiversx/mx-chain-logger-go" sdkCore "github.com/multiversx/mx-sdk-go/core" @@ -125,6 +126,16 @@ func (mock *MultiversXChainMock) GetAccount(_ context.Context, address sdkCore.A return mock.accounts.getOrCreate(address), nil } +// GetTransactionInfoWithResults - +func (mock *MultiversXChainMock) GetTransactionInfoWithResults(_ context.Context, _ string) (*data.TransactionInfo, error) { + return &data.TransactionInfo{}, nil +} + +// ProcessTransactionStatus - +func (mock *MultiversXChainMock) ProcessTransactionStatus(_ context.Context, _ string) (transaction.TxStatus, error) { + return "", nil +} + // AddRelayer - func (mock *MultiversXChainMock) AddRelayer(address sdkCore.AddressHandler) { mock.mutState.Lock() @@ -150,11 +161,11 @@ func (mock *MultiversXChainMock) SetLastExecutedEthTxId(lastExecutedEthTxId uint } // AddTokensPair - -func (mock *MultiversXChainMock) AddTokensPair(erc20 common.Address, ticker string) { +func (mock *MultiversXChainMock) AddTokensPair(erc20 common.Address, ticker string, isNativeToken, isMintBurnToken bool, totalBalance, mintBalances, burnBalances *big.Int) { mock.mutState.Lock() defer mock.mutState.Unlock() - mock.addTokensPair(erc20, ticker) + mock.addTokensPair(erc20, ticker, isNativeToken, isMintBurnToken, totalBalance, mintBalances, burnBalances) } // SetQuorum - @@ -205,6 +216,23 @@ func (mock *MultiversXChainMock) AddDepositToCurrentBatch(deposit MultiversXDepo mock.mutState.Unlock() } +// GetESDTTokenData - +func (mock *MultiversXChainMock) GetESDTTokenData(_ context.Context, _ sdkCore.AddressHandler, tokenIdentifier string, _ api.AccountQueryOptions) (*data.ESDTFungibleTokenData, error) { + mock.mutState.RLock() + defer mock.mutState.RUnlock() + + isMintBurn, found := mock.mintBurnTokens[tokenIdentifier] + balance := mock.totalBalances[tokenIdentifier] + if found && isMintBurn { + balance = big.NewInt(0) + } + + return &data.ESDTFungibleTokenData{ + TokenIdentifier: tokenIdentifier, + Balance: balance.String(), + }, nil +} + // IsInterfaceNil - func (mock *MultiversXChainMock) IsInterfaceNil() bool { return mock == nil diff --git a/integrationTests/mock/multiversXContractStateMock.go b/integrationTests/mock/multiversXContractStateMock.go index 2df2e30f..8326b5dd 100644 --- a/integrationTests/mock/multiversXContractStateMock.go +++ b/integrationTests/mock/multiversXContractStateMock.go @@ -32,6 +32,7 @@ type Transfer struct { Token string Amount *big.Int Nonce *big.Int + Data []byte } // MultiversXPendingBatch - @@ -169,23 +170,29 @@ func (mock *multiversXContractStateMock) createProposedTransfer(dataSplit []stri BatchId: big.NewInt(0).SetBytes(buff), } - for i := 2; i < len(dataSplit); i += 5 { - from, errDecode := hex.DecodeString(dataSplit[i]) + currentIndex := 2 + for currentIndex < len(dataSplit) { + from, errDecode := hex.DecodeString(dataSplit[currentIndex]) if errDecode != nil { panic(errDecode) } - to, errDecode := hex.DecodeString(dataSplit[i+1]) + to, errDecode := hex.DecodeString(dataSplit[currentIndex+1]) if errDecode != nil { panic(errDecode) } - amountBytes, errDecode := hex.DecodeString(dataSplit[i+3]) + amountBytes, errDecode := hex.DecodeString(dataSplit[currentIndex+3]) if errDecode != nil { panic(errDecode) } - nonceBytes, errDecode := hex.DecodeString(dataSplit[i+4]) + nonceBytes, errDecode := hex.DecodeString(dataSplit[currentIndex+4]) + if errDecode != nil { + panic(errDecode) + } + + dataBytes, errDecode := hex.DecodeString(dataSplit[currentIndex+5]) if errDecode != nil { panic(errDecode) } @@ -193,12 +200,15 @@ func (mock *multiversXContractStateMock) createProposedTransfer(dataSplit []stri t := Transfer{ From: from, To: to, - Token: dataSplit[i+2], + Token: dataSplit[currentIndex+2], Amount: big.NewInt(0).SetBytes(amountBytes), Nonce: big.NewInt(0).SetBytes(nonceBytes), + Data: dataBytes, } + indexIncrementValue := 6 transfer.Transfers = append(transfer.Transfers, t) + currentIndex += indexIncrementValue } hash, err := core.CalculateHash(integrationTests.TestMarshalizer, integrationTests.TestHasher, transfer) @@ -238,6 +248,8 @@ func (mock *multiversXContractStateMock) processVmRequests(vmRequest *data.VmVal return mock.vmRequestGetErc20AddressForTokenId(vmRequest), nil case "getCurrentTxBatch": return mock.vmRequestGetCurrentPendingBatch(vmRequest), nil + case "getBatch": + return mock.vmRequestGetBatch(vmRequest), nil case "getAllStakedRelayers": return mock.vmRequestGetAllStakedRelayers(vmRequest), nil case "getLastExecutedEthBatchId": @@ -248,6 +260,18 @@ func (mock *multiversXContractStateMock) processVmRequests(vmRequest *data.VmVal return mock.vmRequestSigned(vmRequest), nil case "isPaused": return mock.vmRequestIsPaused(vmRequest), nil + case "isMintBurnToken": + return mock.vmRequestIsMintBurnToken(vmRequest), nil + case "isNativeToken": + return mock.vmRequestIsNativeToken(vmRequest), nil + case "getTotalBalances": + return mock.vmRequestGetTotalBalances(vmRequest), nil + case "getMintBalances": + return mock.vmRequestGetMintBalances(vmRequest), nil + case "getBurnBalances": + return mock.vmRequestGetBurnBalances(vmRequest), nil + case "getLastBatchId": + return mock.vmRequestGetLastBatchId(vmRequest), nil } panic("unimplemented function: " + vmRequest.FuncName) @@ -308,7 +332,7 @@ func (mock *multiversXContractStateMock) vmRequestGetStatusesAfterExecution(_ *d } func (mock *multiversXContractStateMock) sign(dataSplit []string, tx *transaction.FrontendTransaction) { - actionID := getActionIDFromString(dataSplit[1]) + actionID := getBigIntFromString(dataSplit[1]) if !mock.actionIDExists(actionID) { panic(fmt.Sprintf("attempted to sign on a missing action ID: %v as big int, raw: %s", actionID, dataSplit[1])) } @@ -322,7 +346,7 @@ func (mock *multiversXContractStateMock) sign(dataSplit []string, tx *transactio } func (mock *multiversXContractStateMock) performAction(dataSplit []string, _ *transaction.FrontendTransaction) { - actionID := getActionIDFromString(dataSplit[1]) + actionID := getBigIntFromString(dataSplit[1]) if !mock.actionIDExists(actionID) { panic(fmt.Sprintf("attempted to perform on a missing action ID: %v as big int, raw: %s", actionID, dataSplit[1])) } @@ -338,7 +362,7 @@ func (mock *multiversXContractStateMock) performAction(dataSplit []string, _ *tr } func (mock *multiversXContractStateMock) vmRequestWasActionExecuted(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { - actionID := getActionIDFromString(vmRequest.Args[0]) + actionID := getBigIntFromString(vmRequest.Args[0]) if mock.performedAction == nil { return createOkVmResponse([][]byte{BoolToByteSlice(false)}) @@ -368,7 +392,7 @@ func (mock *multiversXContractStateMock) actionIDExists(actionID *big.Int) bool } func (mock *multiversXContractStateMock) vmRequestQuorumReached(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { - actionID := getActionIDFromString(vmRequest.Args[0]) + actionID := getBigIntFromString(vmRequest.Args[0]) m, found := mock.signedActionIDs[actionID.String()] if !found { return createOkVmResponse([][]byte{BoolToByteSlice(false)}) @@ -412,6 +436,10 @@ func (mock *multiversXContractStateMock) vmRequestGetCurrentPendingBatch(_ *data return createOkVmResponse(make([][]byte, 0)) } + return mock.responseWithPendingBatch() +} + +func (mock *multiversXContractStateMock) responseWithPendingBatch() *data.VmValuesResponseData { args := [][]byte{mock.pendingBatch.Nonce.Bytes()} // first non-empty slice for _, deposit := range mock.pendingBatch.MultiversXDeposits { args = append(args, make([]byte, 0)) // mocked block nonce @@ -424,13 +452,26 @@ func (mock *multiversXContractStateMock) vmRequestGetCurrentPendingBatch(_ *data return createOkVmResponse(args) } +func (mock *multiversXContractStateMock) vmRequestGetBatch(request *data.VmValueRequest) *data.VmValuesResponseData { + if mock.pendingBatch == nil { + return createOkVmResponse(make([][]byte, 0)) + } + + nonce := getBigIntFromString(request.Args[0]) + if nonce.Cmp(mock.pendingBatch.Nonce) == 0 { + return mock.responseWithPendingBatch() + } + + return createOkVmResponse(make([][]byte, 0)) +} + func (mock *multiversXContractStateMock) setPendingBatch(pendingBatch *MultiversXPendingBatch) { mock.pendingBatch = pendingBatch } func (mock *multiversXContractStateMock) vmRequestSigned(request *data.VmValueRequest) *data.VmValuesResponseData { hexAddress := request.Args[0] - actionID := getActionIDFromString(request.Args[1]) + actionID := getBigIntFromString(request.Args[1]) actionIDMap, found := mock.signedActionIDs[actionID.String()] if !found { @@ -443,9 +484,10 @@ func (mock *multiversXContractStateMock) vmRequestSigned(request *data.VmValueRe } address := data.NewAddressFromBytes(addressBytes) - _, found = actionIDMap[address.AddressAsBech32String()] + bech32Address, _ := address.AddressAsBech32String() + _, found = actionIDMap[bech32Address] if !found { - log.Error("action ID not found", "address", address.AddressAsBech32String()) + log.Error("action ID not found", "address", bech32Address) } return createOkVmResponse([][]byte{BoolToByteSlice(found)}) @@ -455,7 +497,44 @@ func (mock *multiversXContractStateMock) vmRequestIsPaused(_ *data.VmValueReques return createOkVmResponse([][]byte{BoolToByteSlice(false)}) } -func getActionIDFromString(data string) *big.Int { +func (mock *multiversXContractStateMock) vmRequestIsMintBurnToken(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { + address := vmRequest.Args[0] + + return createOkVmResponse([][]byte{BoolToByteSlice(mock.isMintBurnToken(address))}) +} + +func (mock *multiversXContractStateMock) vmRequestIsNativeToken(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { + address := vmRequest.Args[0] + + return createOkVmResponse([][]byte{BoolToByteSlice(mock.isNativeToken(address))}) +} + +func (mock *multiversXContractStateMock) vmRequestGetTotalBalances(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { + address := vmRequest.Args[0] + + return createOkVmResponse([][]byte{mock.getTotalBalances(address).Bytes()}) +} + +func (mock *multiversXContractStateMock) vmRequestGetMintBalances(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { + address := vmRequest.Args[0] + + return createOkVmResponse([][]byte{mock.getMintBalances(address).Bytes()}) +} + +func (mock *multiversXContractStateMock) vmRequestGetBurnBalances(vmRequest *data.VmValueRequest) *data.VmValuesResponseData { + address := vmRequest.Args[0] + + return createOkVmResponse([][]byte{mock.getBurnBalances(address).Bytes()}) +} + +func (mock *multiversXContractStateMock) vmRequestGetLastBatchId(_ *data.VmValueRequest) *data.VmValuesResponseData { + if mock.pendingBatch == nil { + return createOkVmResponse([][]byte{big.NewInt(0).Bytes()}) + } + return createOkVmResponse([][]byte{mock.pendingBatch.Nonce.Bytes()}) +} + +func getBigIntFromString(data string) *big.Int { buff, err := hex.DecodeString(data) if err != nil { panic(err) diff --git a/integrationTests/mock/tokensRegistryMock.go b/integrationTests/mock/tokensRegistryMock.go index 802b3eba..3ac3d36c 100644 --- a/integrationTests/mock/tokensRegistryMock.go +++ b/integrationTests/mock/tokensRegistryMock.go @@ -2,6 +2,7 @@ package mock import ( "encoding/hex" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/multiversx/mx-bridge-eth-go/integrationTests" @@ -11,20 +12,43 @@ import ( type tokensRegistryMock struct { ethToMultiversX map[common.Address]string multiversXToEth map[string]common.Address + mintBurnTokens map[string]bool + nativeTokens map[string]bool + totalBalances map[string]*big.Int + mintBalances map[string]*big.Int + burnBalances map[string]*big.Int } -func (mock *tokensRegistryMock) addTokensPair(erc20Address common.Address, ticker string) { - integrationTests.Log.Info("added tokens pair", "ticker", ticker, "erc20 address", erc20Address.String()) +func (mock *tokensRegistryMock) addTokensPair(erc20Address common.Address, ticker string, isNativeToken, isMintBurnToken bool, totalBalance, mintBalances, burnBalances *big.Int) { + integrationTests.Log.Info("added tokens pair", "ticker", ticker, + "erc20 address", erc20Address.String(), "is native token", isNativeToken, "is mint burn token", isMintBurnToken, + "total balance", totalBalance, "mint balances", mintBalances, "burn balances", burnBalances) mock.ethToMultiversX[erc20Address] = ticker hexedTicker := hex.EncodeToString([]byte(ticker)) mock.multiversXToEth[hexedTicker] = erc20Address + + if isNativeToken { + mock.nativeTokens[hexedTicker] = true + } + if isMintBurnToken { + mock.mintBurnTokens[hexedTicker] = true + mock.mintBalances[hexedTicker] = mintBalances + mock.burnBalances[hexedTicker] = burnBalances + } else { + mock.totalBalances[hexedTicker] = totalBalance + } } func (mock *tokensRegistryMock) clearTokens() { mock.ethToMultiversX = make(map[common.Address]string) mock.multiversXToEth = make(map[string]common.Address) + mock.mintBurnTokens = make(map[string]bool) + mock.nativeTokens = make(map[string]bool) + mock.totalBalances = make(map[string]*big.Int) + mock.mintBalances = make(map[string]*big.Int) + mock.burnBalances = make(map[string]*big.Int) } func (mock *tokensRegistryMock) getTicker(erc20Address common.Address) string { @@ -44,3 +68,27 @@ func (mock *tokensRegistryMock) getErc20Address(ticker string) common.Address { return addr } + +func (mock *tokensRegistryMock) isMintBurnToken(ticker string) bool { + _, found := mock.mintBurnTokens[ticker] + + return found +} + +func (mock *tokensRegistryMock) isNativeToken(ticker string) bool { + _, found := mock.nativeTokens[ticker] + + return found +} + +func (mock *tokensRegistryMock) getTotalBalances(ticker string) *big.Int { + return mock.totalBalances[ticker] +} + +func (mock *tokensRegistryMock) getMintBalances(ticker string) *big.Int { + return mock.mintBalances[ticker] +} + +func (mock *tokensRegistryMock) getBurnBalances(ticker string) *big.Int { + return mock.burnBalances[ticker] +} diff --git a/integrationTests/relayers/common.go b/integrationTests/relayers/common.go index a2143093..20745a0b 100644 --- a/integrationTests/relayers/common.go +++ b/integrationTests/relayers/common.go @@ -4,11 +4,20 @@ import ( "context" "fmt" "math/big" + "path" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/multiversx/mx-bridge-eth-go/clients/chain" + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" bridgeTests "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" + chainConfig "github.com/multiversx/mx-chain-go/config" + logger "github.com/multiversx/mx-chain-logger-go" ) +var log = logger.GetOrCreate("integrationTests/relayers") + func createMockErc20ContractsHolder(tokens []common.Address, safeContractEthAddress common.Address, availableBalances []*big.Int) *bridgeTests.ERC20ContractsHolderStub { return &bridgeTests.ERC20ContractsHolderStub{ BalanceOfCalled: func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) { @@ -40,3 +49,76 @@ func availableTokensMapToSlices(erc20Map map[common.Address]*big.Int) ([]common. return tokens, availableBalances } + +func closeRelayers(relayers []bridgeComponents) { + for _, r := range relayers { + _ = r.Close() + } +} + +// CreateBridgeComponentsConfig - +func CreateBridgeComponentsConfig(index int, workingDir string, gasStationURL string) config.Config { + stateMachineConfig := config.ConfigStateMachine{ + StepDurationInMillis: 1000, + IntervalForLeaderInSeconds: 60, + } + + return config.Config{ + Eth: config.EthereumConfig{ + Chain: chain.Ethereum, + NetworkAddress: "mock", + MultisigContractAddress: "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c", + PrivateKeyFile: fmt.Sprintf("testdata/ethereum%d.sk", index), + IntervalToResendTxsInSeconds: 10, + GasLimitBase: 350000, + GasLimitForEach: 30000, + GasStation: config.GasStationConfig{ + Enabled: len(gasStationURL) > 0, + URL: gasStationURL, + PollingIntervalInSeconds: 1, + GasPriceMultiplier: 1, + GasPriceSelector: "SafeGasPrice", + MaxFetchRetries: 3, + MaximumAllowedGasPrice: math.MaxUint64 / 2, + RequestRetryDelayInSeconds: 1, + RequestTimeInSeconds: 1, + }, + MaxRetriesOnQuorumReached: 1, + IntervalToWaitForTransferInSeconds: 1, + ClientAvailabilityAllowDelta: 5, + EventsBlockRangeFrom: -5, + EventsBlockRangeTo: 50, + }, + MultiversX: config.MultiversXConfig{ + NetworkAddress: "mock", + MultisigContractAddress: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", + SafeContractAddress: "erd1qqqqqqqqqqqqqpgqtvnswnzxxz8susupesys0hvg7q2z5nawrcjq06qdus", + PrivateKeyFile: path.Join(workingDir, fmt.Sprintf("multiversx%d.pem", index)), + IntervalToResendTxsInSeconds: 10, + GasMap: testsCommon.CreateTestMultiversXGasMap(), + MaxRetriesOnQuorumReached: 1, + MaxRetriesOnWasTransferProposed: 3, + ClientAvailabilityAllowDelta: 5, + Proxy: config.ProxyConfig{ + CacherExpirationSeconds: 600, + RestAPIEntityType: "observer", + MaxNoncesDelta: 10, + FinalityCheck: true, + }, + }, + P2P: config.ConfigP2P{}, + StateMachine: map[string]config.ConfigStateMachine{ + "EthereumToMultiversX": stateMachineConfig, + "MultiversXToEthereum": stateMachineConfig, + }, + Relayer: config.ConfigRelayer{ + Marshalizer: chainConfig.MarshalizerConfig{ + Type: "json", + SizeCheckDelta: 10, + }, + RoleProvider: config.RoleProviderConfig{ + PollingIntervalInMillis: 1000, + }, + }, + } +} diff --git a/integrationTests/relayers/ethToMultiversX_test.go b/integrationTests/relayers/ethToMultiversX_test.go index b42828ce..da721ed7 100644 --- a/integrationTests/relayers/ethToMultiversX_test.go +++ b/integrationTests/relayers/ethToMultiversX_test.go @@ -1,36 +1,53 @@ +//go:build !slow + package relayers import ( "context" "encoding/hex" - "fmt" "math/big" "testing" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/multiversx/mx-bridge-eth-go/clients" - "github.com/multiversx/mx-bridge-eth-go/clients/chain" + "github.com/ethereum/go-ethereum/core/types" "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" "github.com/multiversx/mx-bridge-eth-go/config" - "github.com/multiversx/mx-bridge-eth-go/core" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-bridge-eth-go/factory" "github.com/multiversx/mx-bridge-eth-go/integrationTests" "github.com/multiversx/mx-bridge-eth-go/integrationTests/mock" "github.com/multiversx/mx-bridge-eth-go/status" "github.com/multiversx/mx-bridge-eth-go/testsCommon" - chainConfig "github.com/multiversx/mx-chain-go/config" + "github.com/multiversx/mx-bridge-eth-go/testsCommon/bridge" "github.com/multiversx/mx-chain-go/p2p" "github.com/multiversx/mx-chain-go/testscommon/statusHandler" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestRelayersShouldExecuteTransferFromEthToMultiversX(t *testing.T) { +const noGasStationURL = "" + +type argsForSCCallsTest struct { + providedScCallData []byte + expectedScCallData []byte +} + +func TestRelayersShouldExecuteTransfersFromEthToMultiversX(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } + t.Run("simple tokens transfers", func(t *testing.T) { + testRelayersShouldExecuteTransfersFromEthToMultiversX(t, false) + }) + t.Run("native tokens transfers", func(t *testing.T) { + testRelayersShouldExecuteTransfersFromEthToMultiversX(t, true) + }) +} + +func testRelayersShouldExecuteTransfersFromEthToMultiversX(t *testing.T, withNativeTokens bool) { safeContractEthAddress := testsCommon.CreateRandomEthereumAddress() token1Erc20 := testsCommon.CreateRandomEthereumAddress() ticker1 := "tck-000001" @@ -81,14 +98,232 @@ func TestRelayersShouldExecuteTransferFromEthToMultiversX(t *testing.T) { }) ethereumChainMock.AddBatch(batch) ethereumChainMock.SetQuorum(numRelayers) + ethereumChainMock.SetFinalNonce(batchNonceOnEthereum + 1) + + multiversXChainMock := mock.NewMultiversXChainMock() + + if !withNativeTokens { + ethereumChainMock.UpdateNativeTokens(token1Erc20, true) + ethereumChainMock.UpdateMintBurnTokens(token1Erc20, false) + ethereumChainMock.UpdateTotalBalances(token1Erc20, value1) + + ethereumChainMock.UpdateNativeTokens(token2Erc20, true) + ethereumChainMock.UpdateMintBurnTokens(token2Erc20, false) + ethereumChainMock.UpdateTotalBalances(token2Erc20, value2) + + multiversXChainMock.AddTokensPair(token1Erc20, ticker1, withNativeTokens, true, zero, zero, zero) + multiversXChainMock.AddTokensPair(token2Erc20, ticker2, withNativeTokens, true, zero, zero, zero) + } else { + ethereumChainMock.UpdateNativeTokens(token1Erc20, false) + ethereumChainMock.UpdateMintBurnTokens(token1Erc20, true) + ethereumChainMock.UpdateBurnBalances(token1Erc20, value1) + ethereumChainMock.UpdateMintBalances(token1Erc20, value1) + + ethereumChainMock.UpdateNativeTokens(token2Erc20, false) + ethereumChainMock.UpdateMintBurnTokens(token2Erc20, true) + ethereumChainMock.UpdateBurnBalances(token2Erc20, value2) + ethereumChainMock.UpdateMintBalances(token2Erc20, value2) + + multiversXChainMock.AddTokensPair(token1Erc20, ticker1, withNativeTokens, true, zero, zero, value1) + multiversXChainMock.AddTokensPair(token2Erc20, ticker2, withNativeTokens, true, zero, zero, value2) + } + + multiversXChainMock.SetLastExecutedEthBatchID(batchNonceOnEthereum) + multiversXChainMock.SetLastExecutedEthTxId(txNonceOnEthereum) + multiversXChainMock.GetStatusesAfterExecutionHandler = func() []byte { + return []byte{bridgeCore.Executed, bridgeCore.Rejected} + } + multiversXChainMock.SetQuorum(numRelayers) + + relayers := make([]bridgeComponents, 0, numRelayers) + defer closeRelayers(relayers) + + messengers := integrationTests.CreateLinkedMessengers(numRelayers) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*120) + defer cancel() + multiversXChainMock.ProcessFinishedHandler = func() { + log.Info("multiversXChainMock.ProcessFinishedHandler called") + asyncCancelCall(cancel, time.Second*5) + } + + for i := 0; i < numRelayers; i++ { + argsBridgeComponents := createMockBridgeComponentsArgs(i, messengers[i], multiversXChainMock, ethereumChainMock) + argsBridgeComponents.Configs.GeneralConfig.Eth.SafeContractAddress = safeContractEthAddress.Hex() + argsBridgeComponents.Erc20ContractsHolder = erc20ContractsHolder + relayer, err := factory.NewEthMultiversXBridgeComponents(argsBridgeComponents) + require.Nil(t, err) + + multiversXChainMock.AddRelayer(relayer.MultiversXRelayerAddress()) + ethereumChainMock.AddRelayer(relayer.EthereumRelayerAddress()) + + go func() { + err = relayer.Start() + integrationTests.Log.LogIfError(err) + require.Nil(t, err) + }() + + relayers = append(relayers, relayer) + } + + <-ctx.Done() + time.Sleep(time.Second * 5) + + assert.NotNil(t, multiversXChainMock.PerformedActionID()) + transfer := multiversXChainMock.ProposedTransfer() + require.NotNil(t, transfer) + require.Equal(t, 2, len(transfer.Transfers)) + assert.Equal(t, batchNonceOnEthereum+1, transfer.BatchId.Uint64()) + + assert.Equal(t, destination1.AddressBytes(), transfer.Transfers[0].To) + assert.Equal(t, hex.EncodeToString([]byte(ticker1)), transfer.Transfers[0].Token) + assert.Equal(t, value1, transfer.Transfers[0].Amount) + assert.Equal(t, depositor1, common.BytesToAddress(transfer.Transfers[0].From)) + assert.Equal(t, txNonceOnEthereum+1, transfer.Transfers[0].Nonce.Uint64()) + assert.Equal(t, []byte{bridgeCore.MissingDataProtocolMarker}, transfer.Transfers[0].Data) + + assert.Equal(t, destination2.AddressBytes(), transfer.Transfers[1].To) + assert.Equal(t, hex.EncodeToString([]byte(ticker2)), transfer.Transfers[1].Token) + assert.Equal(t, value2, transfer.Transfers[1].Amount) + assert.Equal(t, depositor2, common.BytesToAddress(transfer.Transfers[1].From)) + assert.Equal(t, txNonceOnEthereum+2, transfer.Transfers[1].Nonce.Uint64()) + assert.Equal(t, []byte{bridgeCore.MissingDataProtocolMarker}, transfer.Transfers[1].Data) +} + +func TestRelayersShouldExecuteTransferFromEthToMultiversXHavingTxsWithSCcalls(t *testing.T) { + if testing.Short() { + t.Skip("this is not a short test") + } + + t.Run("correct SC call", func(t *testing.T) { + testArgs := argsForSCCallsTest{ + providedScCallData: bridge.EthCallDataMock, + expectedScCallData: bridge.CallDataMock, + } + + testRelayersShouldExecuteTransferFromEthToMultiversXHavingTxsWithSCcalls(t, testArgs) + }) + t.Run("no SC call", func(t *testing.T) { + testArgs := argsForSCCallsTest{ + providedScCallData: []byte{bridgeCore.MissingDataProtocolMarker}, + expectedScCallData: []byte{bridgeCore.MissingDataProtocolMarker}, + } + + testRelayersShouldExecuteTransferFromEthToMultiversXHavingTxsWithSCcalls(t, testArgs) + }) +} + +func testRelayersShouldExecuteTransferFromEthToMultiversXHavingTxsWithSCcalls(t *testing.T, args argsForSCCallsTest) { + safeContractEthAddress := testsCommon.CreateRandomEthereumAddress() + + token1Erc20 := testsCommon.CreateRandomEthereumAddress() + ticker1 := "tck-000001" + + token2Erc20 := testsCommon.CreateRandomEthereumAddress() + ticker2 := "tck-000002" + + token3Erc20 := testsCommon.CreateRandomEthereumAddress() + ticker3 := "tck-000003" + + value1 := big.NewInt(111111111) + destination1 := testsCommon.CreateRandomMultiversXAddress() + depositor1 := testsCommon.CreateRandomEthereumAddress() + + value2 := big.NewInt(222222222) + destination2 := testsCommon.CreateRandomMultiversXAddress() + depositor2 := testsCommon.CreateRandomEthereumAddress() + + depositor3 := testsCommon.CreateRandomEthereumAddress() + + value3 := big.NewInt(333333333) + destination3Sc := testsCommon.CreateRandomMultiversXSCAddress() + + tokens := []common.Address{token1Erc20, token2Erc20, token3Erc20} + availableBalances := []*big.Int{value1, value2, value3} + + erc20ContractsHolder := createMockErc20ContractsHolder(tokens, safeContractEthAddress, availableBalances) + + batchNonceOnEthereum := uint64(345) + txNonceOnEthereum := uint64(772634) + batch := contract.Batch{ + Nonce: big.NewInt(int64(batchNonceOnEthereum) + 1), + BlockNumber: 0, + LastUpdatedBlockNumber: 0, + DepositsCount: 3, + } + + numRelayers := 3 + ethereumChainMock := mock.NewEthereumChainMock() + ethereumChainMock.AddBatch(batch) + ethereumChainMock.AddDepositToBatch(batchNonceOnEthereum+1, contract.Deposit{ + Nonce: big.NewInt(int64(txNonceOnEthereum) + 1), + TokenAddress: token1Erc20, + Amount: value1, + Depositor: depositor1, + Recipient: destination1.AddressSlice(), + Status: 0, + }) + ethereumChainMock.AddDepositToBatch(batchNonceOnEthereum+1, contract.Deposit{ + Nonce: big.NewInt(int64(txNonceOnEthereum) + 2), + TokenAddress: token2Erc20, + Amount: value2, + Depositor: depositor2, + Recipient: destination2.AddressSlice(), + Status: 0, + }) + ethereumChainMock.AddDepositToBatch(batchNonceOnEthereum+1, contract.Deposit{ + Nonce: big.NewInt(int64(txNonceOnEthereum) + 3), + TokenAddress: token3Erc20, + Amount: value3, + Depositor: depositor3, + Recipient: destination3Sc.AddressSlice(), + Status: 0, + }) + ethereumChainMock.AddBatch(batch) + ethereumChainMock.SetQuorum(numRelayers) + ethereumChainMock.SetFinalNonce(batchNonceOnEthereum + 1) + + ethereumChainMock.UpdateNativeTokens(token1Erc20, true) + ethereumChainMock.UpdateMintBurnTokens(token1Erc20, false) + ethereumChainMock.UpdateTotalBalances(token1Erc20, value1) + + ethereumChainMock.UpdateNativeTokens(token2Erc20, true) + ethereumChainMock.UpdateMintBurnTokens(token2Erc20, false) + ethereumChainMock.UpdateTotalBalances(token2Erc20, value2) + + ethereumChainMock.UpdateNativeTokens(token3Erc20, true) + ethereumChainMock.UpdateMintBurnTokens(token3Erc20, false) + ethereumChainMock.UpdateTotalBalances(token3Erc20, value3) + + ethereumChainMock.FilterLogsCalled = func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + expectedBatchNonceHash := []common.Hash{ + common.BytesToHash(big.NewInt(int64(batchNonceOnEthereum + 1)).Bytes()), + } + require.Equal(t, 2, len(q.Topics)) + assert.Equal(t, expectedBatchNonceHash, q.Topics[1]) + + scExecAbi, err := contract.ERC20SafeMetaData.GetAbi() + require.Nil(t, err) + + eventInputs := scExecAbi.Events["ERC20SCDeposit"].Inputs.NonIndexed() + packedArgs, err := eventInputs.Pack(big.NewInt(0).SetUint64(txNonceOnEthereum+3), args.providedScCallData) + require.Nil(t, err) + + scLog := types.Log{ + Data: packedArgs, + } + + return []types.Log{scLog}, nil + } multiversXChainMock := mock.NewMultiversXChainMock() - multiversXChainMock.AddTokensPair(token1Erc20, ticker1) - multiversXChainMock.AddTokensPair(token2Erc20, ticker2) + multiversXChainMock.AddTokensPair(token1Erc20, ticker1, false, true, zero, zero, zero) + multiversXChainMock.AddTokensPair(token2Erc20, ticker2, false, true, zero, zero, zero) + multiversXChainMock.AddTokensPair(token3Erc20, ticker3, false, true, zero, zero, zero) multiversXChainMock.SetLastExecutedEthBatchID(batchNonceOnEthereum) multiversXChainMock.SetLastExecutedEthTxId(txNonceOnEthereum) multiversXChainMock.GetStatusesAfterExecutionHandler = func() []byte { - return []byte{clients.Executed, clients.Rejected} + return []byte{bridgeCore.Executed, bridgeCore.Rejected, bridgeCore.Executed} } multiversXChainMock.SetQuorum(numRelayers) @@ -133,7 +368,7 @@ func TestRelayersShouldExecuteTransferFromEthToMultiversX(t *testing.T) { assert.NotNil(t, multiversXChainMock.PerformedActionID()) transfer := multiversXChainMock.ProposedTransfer() require.NotNil(t, transfer) - require.Equal(t, 2, len(transfer.Transfers)) + require.Equal(t, 3, len(transfer.Transfers)) assert.Equal(t, batchNonceOnEthereum+1, transfer.BatchId.Uint64()) assert.Equal(t, destination1.AddressBytes(), transfer.Transfers[0].To) @@ -141,12 +376,21 @@ func TestRelayersShouldExecuteTransferFromEthToMultiversX(t *testing.T) { assert.Equal(t, value1, transfer.Transfers[0].Amount) assert.Equal(t, depositor1, common.BytesToAddress(transfer.Transfers[0].From)) assert.Equal(t, txNonceOnEthereum+1, transfer.Transfers[0].Nonce.Uint64()) + assert.Equal(t, []byte{bridgeCore.MissingDataProtocolMarker}, transfer.Transfers[0].Data) assert.Equal(t, destination2.AddressBytes(), transfer.Transfers[1].To) assert.Equal(t, hex.EncodeToString([]byte(ticker2)), transfer.Transfers[1].Token) assert.Equal(t, value2, transfer.Transfers[1].Amount) assert.Equal(t, depositor2, common.BytesToAddress(transfer.Transfers[1].From)) assert.Equal(t, txNonceOnEthereum+2, transfer.Transfers[1].Nonce.Uint64()) + assert.Equal(t, []byte{bridgeCore.MissingDataProtocolMarker}, transfer.Transfers[1].Data) + + assert.Equal(t, destination3Sc.AddressBytes(), transfer.Transfers[2].To) + assert.Equal(t, hex.EncodeToString([]byte(ticker3)), transfer.Transfers[2].Token) + assert.Equal(t, value3, transfer.Transfers[2].Amount) + assert.Equal(t, depositor3, common.BytesToAddress(transfer.Transfers[2].From)) + assert.Equal(t, txNonceOnEthereum+3, transfer.Transfers[2].Nonce.Uint64()) + assert.Equal(t, args.expectedScCallData, transfer.Transfers[2].Data) } func createMockBridgeComponentsArgs( @@ -156,13 +400,13 @@ func createMockBridgeComponentsArgs( ethereumChainMock *mock.EthereumChainMock, ) factory.ArgsEthereumToMultiversXBridge { - generalConfigs := createBridgeComponentsConfig(index) + generalConfigs := CreateBridgeComponentsConfig(index, "testdata", noGasStationURL) return factory.ArgsEthereumToMultiversXBridge{ Configs: config.Configs{ GeneralConfig: generalConfigs, ApiRoutesConfig: config.ApiRoutesConfig{}, FlagsConfig: config.ContextFlagsConfig{ - RestApiInterface: core.WebServerOffString, + RestApiInterface: bridgeCore.WebServerOffString, }, }, Proxy: multiversXChainMock, @@ -176,52 +420,3 @@ func createMockBridgeComponentsArgs( MultiversXClientStatusHandler: &testsCommon.StatusHandlerStub{}, } } - -func createBridgeComponentsConfig(index int) config.Config { - stateMachineConfig := config.ConfigStateMachine{ - StepDurationInMillis: 1000, - IntervalForLeaderInSeconds: 60, - } - - return config.Config{ - Eth: config.EthereumConfig{ - Chain: chain.Ethereum, - NetworkAddress: "mock", - MultisigContractAddress: "3009d97FfeD62E57d444e552A9eDF9Ee6Bc8644c", - PrivateKeyFile: fmt.Sprintf("testdata/ethereum%d.sk", index), - IntervalToResendTxsInSeconds: 10, - GasLimitBase: 200000, - GasLimitForEach: 30000, - GasStation: config.GasStationConfig{ - Enabled: false, - }, - MaxRetriesOnQuorumReached: 1, - IntervalToWaitForTransferInSeconds: 1, - MaxBlocksDelta: 5, - }, - MultiversX: config.MultiversXConfig{ - NetworkAddress: "mock", - MultisigContractAddress: "erd1qqqqqqqqqqqqqpgqzyuaqg3dl7rqlkudrsnm5ek0j3a97qevd8sszj0glf", - PrivateKeyFile: fmt.Sprintf("testdata/multiversx%d.pem", index), - IntervalToResendTxsInSeconds: 10, - GasMap: testsCommon.CreateTestMultiversXGasMap(), - MaxRetriesOnQuorumReached: 1, - MaxRetriesOnWasTransferProposed: 3, - ProxyMaxNoncesDelta: 5, - }, - P2P: config.ConfigP2P{}, - StateMachine: map[string]config.ConfigStateMachine{ - "EthereumToMultiversX": stateMachineConfig, - "MultiversXToEthereum": stateMachineConfig, - }, - Relayer: config.ConfigRelayer{ - Marshalizer: chainConfig.MarshalizerConfig{ - Type: "json", - SizeCheckDelta: 10, - }, - RoleProvider: config.RoleProviderConfig{ - PollingIntervalInMillis: 1000, - }, - }, - } -} diff --git a/integrationTests/relayers/multiversXToEth_test.go b/integrationTests/relayers/multiversXToEth_test.go index 9664df0d..c3cf23ef 100644 --- a/integrationTests/relayers/multiversXToEth_test.go +++ b/integrationTests/relayers/multiversXToEth_test.go @@ -1,24 +1,28 @@ +//go:build !slow + package relayers import ( "context" "fmt" "math/big" + "runtime/debug" + "strings" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/multiversx/mx-bridge-eth-go/clients" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-bridge-eth-go/factory" "github.com/multiversx/mx-bridge-eth-go/integrationTests" "github.com/multiversx/mx-bridge-eth-go/integrationTests/mock" "github.com/multiversx/mx-bridge-eth-go/testsCommon" - logger "github.com/multiversx/mx-chain-logger-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -var log = logger.GetOrCreate("integrationTests/relayers") +var zero = big.NewInt(0) +var relayerEthBalance = big.NewInt(1000000000) func asyncCancelCall(cancelHandler func(), delay time.Duration) { go func() { @@ -27,7 +31,7 @@ func asyncCancelCall(cancelHandler func(), delay time.Duration) { }() } -func TestRelayersShouldExecuteTransferFromMultiversXToEth(t *testing.T) { +func TestRelayersShouldExecuteSimpleTransfersFromMultiversXToEth(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } @@ -42,13 +46,26 @@ func TestRelayersShouldExecuteTransferFromMultiversXToEth(t *testing.T) { numRelayers := 3 ethereumChainMock := mock.NewEthereumChainMock() ethereumChainMock.SetQuorum(numRelayers) - expectedStatuses := []byte{clients.Executed, clients.Rejected} - ethereumChainMock.GetStatusesAfterExecutionHandler = func() []byte { - return expectedStatuses + expectedStatuses := []byte{bridgeCore.Executed, bridgeCore.Rejected} + ethereumChainMock.GetStatusesAfterExecutionHandler = func() ([]byte, bool) { + if callIsFromBalanceValidator() { + // statuses can not be final at this point as the batch was not executed yet + return expectedStatuses, false + } + + return expectedStatuses, true + } + ethereumChainMock.BalanceAtCalled = func(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + return relayerEthBalance, nil } multiversXChainMock := mock.NewMultiversXChainMock() for i := 0; i < len(deposits); i++ { - multiversXChainMock.AddTokensPair(tokensAddresses[i], deposits[i].Ticker) + ethereumChainMock.UpdateNativeTokens(tokensAddresses[i], false) + ethereumChainMock.UpdateMintBurnTokens(tokensAddresses[i], true) + ethereumChainMock.UpdateMintBalances(tokensAddresses[i], zero) + ethereumChainMock.UpdateBurnBalances(tokensAddresses[i], zero) + + multiversXChainMock.AddTokensPair(tokensAddresses[i], deposits[i].Ticker, true, true, zero, zero, deposits[i].Amount) } pendingBatch := mock.MultiversXPendingBatch{ Nonce: big.NewInt(1), @@ -59,11 +76,7 @@ func TestRelayersShouldExecuteTransferFromMultiversXToEth(t *testing.T) { multiversXChainMock.SetQuorum(numRelayers) relayers := make([]bridgeComponents, 0, numRelayers) - defer func() { - for _, r := range relayers { - _ = r.Close() - } - }() + defer closeRelayers(relayers) messengers := integrationTests.CreateLinkedMessengers(numRelayers) @@ -98,28 +111,28 @@ func TestRelayersShouldExecuteTransferFromMultiversXToEth(t *testing.T) { // let all transactions propagate time.Sleep(time.Second * 5) - transactions := multiversXChainMock.GetAllSentTransactions(context.Background()) - assert.Equal(t, 5, len(transactions)) - assert.Nil(t, multiversXChainMock.ProposedTransfer()) - assert.NotNil(t, multiversXChainMock.PerformedActionID()) - - transfer := ethereumChainMock.GetLastProposedTransfer() - require.NotNil(t, transfer) - - require.Equal(t, numTransactions, len(transfer.Amounts)) + checkTestStatus(t, multiversXChainMock, ethereumChainMock, numTransactions, deposits, tokensAddresses) +} - for i := 0; i < len(transfer.Amounts); i++ { - assert.Equal(t, deposits[i].To, transfer.Recipients[i]) - assert.Equal(t, tokensAddresses[i], transfer.Tokens[i]) - assert.Equal(t, deposits[i].Amount, transfer.Amounts[i]) - } +func callIsFromBalanceValidator() bool { + callStack := string(debug.Stack()) + return strings.Contains(callStack, "(*balanceValidator).getTotalTransferAmountInPendingMvxBatches") } -func TestRelayersShouldExecuteTransferFromMultiversXToEthIfTransactionsAppearInBatch(t *testing.T) { +func TestRelayersShouldExecuteTransfersFromMultiversXToEthIfTransactionsAppearInBatch(t *testing.T) { if testing.Short() { t.Skip("this is not a short test") } + t.Run("simple tokens transfers", func(t *testing.T) { + testRelayersShouldExecuteTransfersFromMultiversXToEthIfTransactionsAppearInBatch(t, false) + }) + t.Run("native tokens transfers", func(t *testing.T) { + testRelayersShouldExecuteTransfersFromMultiversXToEthIfTransactionsAppearInBatch(t, true) + }) +} + +func testRelayersShouldExecuteTransfersFromMultiversXToEthIfTransactionsAppearInBatch(t *testing.T, withNativeTokens bool) { numTransactions := 2 deposits, tokensAddresses, erc20Map := createTransactions(numTransactions) @@ -130,13 +143,36 @@ func TestRelayersShouldExecuteTransferFromMultiversXToEthIfTransactionsAppearInB numRelayers := 3 ethereumChainMock := mock.NewEthereumChainMock() ethereumChainMock.SetQuorum(numRelayers) - expectedStatuses := []byte{clients.Executed, clients.Rejected} - ethereumChainMock.GetStatusesAfterExecutionHandler = func() []byte { - return expectedStatuses + expectedStatuses := []byte{bridgeCore.Executed, bridgeCore.Rejected} + ethereumChainMock.GetStatusesAfterExecutionHandler = func() ([]byte, bool) { + if callIsFromBalanceValidator() { + // statuses can not be final at this point as the batch was not executed yet + return expectedStatuses, false + } + + return expectedStatuses, true + } + ethereumChainMock.BalanceAtCalled = func(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { + return relayerEthBalance, nil } multiversXChainMock := mock.NewMultiversXChainMock() for i := 0; i < len(deposits); i++ { - multiversXChainMock.AddTokensPair(tokensAddresses[i], deposits[i].Ticker) + nativeBalanceValue := deposits[i].Amount + + if !withNativeTokens { + ethereumChainMock.UpdateNativeTokens(tokensAddresses[i], true) + ethereumChainMock.UpdateMintBurnTokens(tokensAddresses[i], false) + ethereumChainMock.UpdateTotalBalances(tokensAddresses[i], nativeBalanceValue) + + multiversXChainMock.AddTokensPair(tokensAddresses[i], deposits[i].Ticker, withNativeTokens, true, zero, nativeBalanceValue, nativeBalanceValue) + } else { + ethereumChainMock.UpdateNativeTokens(tokensAddresses[i], false) + ethereumChainMock.UpdateMintBurnTokens(tokensAddresses[i], true) + ethereumChainMock.UpdateBurnBalances(tokensAddresses[i], zero) + ethereumChainMock.UpdateMintBalances(tokensAddresses[i], zero) + + multiversXChainMock.AddTokensPair(tokensAddresses[i], deposits[i].Ticker, withNativeTokens, true, zero, zero, nativeBalanceValue) + } } pendingBatch := mock.MultiversXPendingBatch{ Nonce: big.NewInt(1), @@ -152,11 +188,7 @@ func TestRelayersShouldExecuteTransferFromMultiversXToEthIfTransactionsAppearInB } relayers := make([]bridgeComponents, 0, numRelayers) - defer func() { - for _, r := range relayers { - _ = r.Close() - } - }() + defer closeRelayers(relayers) messengers := integrationTests.CreateLinkedMessengers(numRelayers) @@ -191,21 +223,7 @@ func TestRelayersShouldExecuteTransferFromMultiversXToEthIfTransactionsAppearInB // let all transactions propagate time.Sleep(time.Second * 5) - transactions := multiversXChainMock.GetAllSentTransactions(context.Background()) - assert.Equal(t, 5, len(transactions)) - assert.Nil(t, multiversXChainMock.ProposedTransfer()) - assert.NotNil(t, multiversXChainMock.PerformedActionID()) - - transfer := ethereumChainMock.GetLastProposedTransfer() - require.NotNil(t, transfer) - - require.Equal(t, numTransactions, len(transfer.Amounts)) - - for i := 0; i < len(transfer.Amounts); i++ { - assert.Equal(t, deposits[i].To, transfer.Recipients[i]) - assert.Equal(t, tokensAddresses[i], transfer.Tokens[i]) - assert.Equal(t, deposits[i].Amount, transfer.Amounts[i]) - } + checkTestStatus(t, multiversXChainMock, ethereumChainMock, numTransactions, deposits, tokensAddresses) } func createTransactions(n int) ([]mock.MultiversXDeposit, []common.Address, map[common.Address]*big.Int) { @@ -235,12 +253,31 @@ func createTransaction(index int) (mock.MultiversXDeposit, common.Address) { From: testsCommon.CreateRandomMultiversXAddress(), To: testsCommon.CreateRandomEthereumAddress(), Ticker: fmt.Sprintf("tck-00000%d", index+1), - Amount: big.NewInt(int64(index)), + Amount: big.NewInt(int64(index*1000) + 500), // 0 as amount is not relevant }, tokenAddress } -// TODO: remove duplicated code from the integration tests: -// L154-L169, for loop is the same as in the first tests -// L108-L129 same with L25-L47 -// L137-L151 same with L49-L63 -// check are the same after ctx.Done() +func checkTestStatus( + t *testing.T, + multiversXChainMock *mock.MultiversXChainMock, + ethereumChainMock *mock.EthereumChainMock, + numTransactions int, + deposits []mock.MultiversXDeposit, + tokensAddresses []common.Address, +) { + transactions := multiversXChainMock.GetAllSentTransactions(context.Background()) + assert.Equal(t, 5, len(transactions)) + assert.Nil(t, multiversXChainMock.ProposedTransfer()) + assert.NotNil(t, multiversXChainMock.PerformedActionID()) + + transfer := ethereumChainMock.GetLastProposedTransfer() + require.NotNil(t, transfer) + + require.Equal(t, numTransactions, len(transfer.Amounts)) + + for i := 0; i < len(transfer.Amounts); i++ { + assert.Equal(t, deposits[i].To, transfer.Recipients[i]) + assert.Equal(t, tokensAddresses[i], transfer.Tokens[i]) + assert.Equal(t, deposits[i].Amount, transfer.Amounts[i]) + } +} diff --git a/integrationTests/relayers/slowTests/common.go b/integrationTests/relayers/slowTests/common.go new file mode 100644 index 00000000..0746134d --- /dev/null +++ b/integrationTests/relayers/slowTests/common.go @@ -0,0 +1,197 @@ +//go:build slow + +package slowTests + +import ( + "math/big" + + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" + "github.com/multiversx/mx-bridge-eth-go/parsers" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + logger "github.com/multiversx/mx-chain-logger-go" +) + +var ( + log = logger.GetOrCreate("integrationTests/relayers/slowTests") +) + +// GenerateTestUSDCToken will generate a test USDC token +func GenerateTestUSDCToken() framework.TestTokenParams { + // USDC is ethNative = true, ethMintBurn = false, mvxNative = false, mvxMintBurn = true + return framework.TestTokenParams{ + IssueTokenParams: framework.IssueTokenParams{ + AbstractTokenIdentifier: "USDC", + NumOfDecimalsUniversal: 6, + NumOfDecimalsChainSpecific: 6, + MvxUniversalTokenTicker: "USDC", + MvxChainSpecificTokenTicker: "ETHUSDC", + MvxUniversalTokenDisplayName: "WrappedUSDC", + MvxChainSpecificTokenDisplayName: "EthereumWrappedUSDC", + ValueToMintOnMvx: "10000000000", + IsMintBurnOnMvX: true, + IsNativeOnMvX: false, + HasChainSpecificToken: true, + EthTokenName: "EthUSDC", + EthTokenSymbol: "USDC", + ValueToMintOnEth: "10000000000", + IsMintBurnOnEth: false, + IsNativeOnEth: true, + }, + TestOperations: []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(5000), + ValueToSendFromMvX: big.NewInt(2500), + }, + { + ValueToTransferToMvx: big.NewInt(7000), + ValueToSendFromMvX: big.NewInt(300), + }, + { + ValueToTransferToMvx: big.NewInt(1000), + ValueToSendFromMvX: nil, + MvxSCCallData: createScCallData("callPayable", 50000000), + }, + }, + ESDTSafeExtraBalance: big.NewInt(100), // extra is just for the fees for the 2 transfers mvx->eth + EthTestAddrExtraBalance: big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000), // -(eth->mvx) + (mvx->eth) - fees + } +} + +// GenerateTestMEMEToken will generate a test MEME token +func GenerateTestMEMEToken() framework.TestTokenParams { + //MEME is ethNative = false, ethMintBurn = true, mvxNative = true, mvxMintBurn = false + return framework.TestTokenParams{ + IssueTokenParams: framework.IssueTokenParams{ + AbstractTokenIdentifier: "MEME", + NumOfDecimalsUniversal: 1, + NumOfDecimalsChainSpecific: 1, + MvxUniversalTokenTicker: "MEME", + MvxChainSpecificTokenTicker: "ETHMEME", + MvxUniversalTokenDisplayName: "WrappedMEME", + MvxChainSpecificTokenDisplayName: "EthereumWrappedMEME", + ValueToMintOnMvx: "10000000000", + IsMintBurnOnMvX: false, + IsNativeOnMvX: true, + HasChainSpecificToken: true, + EthTokenName: "EthMEME", + EthTokenSymbol: "MEME", + ValueToMintOnEth: "10000000000", + IsMintBurnOnEth: true, + IsNativeOnEth: false, + }, + TestOperations: []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(2400), + ValueToSendFromMvX: big.NewInt(4000), + }, + { + ValueToTransferToMvx: big.NewInt(200), + ValueToSendFromMvX: big.NewInt(6000), + }, + { + ValueToTransferToMvx: big.NewInt(1000), + ValueToSendFromMvX: big.NewInt(2000), + MvxSCCallData: createScCallData("callPayable", 50000000), + }, + }, + ESDTSafeExtraBalance: big.NewInt(4000 + 6000 + 2000), // everything is locked in the safe esdt contract + EthTestAddrExtraBalance: big.NewInt(4000 - 50 + 6000 - 50 + 2000 - 50), + } +} + +// GenerateTestEUROCToken will generate a test EUROC token +func GenerateTestEUROCToken() framework.TestTokenParams { + //EUROC is ethNative = true, ethMintBurn = true, mvxNative = false, mvxMintBurn = true + return framework.TestTokenParams{ + IssueTokenParams: framework.IssueTokenParams{ + AbstractTokenIdentifier: "EUROC", + NumOfDecimalsUniversal: 6, + NumOfDecimalsChainSpecific: 6, + MvxUniversalTokenTicker: "EUROC", + MvxChainSpecificTokenTicker: "EUROC", + MvxUniversalTokenDisplayName: "TestEUROC", + MvxChainSpecificTokenDisplayName: "TestEUROC", + ValueToMintOnMvx: "10000000000", + IsMintBurnOnMvX: true, + IsNativeOnMvX: false, + HasChainSpecificToken: false, + EthTokenName: "EthEuroC", + EthTokenSymbol: "EUROC", + ValueToMintOnEth: "10000000000", + IsMintBurnOnEth: true, + IsNativeOnEth: true, + }, + TestOperations: []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(5010), + ValueToSendFromMvX: big.NewInt(2510), + }, + { + ValueToTransferToMvx: big.NewInt(7010), + ValueToSendFromMvX: big.NewInt(310), + }, + { + ValueToTransferToMvx: big.NewInt(1010), + ValueToSendFromMvX: nil, + MvxSCCallData: createScCallData("callPayable", 50000000), + }, + }, + ESDTSafeExtraBalance: big.NewInt(100), // extra is just for the fees for the 2 transfers mvx->eth + EthTestAddrExtraBalance: big.NewInt(-5010 + 2510 - 50 - 7010 + 310 - 50 - 1010), // -(eth->mvx) + (mvx->eth) - fees + } +} + +// GenerateTestMEXToken will generate a test EUROC token +func GenerateTestMEXToken() framework.TestTokenParams { + //MEX is ethNative = false, ethMintBurn = true, mvxNative = true, mvxMintBurn = true + return framework.TestTokenParams{ + IssueTokenParams: framework.IssueTokenParams{ + AbstractTokenIdentifier: "MEX", + NumOfDecimalsUniversal: 2, + NumOfDecimalsChainSpecific: 2, + MvxUniversalTokenTicker: "MEX", + MvxChainSpecificTokenTicker: "MEX", + MvxUniversalTokenDisplayName: "TestMEX", + MvxChainSpecificTokenDisplayName: "TestMEX", + ValueToMintOnMvx: "10000000000", + IsMintBurnOnMvX: true, + IsNativeOnMvX: true, + HasChainSpecificToken: false, + EthTokenName: "EthMex", + EthTokenSymbol: "MEX", + ValueToMintOnEth: "10000000000", + IsMintBurnOnEth: true, + IsNativeOnEth: false, + }, + TestOperations: []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(2410), + ValueToSendFromMvX: big.NewInt(4010), + }, + { + ValueToTransferToMvx: big.NewInt(210), + ValueToSendFromMvX: big.NewInt(6010), + }, + { + ValueToTransferToMvx: big.NewInt(1010), + ValueToSendFromMvX: big.NewInt(2010), + MvxSCCallData: createScCallData("callPayable", 50000000), + }, + }, + ESDTSafeExtraBalance: big.NewInt(150), // just the fees should be collected in ESDT safe + EthTestAddrExtraBalance: big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50), + } +} + +func createScCallData(function string, gasLimit uint64, args ...string) []byte { + codec := testsCommon.TestMultiversXCodec{} + callData := parsers.CallData{ + Type: bridgeCore.DataPresentProtocolMarker, + Function: function, + GasLimit: gasLimit, + Arguments: args, + } + + return codec.EncodeCallDataStrict(callData) +} diff --git a/integrationTests/relayers/slowTests/edgeCases_test.go b/integrationTests/relayers/slowTests/edgeCases_test.go new file mode 100644 index 00000000..e4a6d68e --- /dev/null +++ b/integrationTests/relayers/slowTests/edgeCases_test.go @@ -0,0 +1,95 @@ +//go:build slow + +package slowTests + +import ( + "context" + "errors" + "math/big" + "testing" + + "github.com/multiversx/mx-bridge-eth-go/integrationTests/mock" + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/stretchr/testify/require" +) + +func TestRelayerShouldExecuteSimultaneousSwapsAndNotCatchErrors(t *testing.T) { + errorString := "ERROR" + mockLogObserver := mock.NewMockLogObserver(errorString, "got invalid action ID") + err := logger.AddLogObserver(mockLogObserver, &logger.PlainFormatter{}) + require.NoError(t, err) + defer func() { + require.NoError(t, logger.RemoveLogObserver(mockLogObserver)) + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stopChan := make(chan error, 1000) // ensure sufficient error buffer + + go func() { + for { + select { + case <-ctx.Done(): + return + case <-mockLogObserver.LogFoundChan(): + stopChan <- errors.New("logger should have not caught errors") + } + } + }() + + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations = []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(5000), + ValueToSendFromMvX: big.NewInt(200), + MvxSCCallData: nil, + MvxFaultySCCall: false, + MvxForceSCCall: false, + }, + } + usdcToken.ESDTSafeExtraBalance = big.NewInt(50) + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 - 5000 + 200 - 50) + + _ = testRelayersWithChainSimulatorAndTokensForSimultaneousSwaps( + t, + stopChan, + usdcToken, + ) +} + +func testRelayersWithChainSimulatorAndTokensForSimultaneousSwaps(tb testing.TB, manualStopChan chan error, tokens ...framework.TestTokenParams) *framework.TestSetup { + startsFromEthFlow := &startsFromEthereumEdgecaseFlow{ + TB: tb, + tokens: tokens, + } + + setupFunc := func(tb testing.TB, setup *framework.TestSetup) { + startsFromEthFlow.setup = setup + + setup.IssueAndConfigureTokens(tokens...) + setup.MultiversxHandler.CheckForZeroBalanceOnReceivers(setup.Ctx, tokens...) + setup.EthereumHandler.CreateBatchOnEthereum(setup.Ctx, setup.MultiversxHandler.TestCallerAddress, startsFromEthFlow.tokens...) + } + + processFunc := func(tb testing.TB, setup *framework.TestSetup) bool { + if startsFromEthFlow.process() { + setup.TestWithdrawTotalFeesOnEthereumForTokens(startsFromEthFlow.tokens...) + + return true + } + + setup.EthereumHandler.SimulatedChain.Commit() + setup.ChainSimulator.GenerateBlocks(setup.Ctx, 1) + require.LessOrEqual(tb, setup.ScCallerModuleInstance.GetNumSentTransaction(), setup.GetNumScCallsOperations()) + + return false + } + + return testRelayersWithChainSimulator(tb, + setupFunc, + processFunc, + manualStopChan, + ) +} diff --git a/integrationTests/relayers/slowTests/ethToMultiversXWithChainSimulator_test.go b/integrationTests/relayers/slowTests/ethToMultiversXWithChainSimulator_test.go new file mode 100644 index 00000000..6f4a00a1 --- /dev/null +++ b/integrationTests/relayers/slowTests/ethToMultiversXWithChainSimulator_test.go @@ -0,0 +1,484 @@ +//go:build slow + +// To run these slow tests, simply add the slow tag on the go test command. Also, provide a chain simulator instance on the 8085 port +// example: go test -tags slow + +package slowTests + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + "math/big" + "os" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/multiversx/mx-bridge-eth-go/integrationTests/mock" + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + timeout = time.Minute * 15 + projectedShardForTestKeys = byte(2) +) + +func TestRelayersShouldExecuteTransfers(t *testing.T) { + _ = testRelayersWithChainSimulatorAndTokens( + t, + make(chan error), + GenerateTestUSDCToken(), + GenerateTestMEMEToken(), + ) +} + +func TestRelayersShouldExecuteTransfersWithMintBurnTokens(t *testing.T) { + _ = testRelayersWithChainSimulatorAndTokens( + t, + make(chan error), + GenerateTestEUROCToken(), + GenerateTestMEXToken(), + ) +} + +func TestRelayersShouldExecuteTransfersWithSCCallsWithArguments(t *testing.T) { + dummyAddress := strings.Repeat("2", 32) + dummyUint64 := string([]byte{37}) + + callData := createScCallData("callPayableWithParams", 50000000, dummyUint64, dummyAddress) + + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + + testSetup := testRelayersWithChainSimulatorAndTokens( + t, + make(chan error), + usdcToken, + memeToken, + ) + + testCallPayableWithParamsWasCalled( + testSetup, + 37, + usdcToken.AbstractTokenIdentifier, + memeToken.AbstractTokenIdentifier, + ) +} + +func TestRelayersShouldExecuteTransfersWithSCCallsWithArgumentsWithMintBurnTokens(t *testing.T) { + dummyAddress := strings.Repeat("2", 32) + dummyUint64 := string([]byte{37}) + + callData := createScCallData("callPayableWithParams", 50000000, dummyUint64, dummyAddress) + + eurocToken := GenerateTestEUROCToken() + eurocToken.TestOperations[2].MvxSCCallData = callData + + mexToken := GenerateTestMEXToken() + mexToken.TestOperations[2].MvxSCCallData = callData + + testSetup := testRelayersWithChainSimulatorAndTokens( + t, + make(chan error), + eurocToken, + mexToken, + ) + + testCallPayableWithParamsWasCalled( + testSetup, + 37, + eurocToken.AbstractTokenIdentifier, + mexToken.AbstractTokenIdentifier, + ) +} + +func TestRelayerShouldExecuteTransfersAndNotCatchErrors(t *testing.T) { + errorString := "ERROR" + mockLogObserver := mock.NewMockLogObserver(errorString) + err := logger.AddLogObserver(mockLogObserver, &logger.PlainFormatter{}) + require.NoError(t, err) + defer func() { + require.NoError(t, logger.RemoveLogObserver(mockLogObserver)) + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stopChan := make(chan error, 1000) // ensure sufficient error buffer + + go func() { + for { + select { + case <-ctx.Done(): + return + case <-mockLogObserver.LogFoundChan(): + stopChan <- errors.New("logger should have not caught errors") + } + } + }() + + _ = testRelayersWithChainSimulatorAndTokens( + t, + stopChan, + GenerateTestMEMEToken(), + ) +} + +func TestRelayersShouldExecuteTransfersWithInitSupply(t *testing.T) { + usdcToken := GenerateTestUSDCToken() + usdcToken.InitialSupplyValue = "100000" + + memeToken := GenerateTestMEMEToken() + memeToken.InitialSupplyValue = "200000" + + _ = testRelayersWithChainSimulatorAndTokens( + t, + make(chan error), + usdcToken, + memeToken, + ) +} + +func testRelayersWithChainSimulatorAndTokens(tb testing.TB, manualStopChan chan error, tokens ...framework.TestTokenParams) *framework.TestSetup { + startsFromEthFlow, startsFromMvXFlow := createFlowsBasedOnToken(tb, tokens...) + + setupFunc := func(tb testing.TB, setup *framework.TestSetup) { + startsFromMvXFlow.setup = setup + startsFromEthFlow.setup = setup + + setup.IssueAndConfigureTokens(tokens...) + setup.MultiversxHandler.CheckForZeroBalanceOnReceivers(setup.Ctx, tokens...) + if len(startsFromEthFlow.tokens) > 0 { + setup.EthereumHandler.CreateBatchOnEthereum(setup.Ctx, setup.MultiversxHandler.TestCallerAddress, startsFromEthFlow.tokens...) + } + if len(startsFromMvXFlow.tokens) > 0 { + setup.CreateBatchOnMultiversX(startsFromMvXFlow.tokens...) + } + } + + processFunc := func(tb testing.TB, setup *framework.TestSetup) bool { + if startsFromEthFlow.process() && startsFromMvXFlow.process() { + setup.TestWithdrawTotalFeesOnEthereumForTokens(startsFromMvXFlow.tokens...) + setup.TestWithdrawTotalFeesOnEthereumForTokens(startsFromEthFlow.tokens...) + + return true + } + + // commit blocks in order to execute incoming txs from relayers + setup.EthereumHandler.SimulatedChain.Commit() + setup.ChainSimulator.GenerateBlocks(setup.Ctx, 1) + require.LessOrEqual(tb, setup.ScCallerModuleInstance.GetNumSentTransaction(), setup.GetNumScCallsOperations()) + + return false + } + + return testRelayersWithChainSimulator(tb, + setupFunc, + processFunc, + manualStopChan, + ) +} + +func createFlowsBasedOnToken(tb testing.TB, tokens ...framework.TestTokenParams) (*startsFromEthereumFlow, *startsFromMultiversXFlow) { + startsFromEthFlow := &startsFromEthereumFlow{ + TB: tb, + tokens: make([]framework.TestTokenParams, 0, len(tokens)), + } + + startsFromMvXFlow := &startsFromMultiversXFlow{ + TB: tb, + tokens: make([]framework.TestTokenParams, 0, len(tokens)), + } + + // split the tokens from where should the bridge start + for _, token := range tokens { + if token.IsNativeOnEth { + startsFromEthFlow.tokens = append(startsFromEthFlow.tokens, token) + continue + } + if token.IsNativeOnMvX { + startsFromMvXFlow.tokens = append(startsFromMvXFlow.tokens, token) + continue + } + require.Fail(tb, "invalid setup, found a token that is not native on any chain", "abstract identifier", token.AbstractTokenIdentifier) + } + + return startsFromEthFlow, startsFromMvXFlow +} + +func testRelayersWithChainSimulator(tb testing.TB, + setupFunc func(tb testing.TB, setup *framework.TestSetup), + processLoopFunc func(tb testing.TB, setup *framework.TestSetup) bool, + stopChan chan error, +) *framework.TestSetup { + defer func() { + r := recover() + if r != nil { + require.Fail(tb, fmt.Sprintf("should have not panicked: %v", r)) + } + }() + + testSetup := framework.NewTestSetup(tb) + log.Info(fmt.Sprintf(framework.LogStepMarker, "calling setupFunc")) + setupFunc(tb, testSetup) + + testSetup.StartRelayersAndScModule() + defer testSetup.Close() + + log.Info(fmt.Sprintf(framework.LogStepMarker, "running and continously call processLoopFunc")) + interrupt := make(chan os.Signal, 1) + for { + select { + case <-interrupt: + require.Fail(tb, "signal interrupted") + return testSetup + case <-time.After(timeout): + require.Fail(tb, "time out") + return testSetup + case err := <-stopChan: + require.Nil(tb, err) + return testSetup + default: + testDone := processLoopFunc(tb, testSetup) + if testDone { + return testSetup + } + } + } +} + +func createBadToken() framework.TestTokenParams { + return framework.TestTokenParams{ + IssueTokenParams: framework.IssueTokenParams{ + AbstractTokenIdentifier: "BAD", + NumOfDecimalsUniversal: 6, + NumOfDecimalsChainSpecific: 6, + MvxUniversalTokenTicker: "BAD", + MvxChainSpecificTokenTicker: "ETHBAD", + MvxUniversalTokenDisplayName: "WrappedBAD", + MvxChainSpecificTokenDisplayName: "EthereumWrappedBAD", + ValueToMintOnMvx: "10000000000", + EthTokenName: "ETHTOKEN", + EthTokenSymbol: "ETHT", + ValueToMintOnEth: "10000000000", + }, + TestOperations: []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(5000), + ValueToSendFromMvX: big.NewInt(2500), + }, + { + ValueToTransferToMvx: big.NewInt(7000), + ValueToSendFromMvX: big.NewInt(300), + }, + { + ValueToTransferToMvx: big.NewInt(1000), + ValueToSendFromMvX: nil, + MvxSCCallData: createScCallData("callPayable", 50000000), + }, + }, + ESDTSafeExtraBalance: big.NewInt(0), + EthTestAddrExtraBalance: big.NewInt(0), + } +} + +func TestRelayersShouldNotExecuteTransfers(t *testing.T) { + t.Run("isNativeOnEth = true, isMintBurnOnEth = false, isNativeOnMvX = true, isMintBurnOnMvX = false", func(t *testing.T) { + badToken := createBadToken() + badToken.IsNativeOnEth = true + badToken.IsMintBurnOnEth = false + badToken.IsNativeOnMvX = true + badToken.IsMintBurnOnMvX = false + badToken.HasChainSpecificToken = true + + expectedStringInLogs := "error = invalid setup isNativeOnEthereum = true, isNativeOnMultiversX = true" + testRelayersShouldNotExecuteTransfers(t, expectedStringInLogs, badToken) + }) + t.Run("isNativeOnEth = true, isMintBurnOnEth = false, isNativeOnMvX = true, isMintBurnOnMvX = true", func(t *testing.T) { + badToken := createBadToken() + badToken.IsNativeOnEth = true + badToken.IsMintBurnOnEth = false + badToken.IsNativeOnMvX = true + badToken.IsMintBurnOnMvX = true + badToken.HasChainSpecificToken = false + + expectedStringInLogs := "error = invalid setup isNativeOnEthereum = true, isNativeOnMultiversX = true" + testRelayersShouldNotExecuteTransfers(t, expectedStringInLogs, badToken) + }) + t.Run("isNativeOnEth = true, isMintBurnOnEth = true, isNativeOnMvX = true, isMintBurnOnMvX = false", func(t *testing.T) { + badToken := createBadToken() + badToken.IsNativeOnEth = true + badToken.IsMintBurnOnEth = true + badToken.IsNativeOnMvX = true + badToken.IsMintBurnOnMvX = false + badToken.HasChainSpecificToken = true + + testEthContractsShouldError(t, badToken) + }) + t.Run("isNativeOnEth = false, isMintBurnOnEth = true, isNativeOnMvX = false, isMintBurnOnMvX = true", func(t *testing.T) { + badToken := createBadToken() + badToken.IsNativeOnEth = false + badToken.IsMintBurnOnEth = true + badToken.IsNativeOnMvX = false + badToken.IsMintBurnOnMvX = true + badToken.HasChainSpecificToken = true + + testEthContractsShouldError(t, badToken) + }) +} + +func testRelayersShouldNotExecuteTransfers( + tb testing.TB, + expectedStringInLogs string, + tokens ...framework.TestTokenParams, +) { + startsFromEthFlow, startsFromMvXFlow := createFlowsBasedOnToken(tb, tokens...) + + setupFunc := func(tb testing.TB, setup *framework.TestSetup) { + startsFromMvXFlow.setup = setup + startsFromEthFlow.setup = setup + + setup.IssueAndConfigureTokens(tokens...) + setup.MultiversxHandler.CheckForZeroBalanceOnReceivers(setup.Ctx, tokens...) + if len(startsFromEthFlow.tokens) > 0 { + setup.EthereumHandler.CreateBatchOnEthereum(setup.Ctx, setup.MultiversxHandler.TestCallerAddress, startsFromEthFlow.tokens...) + } + if len(startsFromMvXFlow.tokens) > 0 { + setup.CreateBatchOnMultiversX(startsFromMvXFlow.tokens...) + } + } + + processFunc := func(tb testing.TB, setup *framework.TestSetup) bool { + if startsFromEthFlow.process() && startsFromMvXFlow.process() { + return true + } + + // commit blocks in order to execute incoming txs from relayers + setup.EthereumHandler.SimulatedChain.Commit() + setup.ChainSimulator.GenerateBlocks(setup.Ctx, 1) + + return false + } + + // start a mocked log observer that is looking for a specific relayer error + chanCnt := 0 + mockLogObserver := mock.NewMockLogObserver(expectedStringInLogs) + err := logger.AddLogObserver(mockLogObserver, &logger.PlainFormatter{}) + require.NoError(tb, err) + defer func() { + require.NoError(tb, logger.RemoveLogObserver(mockLogObserver)) + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + numOfTimesToRepeatErrorForRelayer := 10 + numOfErrorsToWait := numOfTimesToRepeatErrorForRelayer * framework.NumRelayers + + stopChan := make(chan error, 1) + + go func() { + for { + select { + case <-ctx.Done(): + return + case <-mockLogObserver.LogFoundChan(): + chanCnt++ + if chanCnt >= numOfErrorsToWait { + log.Info(fmt.Sprintf("test passed, relayers are stuck, expected string `%s` found in all relayers' logs for %d times", expectedStringInLogs, numOfErrorsToWait)) + stopChan <- nil + return + } + } + } + }() + + _ = testRelayersWithChainSimulator(tb, setupFunc, processFunc, stopChan) +} + +func testEthContractsShouldError(tb testing.TB, testToken framework.TestTokenParams) { + setupFunc := func(tb testing.TB, setup *framework.TestSetup) { + setup.IssueAndConfigureTokens(testToken) + + token := setup.GetTokenData(testToken.AbstractTokenIdentifier) + require.NotNil(tb, token) + + valueToMintOnEth, ok := big.NewInt(0).SetString(testToken.ValueToMintOnEth, 10) + require.True(tb, ok) + + receiverKeys := framework.GenerateMvxPrivatePublicKey(tb, projectedShardForTestKeys) + auth, _ := bind.NewKeyedTransactorWithChainID(setup.DepositorKeys.EthSK, setup.EthereumHandler.ChainID) + _, err := setup.EthereumHandler.SafeContract.Deposit(auth, token.EthErc20Address, valueToMintOnEth, receiverKeys.MvxAddress.AddressSlice()) + require.Error(tb, err) + } + + processFunc := func(tb testing.TB, setup *framework.TestSetup) bool { + time.Sleep(time.Second) // allow go routines to start + return true + } + + _ = testRelayersWithChainSimulator(tb, + setupFunc, + processFunc, + make(chan error), + ) +} + +func testCallPayableWithParamsWasCalled(testSetup *framework.TestSetup, value uint64, tokens ...string) { + if len(tokens) == 0 { + return + } + + universalTokens := make([]string, 0, len(tokens)) + for _, identifier := range tokens { + tkData := testSetup.TokensRegistry.GetTokenData(identifier) + universalTokens = append(universalTokens, tkData.MvxUniversalToken) + } + + vmRequest := &data.VmValueRequest{ + Address: testSetup.MultiversxHandler.TestCallerAddress.Bech32(), + FuncName: "getCalledDataParams", + } + + vmResponse, err := testSetup.ChainSimulator.Proxy().ExecuteVMQuery(context.Background(), vmRequest) + require.Nil(testSetup, err) + + returnedData := vmResponse.Data.ReturnData + require.Equal(testSetup, len(tokens), len(returnedData)) + + mapUniversalTokens := make(map[string]int) + for _, tokenIdentifier := range universalTokens { + mapUniversalTokens[tokenIdentifier] = 0 + } + + for _, buff := range returnedData { + parsedValue, parsedToken := processCalledDataParams(buff) + assert.Equal(testSetup, value, parsedValue) + mapUniversalTokens[parsedToken]++ + } + + assert.Equal(testSetup, len(tokens), len(mapUniversalTokens)) + for _, numTokens := range mapUniversalTokens { + assert.Equal(testSetup, 1, numTokens) + } +} + +func processCalledDataParams(buff []byte) (uint64, string) { + valBuff := buff[:8] + value := binary.BigEndian.Uint64(valBuff) + + buff = buff[8+32+4:] // trim the nonce, address and length of the token + token := string(buff) + + return value, token +} diff --git a/integrationTests/relayers/slowTests/framework/address.go b/integrationTests/relayers/slowTests/framework/address.go new file mode 100644 index 00000000..970df17d --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/address.go @@ -0,0 +1,67 @@ +package framework + +import ( + "encoding/hex" + "testing" + + sdkCore "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/require" +) + +// MvxAddress holds the different forms a MultiversX address might have +type MvxAddress struct { + sdkCore.AddressHandler + bytes []byte + bech32 string + hex string +} + +// NewMvxAddressFromBytes return a new instance of MvxAddress from bytes +func NewMvxAddressFromBytes(tb testing.TB, bytes []byte) *MvxAddress { + address := &MvxAddress{ + bytes: make([]byte, len(bytes)), + hex: hex.EncodeToString(bytes), + AddressHandler: data.NewAddressFromBytes(bytes), + } + + var err error + copy(address.bytes, bytes) + address.bech32, err = addressPubkeyConverter.Encode(bytes) + require.Nil(tb, err) + + return address +} + +// NewMvxAddressFromBech32 return a new instance of MvxAddress from the bech32 string +func NewMvxAddressFromBech32(tb testing.TB, bech32 string) *MvxAddress { + addressHandler, err := data.NewAddressFromBech32String(bech32) + require.Nil(tb, err) + + return &MvxAddress{ + bytes: addressHandler.AddressBytes(), + hex: hex.EncodeToString(addressHandler.AddressBytes()), + bech32: bech32, + AddressHandler: addressHandler, + } +} + +// Bytes returns the bytes format address +func (address *MvxAddress) Bytes() []byte { + return address.bytes +} + +// Bech32 returns the bech32 string format address +func (address *MvxAddress) Bech32() string { + return address.bech32 +} + +// Hex returns the hex string format address +func (address *MvxAddress) Hex() string { + return address.hex +} + +// String returns the address in bech32 format +func (address *MvxAddress) String() string { + return address.bech32 +} diff --git a/integrationTests/relayers/slowTests/framework/bridgeComponents.go b/integrationTests/relayers/slowTests/framework/bridgeComponents.go new file mode 100644 index 00000000..e13f2ae5 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/bridgeComponents.go @@ -0,0 +1,123 @@ +package framework + +import ( + "fmt" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/ethclient/simulated" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" + "github.com/multiversx/mx-bridge-eth-go/config" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/factory" + "github.com/multiversx/mx-bridge-eth-go/integrationTests" + testsRelayers "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers" + "github.com/multiversx/mx-bridge-eth-go/status" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + "github.com/multiversx/mx-chain-go/testscommon/statusHandler" + "github.com/stretchr/testify/require" +) + +const ( + relayerETHKeyPathFormat = "../testdata/ethereum%d.sk" +) + +// BridgeComponents holds and manages the relayers components +type BridgeComponents struct { + testing.TB + RelayerInstances []Relayer + gasStationInstance *gasStation +} + +// NewBridgeComponents will create the bridge components (relayers) +func NewBridgeComponents( + tb testing.TB, + workingDir string, + chainSimulator ChainSimulatorWrapper, + ethereumChain ethereum.ClientWrapper, + erc20ContractsHolder ethereum.Erc20ContractsHolder, + ethBackend *simulated.Backend, + numRelayers int, + ethSafeContractAddress string, + mvxSafeAddress *MvxAddress, + mvxMultisigAddress *MvxAddress, +) *BridgeComponents { + bridge := &BridgeComponents{ + TB: tb, + RelayerInstances: make([]Relayer, 0, numRelayers), + gasStationInstance: NewGasStation(ethBackend), + } + + messengers := integrationTests.CreateLinkedMessengers(numRelayers) + + gasStationURL := bridge.gasStationInstance.URL() + log.Info("started gas station server", "URL", gasStationURL) + + wg := sync.WaitGroup{} + wg.Add(numRelayers) + + for i := 0; i < numRelayers; i++ { + generalConfigs := testsRelayers.CreateBridgeComponentsConfig(i, workingDir, gasStationURL) + generalConfigs.Eth.PrivateKeyFile = fmt.Sprintf(relayerETHKeyPathFormat, i) + argsBridgeComponents := factory.ArgsEthereumToMultiversXBridge{ + Configs: config.Configs{ + GeneralConfig: generalConfigs, + ApiRoutesConfig: config.ApiRoutesConfig{}, + FlagsConfig: config.ContextFlagsConfig{ + RestApiInterface: bridgeCore.WebServerOffString, + }, + }, + Proxy: chainSimulator.Proxy(), + ClientWrapper: ethereumChain, + Messenger: messengers[i], + StatusStorer: testsCommon.NewStorerMock(), + TimeForBootstrap: time.Second * 5, + TimeBeforeRepeatJoin: time.Second * 30, + MetricsHolder: status.NewMetricsHolder(), + AppStatusHandler: &statusHandler.AppStatusHandlerStub{}, + MultiversXClientStatusHandler: &testsCommon.StatusHandlerStub{}, + } + argsBridgeComponents.Configs.GeneralConfig.Eth.SafeContractAddress = ethSafeContractAddress + argsBridgeComponents.Erc20ContractsHolder = erc20ContractsHolder + argsBridgeComponents.Configs.GeneralConfig.MultiversX.NetworkAddress = chainSimulator.GetNetworkAddress() + argsBridgeComponents.Configs.GeneralConfig.MultiversX.SafeContractAddress = mvxSafeAddress.Bech32() + argsBridgeComponents.Configs.GeneralConfig.MultiversX.MultisigContractAddress = mvxMultisigAddress.Bech32() + argsBridgeComponents.Configs.GeneralConfig.MultiversX.GasMap = config.MultiversXGasMapConfig{ + Sign: 8000000, + ProposeTransferBase: 11000000, + ProposeTransferForEach: 5500000, + ProposeStatusBase: 10000000, + ProposeStatusForEach: 7000000, + PerformActionBase: 40000000, + PerformActionForEach: 5500000, + ScCallPerByte: 100000, + ScCallPerformForEach: 10000000, + } + relayer, err := factory.NewEthMultiversXBridgeComponents(argsBridgeComponents) + require.Nil(bridge, err) + + go func() { + err = relayer.Start() + log.LogIfError(err) + require.Nil(bridge, err) + wg.Done() + }() + + bridge.RelayerInstances = append(bridge.RelayerInstances, relayer) + } + + // ensure all relayers are successfully started before returning the bridge components instance + wg.Wait() + + return bridge +} + +// CloseRelayers will call close on all created relayers +func (bridge *BridgeComponents) CloseRelayers() { + bridge.gasStationInstance.Close() + + for _, r := range bridge.RelayerInstances { + _ = r.Close() + } +} diff --git a/integrationTests/relayers/slowTests/framework/chainSimulatorWrapper.go b/integrationTests/relayers/slowTests/framework/chainSimulatorWrapper.go new file mode 100644 index 00000000..49bb6633 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/chainSimulatorWrapper.go @@ -0,0 +1,382 @@ +package framework + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "strings" + "testing" + "time" + + "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/core/pubkeyConverter" + apiCore "github.com/multiversx/mx-chain-core-go/data/api" + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-chain-crypto-go/signing" + "github.com/multiversx/mx-chain-crypto-go/signing/ed25519" + "github.com/multiversx/mx-chain-crypto-go/signing/ed25519/singlesig" + "github.com/multiversx/mx-chain-go/integrationTests/vm/wasm" + "github.com/multiversx/mx-chain-go/node/chainSimulator/dtos" + "github.com/multiversx/mx-sdk-go/blockchain" + sdkCore "github.com/multiversx/mx-sdk-go/core" + sdkHttp "github.com/multiversx/mx-sdk-go/core/http" + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/require" +) + +const ( + proxyURL = "http://127.0.0.1:8085" + thousandEgld = "1000000000000000000000" + maxAllowedTimeout = time.Second + setMultipleEndpoint = "simulator/set-state-overwrite" + generateBlocksEndpoint = "simulator/generate-blocks/%d" + generateBlocksUntilEpochReachedEndpoint = "simulator/generate-blocks-until-epoch-reached/%d" + generateBlocksUntilTxProcessedEndpoint = "simulator/generate-blocks-until-transaction-processed/%s" + numProbeRetries = 10 + networkConfigEndpointTemplate = "network/status/%d" +) + +var ( + signer = &singlesig.Ed25519Signer{} + keyGenerator = signing.NewKeyGenerator(ed25519.NewEd25519()) +) + +// ArgChainSimulatorWrapper is the DTO used to create a new instance of proxy that relies on a chain simulator +type ArgChainSimulatorWrapper struct { + TB testing.TB + ProxyCacherExpirationSeconds uint64 + ProxyMaxNoncesDelta int +} + +type chainSimulatorWrapper struct { + testing.TB + clientWrapper httpClientWrapper + proxyInstance multiversx.Proxy + pkConv core.PubkeyConverter +} + +// CreateChainSimulatorWrapper creates a new instance of the chain simulator wrapper +func CreateChainSimulatorWrapper(args ArgChainSimulatorWrapper) *chainSimulatorWrapper { + argsProxy := blockchain.ArgsProxy{ + ProxyURL: proxyURL, + SameScState: false, + ShouldBeSynced: false, + FinalityCheck: false, + AllowedDeltaToFinal: args.ProxyMaxNoncesDelta, + CacheExpirationTime: time.Second * time.Duration(args.ProxyCacherExpirationSeconds), + EntityType: sdkCore.Proxy, + } + proxyInstance, err := blockchain.NewProxy(argsProxy) + require.Nil(args.TB, err) + + pubKeyConverter, err := pubkeyConverter.NewBech32PubkeyConverter(32, "erd") + require.Nil(args.TB, err) + + instance := &chainSimulatorWrapper{ + TB: args.TB, + clientWrapper: sdkHttp.NewHttpClientWrapper(nil, proxyURL), + proxyInstance: proxyInstance, + pkConv: pubKeyConverter, + } + + instance.probeURLWithRetries() + + return instance +} + +func (instance *chainSimulatorWrapper) probeURLWithRetries() { + // at this point we should be able to get the network configs + + var err error + for i := 0; i < numProbeRetries; i++ { + log.Info("trying to probe the chain simulator", "url", proxyURL, "try", i) + + ctx, done := context.WithTimeout(context.Background(), maxAllowedTimeout) + _, err = instance.proxyInstance.GetNetworkConfig(ctx) + done() + + if err == nil { + log.Info("probe ok, chain simulator instance found", "url", proxyURL) + return + } + + time.Sleep(maxAllowedTimeout) + } + + require.Fail(instance, fmt.Sprintf("%s while probing the network config. Please ensure that a chain simulator is running on %s", err.Error(), proxyURL)) +} + +// Proxy returns the managed proxy instance +func (instance *chainSimulatorWrapper) Proxy() multiversx.Proxy { + return instance.proxyInstance +} + +// GetNetworkAddress returns the network address +func (instance *chainSimulatorWrapper) GetNetworkAddress() string { + return proxyURL +} + +// DeploySC will deploy the provided smart contract and return its address +func (instance *chainSimulatorWrapper) DeploySC(ctx context.Context, wasmFilePath string, ownerSK []byte, gasLimit uint64, parameters []string) (*MvxAddress, string, *data.TransactionOnNetwork) { + networkConfig, err := instance.proxyInstance.GetNetworkConfig(ctx) + require.Nil(instance.TB, err) + + ownerPK := instance.getPublicKey(ownerSK) + nonce, err := instance.getNonce(ctx, ownerPK) + require.Nil(instance.TB, err) + + scCode := wasm.GetSCCode(wasmFilePath) + params := []string{scCode, wasm.VMTypeHex, wasm.DummyCodeMetadataHex} + params = append(params, parameters...) + txData := strings.Join(params, "@") + + ftx := &transaction.FrontendTransaction{ + Nonce: nonce, + Value: "0", + Receiver: emptyAddress, + Sender: ownerPK, + GasPrice: networkConfig.MinGasPrice, + GasLimit: gasLimit, + Data: []byte(txData), + ChainID: networkConfig.ChainID, + Version: 1, + } + + hash := instance.signAndSend(ctx, ownerSK, ftx, 1) + txResult := instance.GetTransactionResult(ctx, hash) + + return NewMvxAddressFromBech32(instance.TB, txResult.Logs.Events[0].Address), hash, txResult +} + +// GetTransactionResult tries to get a transaction result. It may wait a few blocks +func (instance *chainSimulatorWrapper) GetTransactionResult(ctx context.Context, hash string) *data.TransactionOnNetwork { + instance.GenerateBlocksUntilTxProcessed(ctx, hash) + + txResult, err := instance.proxyInstance.GetTransactionInfoWithResults(ctx, hash) + require.Nil(instance, err) + + txStatus, err := instance.proxyInstance.ProcessTransactionStatus(ctx, hash) + require.Nil(instance, err) + + jsonData, err := json.MarshalIndent(txResult.Data.Transaction, "", " ") + require.Nil(instance, err) + require.Equal(instance, transaction.TxStatusSuccess, txStatus, fmt.Sprintf("tx hash: %s,\n tx: %s", hash, string(jsonData))) + + return &txResult.Data.Transaction +} + +// GenerateBlocks calls the chain simulator generate block endpoint +func (instance *chainSimulatorWrapper) GenerateBlocks(ctx context.Context, numBlocks int) { + if numBlocks <= 0 { + return + } + + _, status, err := instance.clientWrapper.PostHTTP(ctx, fmt.Sprintf(generateBlocksEndpoint, numBlocks), nil) + if err != nil || status != http.StatusOK { + log.Error("error in chainSimulatorWrapper.GenerateBlocks", "error", err, "status", status) + return + } +} + +// GenerateBlocksUntilEpochReached will generate blocks until the provided epoch is reached +func (instance *chainSimulatorWrapper) GenerateBlocksUntilEpochReached(ctx context.Context, epoch uint32) { + _, status, err := instance.clientWrapper.PostHTTP(ctx, fmt.Sprintf(generateBlocksUntilEpochReachedEndpoint, epoch), nil) + if err != nil || status != http.StatusOK { + log.Error("error in chainSimulatorWrapper.GenerateBlocksUntilEpochReached", "error", err, "status", status) + return + } +} + +// GenerateBlocksUntilTxProcessed will generate blocks until the provided tx hash is executed +func (instance *chainSimulatorWrapper) GenerateBlocksUntilTxProcessed(ctx context.Context, hexTxHash string) { + _, status, err := instance.clientWrapper.PostHTTP(ctx, fmt.Sprintf(generateBlocksUntilTxProcessedEndpoint, hexTxHash), nil) + if err != nil || status != http.StatusOK { + log.Error("error in chainSimulatorWrapper.GenerateBlocksUntilTxProcessed", "error", err, "status", status) + return + } +} + +// ScCall will make the provided sc call +func (instance *chainSimulatorWrapper) ScCall(ctx context.Context, senderSK []byte, contract *MvxAddress, value string, gasLimit uint64, function string, parameters []string) (string, *data.TransactionOnNetwork) { + return instance.SendTx(ctx, senderSK, contract, value, gasLimit, createTxData(function, parameters)) +} + +// ScCallWithoutGenerateBlocks will make the provided sc call and do not trigger the generate blocks command +func (instance *chainSimulatorWrapper) ScCallWithoutGenerateBlocks(ctx context.Context, senderSK []byte, contract *MvxAddress, value string, gasLimit uint64, function string, parameters []string) string { + return instance.SendTxWithoutGenerateBlocks(ctx, senderSK, contract, value, gasLimit, createTxData(function, parameters)) +} + +func createTxData(function string, parameters []string) []byte { + params := []string{function} + params = append(params, parameters...) + txData := strings.Join(params, "@") + + return []byte(txData) +} + +// SendTx will build and send a transaction +func (instance *chainSimulatorWrapper) SendTx(ctx context.Context, senderSK []byte, receiver *MvxAddress, value string, gasLimit uint64, dataField []byte) (string, *data.TransactionOnNetwork) { + hash := instance.SendTxWithoutGenerateBlocks(ctx, senderSK, receiver, value, gasLimit, dataField) + instance.GenerateBlocks(ctx, 1) + txResult := instance.GetTransactionResult(ctx, hash) + + return hash, txResult +} + +// SendTxWithoutGenerateBlocks will build and send a transaction and won't call the generate blocks command +func (instance *chainSimulatorWrapper) SendTxWithoutGenerateBlocks(ctx context.Context, senderSK []byte, receiver *MvxAddress, value string, gasLimit uint64, dataField []byte) string { + networkConfig, err := instance.proxyInstance.GetNetworkConfig(ctx) + require.Nil(instance, err) + + senderPK := instance.getPublicKey(senderSK) + nonce, err := instance.getNonce(ctx, senderPK) + require.Nil(instance, err) + + ftx := &transaction.FrontendTransaction{ + Nonce: nonce, + Value: value, + Receiver: receiver.Bech32(), + Sender: senderPK, + GasPrice: networkConfig.MinGasPrice, + GasLimit: gasLimit, + Data: dataField, + ChainID: networkConfig.ChainID, + Version: 1, + } + + hash := instance.signAndSend(ctx, senderSK, ftx, 0) + + return hash +} + +// FundWallets sends funds to the provided addresses +func (instance *chainSimulatorWrapper) FundWallets(ctx context.Context, wallets []string) { + addressesState := make([]*dtos.AddressState, 0, len(wallets)) + for _, wallet := range wallets { + addressesState = append(addressesState, &dtos.AddressState{ + Address: wallet, + Nonce: new(uint64), + Balance: thousandEgld, + }) + } + + buff, err := json.Marshal(addressesState) + if err != nil { + log.Error("error in chainSimulatorWrapper.FundWallets", "error", err) + return + } + + _, status, err := instance.clientWrapper.PostHTTP(ctx, setMultipleEndpoint, buff) + if err != nil || status != http.StatusOK { + log.Error("error in chainSimulatorWrapper.FundWallets - PostHTTP", "error", err, "status", status) + return + } +} + +// GetESDTBalance returns the balance of the esdt token for the provided address +func (instance *chainSimulatorWrapper) GetESDTBalance(ctx context.Context, address *MvxAddress, token string) string { + tokenData, err := instance.proxyInstance.GetESDTTokenData(ctx, address, token, apiCore.AccountQueryOptions{ + OnFinalBlock: true, + }) + require.Nil(instance, err) + + return tokenData.Balance +} + +// GetBlockchainTimeStamp will return the latest block timestamp by querying the endpoint route: /network/status/4294967295 +func (instance *chainSimulatorWrapper) GetBlockchainTimeStamp(ctx context.Context) uint64 { + resultBytes, status, err := instance.clientWrapper.GetHTTP(ctx, fmt.Sprintf(networkConfigEndpointTemplate, core.MetachainShardId)) + if err != nil || status != http.StatusOK { + require.Fail(instance, fmt.Sprintf("error %v, status code %d in chainSimulatorWrapper.GetBlockchainTimeStamp", err, status)) + } + + resultStruct := struct { + Data struct { + Status struct { + ErdBlockTimestamp uint64 `json:"erd_block_timestamp"` + } `json:"status"` + } `json:"data"` + }{} + + err = json.Unmarshal(resultBytes, &resultStruct) + require.Nil(instance, err) + + return resultStruct.Data.Status.ErdBlockTimestamp +} + +func (instance *chainSimulatorWrapper) getNonce(ctx context.Context, bech32Address string) (uint64, error) { + address, err := data.NewAddressFromBech32String(bech32Address) + if err != nil { + return 0, err + } + + account, err := instance.proxyInstance.GetAccount(ctx, address) + if err != nil { + return 0, err + } + + return account.Nonce, nil +} + +func (instance *chainSimulatorWrapper) signAndSend(ctx context.Context, senderSK []byte, ftx *transaction.FrontendTransaction, numBlocksToGenerate int) string { + sig, err := computeTransactionSignature(senderSK, ftx) + require.Nil(instance, err) + + ftx.Signature = hex.EncodeToString(sig) + + hash, err := instance.proxyInstance.SendTransaction(ctx, ftx) + require.Nil(instance, err) + + instance.GenerateBlocks(ctx, numBlocksToGenerate) + + return hash +} + +func (instance *chainSimulatorWrapper) getPublicKey(privateKeyBytes []byte) string { + sk, err := keyGenerator.PrivateKeyFromByteArray(privateKeyBytes) + require.Nil(instance, err) + + pk := sk.GeneratePublic() + pkBytes, err := pk.ToByteArray() + require.Nil(instance, err) + + pkString, err := addressPubkeyConverter.Encode(pkBytes) + require.Nil(instance, err) + + return pkString +} + +func computeTransactionSignature(senderSk []byte, tx *transaction.FrontendTransaction) ([]byte, error) { + privateKey, err := keyGenerator.PrivateKeyFromByteArray(senderSk) + if err != nil { + return nil, err + } + + dataToSign, err := json.Marshal(tx) + if err != nil { + return nil, err + } + + return signer.Sign(privateKey, dataToSign) +} + +// ExecuteVMQuery will try to execute a VM query and return the results +func (instance *chainSimulatorWrapper) ExecuteVMQuery( + ctx context.Context, + scAddress *MvxAddress, + function string, + hexParams []string, +) [][]byte { + vmRequest := &data.VmValueRequest{ + Address: scAddress.Bech32(), + FuncName: function, + Args: hexParams, + } + response, err := instance.Proxy().ExecuteVMQuery(ctx, vmRequest) + require.Nil(instance, err) + + return response.Data.ReturnData +} diff --git a/integrationTests/relayers/slowTests/framework/common.go b/integrationTests/relayers/slowTests/framework/common.go new file mode 100644 index 00000000..89902031 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/common.go @@ -0,0 +1,14 @@ +package framework + +import ( + "math/big" + + "github.com/multiversx/mx-chain-core-go/core/pubkeyConverter" + logger "github.com/multiversx/mx-chain-logger-go" +) + +var ( + log = logger.GetOrCreate("integrationtests/slowtests") + addressPubkeyConverter, _ = pubkeyConverter.NewBech32PubkeyConverter(32, "erd") + zeroValueBigInt = big.NewInt(0) +) diff --git a/integrationTests/relayers/slowTests/framework/ethereumHandler.go b/integrationTests/relayers/slowTests/framework/ethereumHandler.go new file mode 100644 index 00000000..81d7b68a --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/ethereumHandler.go @@ -0,0 +1,494 @@ +package framework + +import ( + "bytes" + "context" + "crypto/ecdsa" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient/simulated" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/wrappers" + "github.com/multiversx/mx-bridge-eth-go/core/converters" + "github.com/multiversx/mx-bridge-eth-go/testsCommon" + "github.com/multiversx/mx-sdk-go/core" + "github.com/stretchr/testify/require" +) + +const ( + ethSimulatedGasLimit = 9000000 + ethStatusSuccess = uint64(1) + minterRoleString = "MINTER_ROLE" + ethMinAmountAllowedToTransfer = 25 + ethMaxAmountAllowedToTransfer = 500000 + + erc20SafeABI = "testdata/contracts/eth/ERC20Safe.abi.json" + erc20SafeBytecode = "testdata/contracts/eth/ERC20Safe.hex" + bridgeABI = "testdata/contracts/eth/Bridge.abi.json" + bridgeBytecode = "testdata/contracts/eth/Bridge.hex" + genericERC20ABI = "testdata/contracts/eth/GenericERC20.abi.json" + genericERC20Bytecode = "testdata/contracts/eth/GenericERC20.hex" + mintBurnERC20ABI = "testdata/contracts/eth/MintBurnERC20.abi.json" + mintBurnERC20Bytecode = "testdata/contracts/eth/MintBurnERC20.hex" + proxyABI = "testdata/contracts/eth/Proxy.abi.json" + proxyBytecode = "testdata/contracts/eth/Proxy.hex" + + proxyInitializeFunction = "initialize" +) + +// EthereumHandler will handle all the operations on the Ethereum side +type EthereumHandler struct { + testing.TB + *KeysStore + TokensRegistry TokensRegistry + Quorum string + MvxTestCallerAddress core.AddressHandler + SimulatedChain *simulated.Backend + SimulatedChainWrapper EthereumBlockchainClient + ChainID *big.Int + SafeAddress common.Address + SafeContract *contract.ERC20Safe + BridgeAddress common.Address + BridgeContract *contract.Bridge + Erc20ContractsHolder ethereum.Erc20ContractsHolder + EthChainWrapper ethereum.ClientWrapper +} + +// NewEthereumHandler will create the handler that will adapt all test operations on Ethereum +func NewEthereumHandler( + tb testing.TB, + ctx context.Context, + keysStore *KeysStore, + tokensRegistry TokensRegistry, + quorum string, +) *EthereumHandler { + handler := &EthereumHandler{ + TB: tb, + KeysStore: keysStore, + TokensRegistry: tokensRegistry, + Quorum: quorum, + } + + walletsToFundOnEthereum := handler.WalletsToFundOnEthereum() + addr := make(map[common.Address]types.Account, len(walletsToFundOnEthereum)) + for _, address := range walletsToFundOnEthereum { + addr[address] = types.Account{Balance: new(big.Int).Lsh(big.NewInt(1), 100)} + } + alloc := types.GenesisAlloc(addr) + handler.SimulatedChain = simulated.NewBackend(alloc, + simulated.WithBlockGasLimit(ethSimulatedGasLimit), + ) + + handler.SimulatedChainWrapper = handler.SimulatedChain.Client() + handler.ChainID, _ = handler.SimulatedChainWrapper.ChainID(ctx) + + var err error + handler.Erc20ContractsHolder, err = ethereum.NewErc20SafeContractsHolder(ethereum.ArgsErc20SafeContractsHolder{ + EthClient: handler.SimulatedChain.Client(), + EthClientStatusHandler: &testsCommon.StatusHandlerStub{}, + }) + require.NoError(tb, err) + + return handler +} + +// DeployContracts will deploy all required contracts on Ethereum side +func (handler *EthereumHandler) DeployContracts(ctx context.Context) { + // deploy safe + handler.SafeAddress = handler.DeployUpgradeableContract(ctx, erc20SafeABI, erc20SafeBytecode) + ethSafeContract, err := contract.NewERC20Safe(handler.SafeAddress, handler.SimulatedChain.Client()) + require.NoError(handler, err) + handler.SafeContract = ethSafeContract + + // deploy bridge + ethRelayersAddresses := make([]common.Address, 0, len(handler.RelayersKeys)) + for _, relayerKeys := range handler.RelayersKeys { + ethRelayersAddresses = append(ethRelayersAddresses, relayerKeys.EthAddress) + } + quorumInt, _ := big.NewInt(0).SetString(handler.Quorum, 10) + handler.BridgeAddress = handler.DeployUpgradeableContract(ctx, bridgeABI, bridgeBytecode, ethRelayersAddresses, quorumInt, handler.SafeAddress) + handler.BridgeContract, err = contract.NewBridge(handler.BridgeAddress, handler.SimulatedChain.Client()) + require.NoError(handler, err) + + // set bridge on safe + auth, _ := bind.NewKeyedTransactorWithChainID(handler.OwnerKeys.EthSK, handler.ChainID) + tx, err := ethSafeContract.SetBridge(auth, handler.BridgeAddress) + + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + + handler.EthChainWrapper, err = wrappers.NewEthereumChainWrapper(wrappers.ArgsEthereumChainWrapper{ + StatusHandler: &testsCommon.StatusHandlerStub{}, + MultiSigContract: handler.BridgeContract, + SafeContract: handler.SafeContract, + BlockchainClient: handler.SimulatedChainWrapper, + }) + require.NoError(handler, err) + + handler.UnPauseContractsAfterTokenChanges(ctx) +} + +// DeployContract can deploy an Ethereum contract +func (handler *EthereumHandler) DeployContract( + ctx context.Context, + abiFile string, + bytecodeFile string, + params ...interface{}, +) common.Address { + abiBytes, err := os.ReadFile(abiFile) + require.NoError(handler, err) + parsed, err := abi.JSON(bytes.NewReader(abiBytes)) + require.NoError(handler, err) + + contractBytes, err := os.ReadFile(bytecodeFile) + require.NoError(handler, err) + + contractAuth, _ := bind.NewKeyedTransactorWithChainID(handler.OwnerKeys.EthSK, handler.ChainID) + contractAddress, tx, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(converters.TrimWhiteSpaceCharacters(string(contractBytes))), handler.SimulatedChain.Client(), params...) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + + handler.checkEthTxResult(ctx, tx.Hash()) + + log.Info("deployed eth contract", "from file", bytecodeFile, "address", contractAddress.Hex()) + + return contractAddress +} + +// DeployUpgradeableContract can deploy an upgradeable Ethereum contract +func (handler *EthereumHandler) DeployUpgradeableContract( + ctx context.Context, + abiFile string, + bytecodeFile string, + params ...interface{}, +) common.Address { + abiBytes, err := os.ReadFile(abiFile) + require.NoError(handler, err) + parsed, err := abi.JSON(bytes.NewReader(abiBytes)) + require.NoError(handler, err) + + contractBytes, err := os.ReadFile(bytecodeFile) + require.NoError(handler, err) + + contractAuth, _ := bind.NewKeyedTransactorWithChainID(handler.OwnerKeys.EthSK, handler.ChainID) + contractAddress, tx, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(converters.TrimWhiteSpaceCharacters(string(contractBytes))), handler.SimulatedChain.Client()) // no parameters on the logic contract constructor + require.NoError(handler, err) + handler.SimulatedChain.Commit() + + handler.checkEthTxResult(ctx, tx.Hash()) + + log.Info("deployed eth logic contract", "from file", bytecodeFile, "address", contractAddress.Hex()) + + packedParams, err := parsed.Pack(proxyInitializeFunction, params...) + require.NoError(handler, err) + proxyParams := []interface{}{ + contractAddress, + handler.OwnerKeys.EthAddress, // make the owner of the logic contract the admin for the proxy + packedParams, + } + proxyAddress := handler.DeployContract(ctx, proxyABI, proxyBytecode, proxyParams...) + + log.Info("deployed proxy contract", "address", proxyAddress.Hex()) + + return proxyAddress // return the proxy to test that it behaves just the same as the logic contract +} + +func (handler *EthereumHandler) checkEthTxResult(ctx context.Context, hash common.Hash) { + receipt, err := handler.SimulatedChain.Client().TransactionReceipt(ctx, hash) + require.NoError(handler, err) + require.Equal(handler, ethStatusSuccess, receipt.Status) +} + +// GetBalance returns the receiver's balance +func (handler *EthereumHandler) GetBalance(receiver common.Address, abstractTokenIdentifier string) *big.Int { + token := handler.TokensRegistry.GetTokenData(abstractTokenIdentifier) + require.NotNil(handler, token) + require.NotNil(handler, token.EthErc20Address) + + balance, err := token.EthErc20Contract.BalanceOf(nil, receiver) + require.NoError(handler, err) + + return balance +} + +// UnPauseContractsAfterTokenChanges can unpause contracts after token changes +func (handler *EthereumHandler) UnPauseContractsAfterTokenChanges(ctx context.Context) { + auth, _ := bind.NewKeyedTransactorWithChainID(handler.OwnerKeys.EthSK, handler.ChainID) + + // unpause bridge contract + tx, err := handler.BridgeContract.Unpause(auth) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + + // unpause safe contract + tx, err = handler.SafeContract.Unpause(auth) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) +} + +// PauseContractsForTokenChanges can pause contracts for token changes +func (handler *EthereumHandler) PauseContractsForTokenChanges(ctx context.Context) { + auth, _ := bind.NewKeyedTransactorWithChainID(handler.OwnerKeys.EthSK, handler.ChainID) + + // pause bridge contract + tx, err := handler.BridgeContract.Pause(auth) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + + // pause safe contract + tx, err = handler.SafeContract.Pause(auth) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) +} + +// IssueAndWhitelistToken will issue and whitelist the token on Ethereum +func (handler *EthereumHandler) IssueAndWhitelistToken(ctx context.Context, params IssueTokenParams) { + erc20Address, erc20ContractInstance := handler.deployTestERC20Contract(ctx, params) + + handler.TokensRegistry.RegisterEthAddressAndContract(params.AbstractTokenIdentifier, erc20Address, erc20ContractInstance) + + // whitelist eth token + auth, _ := bind.NewKeyedTransactorWithChainID(handler.OwnerKeys.EthSK, handler.ChainID) + tx, err := handler.SafeContract.WhitelistToken( + auth, + erc20Address, + big.NewInt(ethMinAmountAllowedToTransfer), + big.NewInt(ethMaxAmountAllowedToTransfer), + params.IsMintBurnOnEth, + params.IsNativeOnEth, + zeroValueBigInt, + zeroValueBigInt, + zeroValueBigInt, + ) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + + if len(params.InitialSupplyValue) > 0 { + if params.IsMintBurnOnEth { + mintAmount, ok := big.NewInt(0).SetString(params.InitialSupplyValue, 10) + require.True(handler, ok) + + tx, err = handler.SafeContract.InitSupplyMintBurn(auth, erc20Address, mintAmount, zeroValueBigInt) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + } else { + // reset the tokens value for the safe contract, so it will "know" about the balance that it has in the ERC20 contract + tx, err = handler.SafeContract.ResetTotalBalance(auth, erc20Address) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + } + } +} + +func (handler *EthereumHandler) deployTestERC20Contract(ctx context.Context, params IssueTokenParams) (common.Address, ERC20Contract) { + if params.IsMintBurnOnEth { + ethMintBurnAddress := handler.DeployUpgradeableContract( + ctx, + mintBurnERC20ABI, + mintBurnERC20Bytecode, + params.EthTokenName, + params.EthTokenSymbol, + params.NumOfDecimalsChainSpecific, + ) + + ethMintBurnContract, err := contract.NewMintBurnERC20(ethMintBurnAddress, handler.SimulatedChain.Client()) + require.NoError(handler, err) + + ownerAuth, _ := bind.NewKeyedTransactorWithChainID(handler.OwnerKeys.EthSK, handler.ChainID) + minterRoleBytes := [32]byte(crypto.Keccak256([]byte(minterRoleString))) + + // grant mint role to the depositor address for the initial mint + txGrantRole, err := ethMintBurnContract.GrantRole(ownerAuth, minterRoleBytes, handler.DepositorKeys.EthAddress) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, txGrantRole.Hash()) + + // grant mint role to the safe contract + txGrantRole, err = ethMintBurnContract.GrantRole(ownerAuth, minterRoleBytes, handler.SafeAddress) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, txGrantRole.Hash()) + + // mint generic token on the behalf of the depositor + auth, _ := bind.NewKeyedTransactorWithChainID(handler.DepositorKeys.EthSK, handler.ChainID) + + mintAmount, ok := big.NewInt(0).SetString(params.ValueToMintOnEth, 10) + require.True(handler, ok) + tx, err := ethMintBurnContract.Mint(auth, handler.DepositorKeys.EthAddress, mintAmount) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + + balance, err := ethMintBurnContract.BalanceOf(nil, handler.DepositorKeys.EthAddress) + require.NoError(handler, err) + require.Equal(handler, mintAmount.String(), balance.String()) + + if params.IsNativeOnEth { + tx, err = ethMintBurnContract.Mint(auth, handler.TestKeys.EthAddress, mintAmount) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + } + + return ethMintBurnAddress, ethMintBurnContract + } + + // deploy generic eth token + ethGenericTokenAddress := handler.DeployContract( + ctx, + genericERC20ABI, + genericERC20Bytecode, + params.EthTokenName, + params.EthTokenSymbol, + params.NumOfDecimalsChainSpecific, + ) + + ethGenericTokenContract, err := contract.NewGenericERC20(ethGenericTokenAddress, handler.SimulatedChain.Client()) + require.NoError(handler, err) + + // mint the address that will create the transfers + handler.mintTokens(ctx, ethGenericTokenContract, params.ValueToMintOnEth, handler.TestKeys.EthAddress) + if len(params.InitialSupplyValue) > 0 { + handler.mintTokens(ctx, ethGenericTokenContract, params.InitialSupplyValue, handler.SafeAddress) + } + + return ethGenericTokenAddress, ethGenericTokenContract +} + +func (handler *EthereumHandler) mintTokens( + ctx context.Context, + ethGenericTokenContract *contract.GenericERC20, + value string, + recipientAddress common.Address, +) { + auth, _ := bind.NewKeyedTransactorWithChainID(handler.DepositorKeys.EthSK, handler.ChainID) + + mintAmount, ok := big.NewInt(0).SetString(value, 10) + require.True(handler, ok) + + tx, err := ethGenericTokenContract.Mint(auth, recipientAddress, mintAmount) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + + balance, err := ethGenericTokenContract.BalanceOf(nil, recipientAddress) + require.NoError(handler, err) + require.Equal(handler, mintAmount.String(), balance.String()) +} + +// CreateBatchOnEthereum will create a batch on Ethereum using the provided tokens parameters list +func (handler *EthereumHandler) CreateBatchOnEthereum( + ctx context.Context, + mvxTestCallerAddress core.AddressHandler, + tokensParams ...TestTokenParams, +) { + for _, params := range tokensParams { + handler.createDepositsOnEthereumForToken(ctx, params, handler.TestKeys.EthSK, mvxTestCallerAddress) + } + + // wait until batch is settled + batchSettleLimit, _ := handler.SafeContract.BatchSettleLimit(nil) + for i := uint8(0); i < batchSettleLimit+1; i++ { + handler.SimulatedChain.Commit() + } +} + +func (handler *EthereumHandler) createDepositsOnEthereumForToken( + ctx context.Context, + params TestTokenParams, + from *ecdsa.PrivateKey, + mvxTestCallerAddress core.AddressHandler, +) { + // add allowance for the sender + auth, _ := bind.NewKeyedTransactorWithChainID(from, handler.ChainID) + + token := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + require.NotNil(handler, token) + require.NotNil(handler, token.EthErc20Contract) + + allowanceValue := big.NewInt(0) + for _, operation := range params.TestOperations { + if operation.ValueToTransferToMvx == nil { + continue + } + + allowanceValue.Add(allowanceValue, operation.ValueToTransferToMvx) + } + + if allowanceValue.Cmp(zeroValueBigInt) > 0 { + tx, err := token.EthErc20Contract.Approve(auth, handler.SafeAddress, allowanceValue) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + } + + var err error + for _, operation := range params.TestOperations { + if operation.ValueToTransferToMvx == nil { + continue + } + + var tx *types.Transaction + if len(operation.MvxSCCallData) > 0 || operation.MvxForceSCCall { + tx, err = handler.SafeContract.DepositWithSCExecution( + auth, + token.EthErc20Address, + operation.ValueToTransferToMvx, + mvxTestCallerAddress.AddressSlice(), + operation.MvxSCCallData, + ) + } else { + tx, err = handler.SafeContract.Deposit(auth, token.EthErc20Address, operation.ValueToTransferToMvx, handler.TestKeys.MvxAddress.AddressSlice()) + } + + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) + } +} + +// SendFromEthereumToMultiversX will create the deposit transactions on the Ethereum side +func (handler *EthereumHandler) SendFromEthereumToMultiversX( + ctx context.Context, + mvxTestCallerAddress core.AddressHandler, + tokensParams ...TestTokenParams, +) { + for _, params := range tokensParams { + handler.createDepositsOnEthereumForToken(ctx, params, handler.TestKeys.EthSK, mvxTestCallerAddress) + } +} + +// Mint will mint the provided token on Ethereum with the provided value on the behalf of the Depositor address +func (handler *EthereumHandler) Mint(ctx context.Context, params TestTokenParams, valueToMint *big.Int) { + token := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + require.NotNil(handler, token) + require.NotNil(handler, token.EthErc20Contract) + + // mint erc20 token into eth safe + auth, _ := bind.NewKeyedTransactorWithChainID(handler.DepositorKeys.EthSK, handler.ChainID) + tx, err := token.EthErc20Contract.Mint(auth, handler.SafeAddress, valueToMint) + require.NoError(handler, err) + handler.SimulatedChain.Commit() + handler.checkEthTxResult(ctx, tx.Hash()) +} + +// Close will close the resources allocated +func (handler *EthereumHandler) Close() error { + return handler.SimulatedChain.Close() +} diff --git a/integrationTests/relayers/slowTests/framework/gasStation.go b/integrationTests/relayers/slowTests/framework/gasStation.go new file mode 100644 index 00000000..2594a093 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/gasStation.go @@ -0,0 +1,82 @@ +package framework + +import ( + "context" + "encoding/json" + "fmt" + "net" + "net/http" + + "github.com/ethereum/go-ethereum/ethclient/simulated" +) + +type gasStationResponse struct { + Status string `json:"status"` + Message string `json:"message"` + Result gasStationResponseResult `json:"result"` +} + +type gasStationResponseResult struct { + LastBlock string `json:"LastBlock"` + SafeGasPrice string `json:"SafeGasPrice"` + ProposeGasPrice string `json:"ProposeGasPrice"` + FastGasPrice string `json:"FastGasPrice"` + SuggestBaseFee string `json:"suggestBaseFee"` + GasUsedRatio string `json:"gasUsedRatio"` +} + +type gasStation struct { + ethBackend *simulated.Backend + listner net.Listener +} + +// NewGasStation will create a test gas station instance that will run a test http server that can respond to gas station +// HTTP requests +func NewGasStation(ethBackend *simulated.Backend) *gasStation { + gasStationInstance := &gasStation{ + ethBackend: ethBackend, + } + + gasStationInstance.listner, _ = net.Listen("tcp", "127.0.0.1:0") + go func() { + _ = http.Serve(gasStationInstance.listner, http.HandlerFunc(gasStationInstance.handler)) + }() + + return gasStationInstance +} + +func (station *gasStation) handler(w http.ResponseWriter, _ *http.Request) { + value, err := station.ethBackend.Client().SuggestGasPrice(context.Background()) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + response := &gasStationResponse{ + Result: gasStationResponseResult{ + LastBlock: "", + SafeGasPrice: fmt.Sprintf("%d", value.Uint64()), + ProposeGasPrice: fmt.Sprintf("%d", value.Uint64()), + FastGasPrice: fmt.Sprintf("%d", value.Uint64()), + SuggestBaseFee: fmt.Sprintf("%d", value.Uint64()), + }, + } + + buff, err := json.Marshal(response) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + _, _ = w.Write(buff) +} + +// URL returns the URL for the test gas station +func (station *gasStation) URL() string { + return "http://" + station.listner.Addr().String() +} + +// Close will close the gas station server +func (station *gasStation) Close() { + _ = station.listner.Close() +} diff --git a/integrationTests/relayers/slowTests/framework/interface.go b/integrationTests/relayers/slowTests/framework/interface.go new file mode 100644 index 00000000..7b5e9db6 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/interface.go @@ -0,0 +1,81 @@ +package framework + +import ( + "context" + "math/big" + + goEthereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/multiversx/mx-bridge-eth-go/clients/multiversx" + sdkCore "github.com/multiversx/mx-sdk-go/core" + "github.com/multiversx/mx-sdk-go/data" +) + +type httpClientWrapper interface { + GetHTTP(ctx context.Context, endpoint string) ([]byte, int, error) + PostHTTP(ctx context.Context, endpoint string, data []byte) ([]byte, int, error) + IsInterfaceNil() bool +} + +// Relayer defines the behavior a bridge relayer must implement +type Relayer interface { + MultiversXRelayerAddress() sdkCore.AddressHandler + EthereumRelayerAddress() common.Address + Start() error + Close() error +} + +// ChainSimulatorWrapper defines the wrapper over the chain simulator +type ChainSimulatorWrapper interface { + Proxy() multiversx.Proxy + GetNetworkAddress() string + DeploySC(ctx context.Context, path string, ownerSK []byte, gasLimit uint64, extraParams []string) (*MvxAddress, string, *data.TransactionOnNetwork) + ScCall(ctx context.Context, senderSK []byte, contract *MvxAddress, value string, gasLimit uint64, function string, parameters []string) (string, *data.TransactionOnNetwork) + ScCallWithoutGenerateBlocks(ctx context.Context, senderSK []byte, contract *MvxAddress, value string, gasLimit uint64, function string, parameters []string) string + SendTx(ctx context.Context, senderSK []byte, receiver *MvxAddress, value string, gasLimit uint64, dataField []byte) (string, *data.TransactionOnNetwork) + SendTxWithoutGenerateBlocks(ctx context.Context, senderSK []byte, receiver *MvxAddress, value string, gasLimit uint64, dataField []byte) string + FundWallets(ctx context.Context, wallets []string) + GenerateBlocksUntilEpochReached(ctx context.Context, epoch uint32) + GenerateBlocks(ctx context.Context, numBlocks int) + GetESDTBalance(ctx context.Context, address *MvxAddress, token string) string + GetBlockchainTimeStamp(ctx context.Context) uint64 + GetTransactionResult(ctx context.Context, hash string) *data.TransactionOnNetwork + ExecuteVMQuery(ctx context.Context, scAddress *MvxAddress, function string, hexParams []string) [][]byte +} + +// EthereumBlockchainClient defines the operations supported by the Ethereum client +type EthereumBlockchainClient interface { + BlockNumber(ctx context.Context) (uint64, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) + ChainID(ctx context.Context) (*big.Int, error) + BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + FilterLogs(ctx context.Context, q goEthereum.FilterQuery) ([]types.Log, error) +} + +// ERC20Contract defines the operations of an ERC20 contract +type ERC20Contract interface { + BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) + Mint(opts *bind.TransactOpts, recipientAddress common.Address, amount *big.Int) (*types.Transaction, error) + Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) +} + +// TokensRegistry defines the registry used for the tokens in tests +type TokensRegistry interface { + AddToken(params IssueTokenParams) + RegisterEthAddressAndContract( + abstractTokenIdentifier string, + ethErc20Address common.Address, + ethErc20Contract ERC20Contract, + ) + GetTokenData(abstractTokenIdentifier string) *TokenData + RegisterUniversalToken(abstractTokenIdentifier string, mvxUniversalToken string) + RegisterChainSpecificToken(abstractTokenIdentifier string, mvxChainSpecificToken string) +} + +// SCCallerModule defines the operation for the module able to execute smart contract calls +type SCCallerModule interface { + GetNumSentTransaction() uint32 + Close() error +} diff --git a/integrationTests/relayers/slowTests/framework/keys.go b/integrationTests/relayers/slowTests/framework/keys.go new file mode 100644 index 00000000..e05b1474 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/keys.go @@ -0,0 +1,217 @@ +package framework + +import ( + "bytes" + "crypto/ecdsa" + "crypto/rand" + "encoding/hex" + "encoding/pem" + "fmt" + "os" + "path" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + mxCrypto "github.com/multiversx/mx-chain-crypto-go" + "github.com/stretchr/testify/require" +) + +// constants for the keys store +const ( + relayerPemPathFormat = "multiversx%d.pem" + SCCallerFilename = "scCaller.pem" + projectedShardForBridgeSetup = byte(0) + projectedShardForDepositor = byte(1) + projectedShardForTestKeys = byte(2) +) + +// KeysHolder holds a 2 pk-sk pairs for both chains +type KeysHolder struct { + MvxAddress *MvxAddress + MvxSk []byte + EthSK *ecdsa.PrivateKey + EthAddress common.Address +} + +// KeysStore will hold all the keys used in the test +type KeysStore struct { + testing.TB + RelayersKeys []KeysHolder + OraclesKeys []KeysHolder + SCExecutorKeys KeysHolder + OwnerKeys KeysHolder + DepositorKeys KeysHolder + TestKeys KeysHolder + workingDir string +} + +const ( + ethOwnerSK = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" + ethDepositorSK = "9bb971db41e3815a669a71c3f1bcb24e0b81f21e04bf11faa7a34b9b40e7cfb1" + ethTestSk = "dafea2c94bfe5d25f1a508808c2bc2c2e6c6f18b6b010fc841d8eb80755ba27a" +) + +// NewKeysStore will create a KeysStore instance and generate all keys +func NewKeysStore( + tb testing.TB, + workingDir string, + numRelayers int, + numOracles int, +) *KeysStore { + keysStore := &KeysStore{ + TB: tb, + RelayersKeys: make([]KeysHolder, 0, numRelayers), + SCExecutorKeys: KeysHolder{}, + workingDir: workingDir, + } + + keysStore.generateRelayersKeys(numRelayers) + keysStore.OraclesKeys = keysStore.generateKeys(numOracles, "generated oracle", projectedShardForBridgeSetup) + keysStore.SCExecutorKeys = keysStore.generateKey("", projectedShardForBridgeSetup) + keysStore.OwnerKeys = keysStore.generateKey(ethOwnerSK, projectedShardForBridgeSetup) + log.Info("generated owner", + "MvX address", keysStore.OwnerKeys.MvxAddress.Bech32(), + "Eth address", keysStore.OwnerKeys.EthAddress.String()) + keysStore.DepositorKeys = keysStore.generateKey(ethDepositorSK, projectedShardForDepositor) + keysStore.TestKeys = keysStore.generateKey(ethTestSk, projectedShardForTestKeys) + + filename := path.Join(keysStore.workingDir, SCCallerFilename) + SaveMvxKey(keysStore, filename, keysStore.SCExecutorKeys) + + return keysStore +} + +func (keyStore *KeysStore) generateRelayersKeys(numKeys int) { + for i := 0; i < numKeys; i++ { + relayerETHSKBytes, err := os.ReadFile(fmt.Sprintf(relayerETHKeyPathFormat, i)) + require.Nil(keyStore, err) + + relayerKeys := keyStore.generateKey(string(relayerETHSKBytes), projectedShardForBridgeSetup) + log.Info("generated relayer", "index", i, + "MvX address", relayerKeys.MvxAddress.Bech32(), + "Eth address", relayerKeys.EthAddress.String()) + + keyStore.RelayersKeys = append(keyStore.RelayersKeys, relayerKeys) + + filename := path.Join(keyStore.workingDir, fmt.Sprintf(relayerPemPathFormat, i)) + + SaveMvxKey(keyStore, filename, relayerKeys) + } +} + +func (keyStore *KeysStore) generateKeys(numKeys int, message string, projectedShard byte) []KeysHolder { + keys := make([]KeysHolder, 0, numKeys) + + for i := 0; i < numKeys; i++ { + ethPrivateKeyBytes := make([]byte, 32) + _, _ = rand.Read(ethPrivateKeyBytes) + + key := keyStore.generateKey(hex.EncodeToString(ethPrivateKeyBytes), projectedShard) + log.Info(message, "index", i, + "MvX address", key.MvxAddress.Bech32(), + "Eth address", key.EthAddress.String()) + + keys = append(keys, key) + } + + return keys +} + +func (keyStore *KeysStore) generateKey(ethSkHex string, projectedShard byte) KeysHolder { + var err error + + keys := GenerateMvxPrivatePublicKey(keyStore, projectedShard) + if len(ethSkHex) == 0 { + // eth keys not required + return keys + } + + keys.EthSK, err = crypto.HexToECDSA(ethSkHex) + require.Nil(keyStore, err) + + keys.EthAddress = crypto.PubkeyToAddress(keys.EthSK.PublicKey) + + return keys +} + +func (keyStore *KeysStore) getAllKeys() []KeysHolder { + allKeys := make([]KeysHolder, 0, len(keyStore.RelayersKeys)+10) + allKeys = append(allKeys, keyStore.RelayersKeys...) + allKeys = append(allKeys, keyStore.OraclesKeys...) + allKeys = append(allKeys, keyStore.SCExecutorKeys, keyStore.OwnerKeys, keyStore.DepositorKeys, keyStore.TestKeys) + + return allKeys +} + +// WalletsToFundOnEthereum will return the wallets to fund on Ethereum +func (keyStore *KeysStore) WalletsToFundOnEthereum() []common.Address { + allKeys := keyStore.getAllKeys() + walletsToFund := make([]common.Address, 0, len(allKeys)) + + for _, key := range allKeys { + if len(key.MvxSk) == 0 { + continue + } + + walletsToFund = append(walletsToFund, key.EthAddress) + } + + return walletsToFund +} + +// WalletsToFundOnMultiversX will return the wallets to fund on MultiversX +func (keyStore *KeysStore) WalletsToFundOnMultiversX() []string { + allKeys := keyStore.getAllKeys() + walletsToFund := make([]string, 0, len(allKeys)) + + for _, key := range allKeys { + walletsToFund = append(walletsToFund, key.MvxAddress.Bech32()) + } + + return walletsToFund +} + +// GenerateMvxPrivatePublicKey will generate a new keys holder instance that will hold only the MultiversX generated keys +func GenerateMvxPrivatePublicKey(tb testing.TB, projectedShard byte) KeysHolder { + sk, pkBytes := generateSkPkInShard(tb, projectedShard) + + skBytes, err := sk.ToByteArray() + require.Nil(tb, err) + + return KeysHolder{ + MvxSk: skBytes, + MvxAddress: NewMvxAddressFromBytes(tb, pkBytes), + } +} + +func generateSkPkInShard(tb testing.TB, projectedShard byte) (mxCrypto.PrivateKey, []byte) { + var sk mxCrypto.PrivateKey + var pk mxCrypto.PublicKey + + for { + sk, pk = keyGenerator.GeneratePair() + + pkBytes, err := pk.ToByteArray() + require.Nil(tb, err) + + if pkBytes[len(pkBytes)-1] == projectedShard { + return sk, pkBytes + } + } +} + +// SaveMvxKey will save the MultiversX key +func SaveMvxKey(tb testing.TB, filename string, key KeysHolder) { + blk := pem.Block{ + Type: "PRIVATE KEY for " + key.MvxAddress.Bech32(), + Bytes: []byte(hex.EncodeToString(key.MvxSk)), + } + + buff := bytes.NewBuffer(make([]byte, 0)) + err := pem.Encode(buff, &blk) + require.Nil(tb, err) + + err = os.WriteFile(filename, buff.Bytes(), os.ModePerm) + require.Nil(tb, err) +} diff --git a/integrationTests/relayers/slowTests/framework/multiversxHandler.go b/integrationTests/relayers/slowTests/framework/multiversxHandler.go new file mode 100644 index 00000000..66d1b617 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/multiversxHandler.go @@ -0,0 +1,1068 @@ +package framework + +import ( + "context" + "encoding/hex" + "fmt" + "math/big" + "strings" + "testing" + + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/require" +) + +const ( + minRelayerStake = "10000000000000000000" // 10 EGLD + esdtIssueCost = "50000000000000000" // 0.05 EGLD + emptyAddress = "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu" + esdtSystemSCAddress = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u" + slashAmount = "00" + zeroStringValue = "0" + canAddSpecialRoles = "canAddSpecialRoles" + trueStr = "true" + esdtRoleLocalMint = "ESDTRoleLocalMint" + esdtRoleLocalBurn = "ESDTRoleLocalBurn" + hexTrue = "01" + hexFalse = "00" + gwei = "GWEI" + maxBridgedAmountForToken = "500000" + deployGasLimit = 150000000 // 150 million + setCallsGasLimit = 80000000 // 80 million + issueTokenGasLimit = 70000000 // 70 million + createDepositGasLimit = 20000000 // 20 million + generalSCCallGasLimit = 50000000 // 50 million + gasLimitPerDataByte = 1500 + + aggregatorContractPath = "testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm" + wrapperContractPath = "testdata/contracts/mvx/bridged-tokens-wrapper.wasm" + multiTransferContractPath = "testdata/contracts/mvx/multi-transfer-esdt.wasm" + safeContractPath = "testdata/contracts/mvx/esdt-safe.wasm" + multisigContractPath = "testdata/contracts/mvx/multisig.wasm" + bridgeProxyContractPath = "testdata/contracts/mvx/bridge-proxy.wasm" + testCallerContractPath = "testdata/contracts/mvx/test-caller.wasm" + + setBridgeProxyContractAddressFunction = "setBridgeProxyContractAddress" + setWrappingContractAddressFunction = "setWrappingContractAddress" + changeOwnerAddressFunction = "ChangeOwnerAddress" + setEsdtSafeOnMultiTransferFunction = "setEsdtSafeOnMultiTransfer" + setEsdtSafeOnWrapperFunction = "setEsdtSafeContractAddress" + setEsdtSafeAddressFunction = "setEsdtSafeAddress" + stakeFunction = "stake" + unpauseFunction = "unpause" + unpauseEsdtSafeFunction = "unpauseEsdtSafe" + unpauseProxyFunction = "unpauseProxy" + pauseEsdtSafeFunction = "pauseEsdtSafe" + pauseFunction = "pause" + issueFunction = "issue" + setSpecialRoleFunction = "setSpecialRole" + esdtTransferFunction = "ESDTTransfer" + setPairDecimalsFunction = "setPairDecimals" + addWrappedTokenFunction = "addWrappedToken" + depositLiquidityFunction = "depositLiquidity" + whitelistTokenFunction = "whitelistToken" + addMappingFunction = "addMapping" + esdtSafeAddTokenToWhitelistFunction = "esdtSafeAddTokenToWhitelist" + esdtSafeSetMaxBridgedAmountForTokenFunction = "esdtSafeSetMaxBridgedAmountForToken" + multiTransferEsdtSetMaxBridgedAmountForTokenFunction = "multiTransferEsdtSetMaxBridgedAmountForToken" + submitBatchFunction = "submitBatch" + unwrapTokenCreateTransactionFunction = "unwrapTokenCreateTransaction" + createTransactionFunction = "createTransaction" + setBridgedTokensWrapperAddressFunction = "setBridgedTokensWrapperAddress" + setMultiTransferAddressFunction = "setMultiTransferAddress" + withdrawRefundFeesForEthereumFunction = "withdrawRefundFeesForEthereum" + getRefundFeesForEthereumFunction = "getRefundFeesForEthereum" + withdrawTransactionFeesFunction = "withdrawTransactionFees" + getTransactionFeesFunction = "getTransactionFees" + initSupplyMintBurnEsdtSafe = "initSupplyMintBurnEsdtSafe" + initSupplyEsdtSafe = "initSupplyEsdtSafe" +) + +var ( + feeInt = big.NewInt(50) +) + +// MultiversxHandler will handle all the operations on the MultiversX side +type MultiversxHandler struct { + testing.TB + *KeysStore + Quorum string + TokensRegistry TokensRegistry + ChainSimulator ChainSimulatorWrapper + + AggregatorAddress *MvxAddress + WrapperAddress *MvxAddress + SafeAddress *MvxAddress + MultisigAddress *MvxAddress + MultiTransferAddress *MvxAddress + ScProxyAddress *MvxAddress + TestCallerAddress *MvxAddress + ESDTSystemContractAddress *MvxAddress +} + +// NewMultiversxHandler will create the handler that will adapt all test operations on MultiversX +func NewMultiversxHandler( + tb testing.TB, + ctx context.Context, + keysStore *KeysStore, + tokensRegistry TokensRegistry, + chainSimulator ChainSimulatorWrapper, + quorum string, +) *MultiversxHandler { + handler := &MultiversxHandler{ + TB: tb, + KeysStore: keysStore, + TokensRegistry: tokensRegistry, + ChainSimulator: chainSimulator, + Quorum: quorum, + } + + handler.ESDTSystemContractAddress = NewMvxAddressFromBech32(handler, esdtSystemSCAddress) + + handler.ChainSimulator.GenerateBlocksUntilEpochReached(ctx, 1) + + handler.ChainSimulator.FundWallets(ctx, handler.WalletsToFundOnMultiversX()) + handler.ChainSimulator.GenerateBlocks(ctx, 1) + + return handler +} + +// DeployAndSetContracts will deploy all required contracts on MultiversX side and do the proper wiring +func (handler *MultiversxHandler) DeployAndSetContracts(ctx context.Context) { + handler.deployContracts(ctx) + + handler.wireMultiTransfer(ctx) + handler.wireSCProxy(ctx) + handler.wireSafe(ctx) + + handler.changeOwners(ctx) + handler.finishSettings(ctx) +} + +func (handler *MultiversxHandler) deployContracts(ctx context.Context) { + // deploy aggregator + stakeValue, _ := big.NewInt(0).SetString(minRelayerStake, 10) + aggregatorDeployParams := []string{ + hex.EncodeToString([]byte("EGLD")), + hex.EncodeToString(stakeValue.Bytes()), + "01", + "02", + "03", + } + + for _, oracleKey := range handler.OraclesKeys { + aggregatorDeployParams = append(aggregatorDeployParams, oracleKey.MvxAddress.Hex()) + } + + hash := "" + handler.AggregatorAddress, hash, _ = handler.ChainSimulator.DeploySC( + ctx, + aggregatorContractPath, + handler.OwnerKeys.MvxSk, + deployGasLimit, + aggregatorDeployParams, + ) + require.NotEqual(handler, emptyAddress, handler.AggregatorAddress) + log.Info("Deploy: aggregator contract", "address", handler.AggregatorAddress, "transaction hash", hash, "num oracles", len(handler.OraclesKeys)) + + // deploy wrapper + handler.WrapperAddress, hash, _ = handler.ChainSimulator.DeploySC( + ctx, + wrapperContractPath, + handler.OwnerKeys.MvxSk, + deployGasLimit, + []string{}, + ) + require.NotEqual(handler, emptyAddress, handler.WrapperAddress) + log.Info("Deploy: wrapper contract", "address", handler.WrapperAddress, "transaction hash", hash) + + // deploy multi-transfer + handler.MultiTransferAddress, hash, _ = handler.ChainSimulator.DeploySC( + ctx, + multiTransferContractPath, + handler.OwnerKeys.MvxSk, + deployGasLimit, + []string{}, + ) + require.NotEqual(handler, emptyAddress, handler.MultiTransferAddress) + log.Info("Deploy: multi-transfer contract", "address", handler.MultiTransferAddress, "transaction hash", hash) + + // deploy safe + handler.SafeAddress, hash, _ = handler.ChainSimulator.DeploySC( + ctx, + safeContractPath, + handler.OwnerKeys.MvxSk, + deployGasLimit, + []string{ + handler.AggregatorAddress.Hex(), + handler.MultiTransferAddress.Hex(), + "01", + }, + ) + require.NotEqual(handler, emptyAddress, handler.SafeAddress) + log.Info("Deploy: safe contract", "address", handler.SafeAddress, "transaction hash", hash) + + // deploy bridge proxy + handler.ScProxyAddress, hash, _ = handler.ChainSimulator.DeploySC( + ctx, + bridgeProxyContractPath, + handler.OwnerKeys.MvxSk, + deployGasLimit, + []string{ + handler.MultiTransferAddress.Hex(), + }, + ) + require.NotEqual(handler, emptyAddress, handler.ScProxyAddress) + log.Info("Deploy: SC proxy contract", "address", handler.ScProxyAddress, "transaction hash", hash) + + // deploy multisig + minRelayerStakeInt, _ := big.NewInt(0).SetString(minRelayerStake, 10) + minRelayerStakeHex := hex.EncodeToString(minRelayerStakeInt.Bytes()) + params := []string{ + handler.SafeAddress.Hex(), + handler.MultiTransferAddress.Hex(), + handler.ScProxyAddress.Hex(), + minRelayerStakeHex, + slashAmount, + handler.Quorum} + for _, relayerKeys := range handler.RelayersKeys { + params = append(params, relayerKeys.MvxAddress.Hex()) + } + handler.MultisigAddress, hash, _ = handler.ChainSimulator.DeploySC( + ctx, + multisigContractPath, + handler.OwnerKeys.MvxSk, + deployGasLimit, + params, + ) + require.NotEqual(handler, emptyAddress, handler.MultisigAddress) + log.Info("Deploy: multisig contract", "address", handler.MultisigAddress, "transaction hash", hash) + + // deploy test-caller + handler.TestCallerAddress, hash, _ = handler.ChainSimulator.DeploySC( + ctx, + testCallerContractPath, + handler.OwnerKeys.MvxSk, + deployGasLimit, + []string{}, + ) + require.NotEqual(handler, emptyAddress, handler.TestCallerAddress) + log.Info("Deploy: test-caller contract", "address", handler.TestCallerAddress, "transaction hash", hash) +} + +func (handler *MultiversxHandler) wireMultiTransfer(ctx context.Context) { + // setBridgeProxyContractAddress + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultiTransferAddress, + zeroStringValue, + setCallsGasLimit, + setBridgeProxyContractAddressFunction, + []string{ + handler.ScProxyAddress.Hex(), + }, + ) + log.Info("Set in multi-transfer contract the SC proxy contract", "transaction hash", hash, "status", txResult.Status) + + // setWrappingContractAddress + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultiTransferAddress, + zeroStringValue, + setCallsGasLimit, + setWrappingContractAddressFunction, + []string{ + handler.WrapperAddress.Hex(), + }, + ) + log.Info("Set in multi-transfer contract the wrapper contract", "transaction hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) wireSCProxy(ctx context.Context) { + // setBridgedTokensWrapper in SC bridge proxy + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ScProxyAddress, + zeroStringValue, + setCallsGasLimit, + setBridgedTokensWrapperAddressFunction, + []string{ + handler.WrapperAddress.Hex(), + }, + ) + log.Info("Set in SC proxy contract the wrapper contract", "transaction hash", hash, "status", txResult.Status) + + // setMultiTransferAddress in SC bridge proxy + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ScProxyAddress, + zeroStringValue, + setCallsGasLimit, + setMultiTransferAddressFunction, + []string{ + handler.MultiTransferAddress.Hex(), + }, + ) + log.Info("Set in SC proxy contract the multi-transfer contract", "transaction hash", hash, "status", txResult.Status) + + // setEsdtSafeAddress on bridge proxy + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ScProxyAddress, + zeroStringValue, + setCallsGasLimit, + setEsdtSafeAddressFunction, + []string{ + handler.SafeAddress.Hex(), + }, + ) + log.Info("Set in SC proxy contract the safe contract", "transaction hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) wireSafe(ctx context.Context) { + // setBridgedTokensWrapperAddress + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.SafeAddress, + zeroStringValue, + setCallsGasLimit, + setBridgedTokensWrapperAddressFunction, + []string{ + handler.WrapperAddress.Hex(), + }, + ) + log.Info("Set in safe contract the wrapper contract", "transaction hash", hash, "status", txResult.Status) + + //setBridgeProxyContractAddress + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.SafeAddress, + zeroStringValue, + setCallsGasLimit, + setBridgeProxyContractAddressFunction, + []string{ + handler.ScProxyAddress.Hex(), + }, + ) + log.Info("Set in safe contract the SC proxy contract", "transaction hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) changeOwners(ctx context.Context) { + // ChangeOwnerAddress for safe + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.SafeAddress, + zeroStringValue, + setCallsGasLimit, + changeOwnerAddressFunction, + []string{ + handler.MultisigAddress.Hex(), + }, + ) + log.Info("ChangeOwnerAddress for safe contract", "transaction hash", hash, "status", txResult.Status) + + // ChangeOwnerAddress for multi-transfer + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultiTransferAddress, + zeroStringValue, + setCallsGasLimit, + changeOwnerAddressFunction, + []string{ + handler.MultisigAddress.Hex(), + }, + ) + log.Info("ChangeOwnerAddress for multi-transfer contract", "transaction hash", hash, "status", txResult.Status) + + // ChangeOwnerAddress for bridge proxy + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ScProxyAddress, + zeroStringValue, + setCallsGasLimit, + changeOwnerAddressFunction, + []string{ + handler.MultisigAddress.Hex(), + }, + ) + log.Info("ChangeOwnerAddress for SC proxy contract", "transaction hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) finishSettings(ctx context.Context) { + // unpause sc proxy + hash, txResult := handler.callContractNoParams(ctx, handler.MultisigAddress, unpauseProxyFunction) + log.Info("Un-paused SC proxy contract", "transaction hash", hash, "status", txResult.Status) + + // setEsdtSafeOnMultiTransfer + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + setCallsGasLimit, + setEsdtSafeOnMultiTransferFunction, + []string{}, + ) + log.Info("Set in multisig contract the safe contract (automatically)", "transaction hash", hash, "status", txResult.Status) + + // stake relayers on multisig + handler.stakeAddressesOnContract(ctx, handler.MultisigAddress, handler.RelayersKeys) + + // stake relayers on price aggregator + handler.stakeAddressesOnContract(ctx, handler.AggregatorAddress, handler.OraclesKeys) + + // unpause multisig + hash, txResult = handler.callContractNoParams(ctx, handler.MultisigAddress, unpauseFunction) + log.Info("Un-paused multisig contract", "transaction hash", hash, "status", txResult.Status) + + handler.UnPauseContractsAfterTokenChanges(ctx) +} + +// CheckForZeroBalanceOnReceivers will check that the balances for all provided tokens are 0 for the test address and the test SC call address +func (handler *MultiversxHandler) CheckForZeroBalanceOnReceivers(ctx context.Context, tokens ...TestTokenParams) { + for _, params := range tokens { + handler.CheckForZeroBalanceOnReceiversForToken(ctx, params) + } +} + +// CheckForZeroBalanceOnReceiversForToken will check that the balance for the test address and the test SC call address is 0 +func (handler *MultiversxHandler) CheckForZeroBalanceOnReceiversForToken(ctx context.Context, token TestTokenParams) { + balance := handler.GetESDTUniversalTokenBalance(ctx, handler.TestKeys.MvxAddress, token.AbstractTokenIdentifier) + require.Equal(handler, big.NewInt(0).String(), balance.String()) + + balance = handler.GetESDTUniversalTokenBalance(ctx, handler.TestCallerAddress, token.AbstractTokenIdentifier) + require.Equal(handler, big.NewInt(0).String(), balance.String()) +} + +// GetESDTUniversalTokenBalance will return the universal ESDT token's balance +func (handler *MultiversxHandler) GetESDTUniversalTokenBalance( + ctx context.Context, + address *MvxAddress, + abstractTokenIdentifier string, +) *big.Int { + token := handler.TokensRegistry.GetTokenData(abstractTokenIdentifier) + require.NotNil(handler, token) + + balanceString := handler.ChainSimulator.GetESDTBalance(ctx, address, token.MvxUniversalToken) + + balance, ok := big.NewInt(0).SetString(balanceString, 10) + require.True(handler, ok) + + return balance +} + +// GetESDTChainSpecificTokenBalance will return the chain specific ESDT token's balance +func (handler *MultiversxHandler) GetESDTChainSpecificTokenBalance( + ctx context.Context, + address *MvxAddress, + abstractTokenIdentifier string, +) *big.Int { + token := handler.TokensRegistry.GetTokenData(abstractTokenIdentifier) + require.NotNil(handler, token) + + balanceString := handler.ChainSimulator.GetESDTBalance(ctx, address, token.MvxChainSpecificToken) + + balance, ok := big.NewInt(0).SetString(balanceString, 10) + require.True(handler, ok) + + return balance +} + +func (handler *MultiversxHandler) callContractNoParams(ctx context.Context, contract *MvxAddress, endpoint string) (string, *data.TransactionOnNetwork) { + return handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + contract, + zeroStringValue, + setCallsGasLimit, + endpoint, + []string{}, + ) +} + +// UnPauseContractsAfterTokenChanges can unpause contracts after token changes +func (handler *MultiversxHandler) UnPauseContractsAfterTokenChanges(ctx context.Context) { + // unpause safe + hash, txResult := handler.callContractNoParams(ctx, handler.MultisigAddress, unpauseEsdtSafeFunction) + log.Info("unpaused safe executed", "hash", hash, "status", txResult.Status) + + // unpause wrapper + hash, txResult = handler.callContractNoParams(ctx, handler.WrapperAddress, unpauseFunction) + log.Info("unpaused wrapper executed", "hash", hash, "status", txResult.Status) + + // unpause aggregator + hash, txResult = handler.callContractNoParams(ctx, handler.AggregatorAddress, unpauseFunction) + log.Info("unpaused aggregator executed", "hash", hash, "status", txResult.Status) +} + +// PauseContractsForTokenChanges can pause contracts for token changes +func (handler *MultiversxHandler) PauseContractsForTokenChanges(ctx context.Context) { + // pause safe + hash, txResult := handler.callContractNoParams(ctx, handler.MultisigAddress, pauseEsdtSafeFunction) + log.Info("paused safe executed", "hash", hash, "status", txResult.Status) + + // pause aggregator + hash, txResult = handler.callContractNoParams(ctx, handler.AggregatorAddress, pauseFunction) + log.Info("paused aggregator executed", "hash", hash, "status", txResult.Status) + + // pause wrapper + hash, txResult = handler.callContractNoParams(ctx, handler.WrapperAddress, pauseFunction) + log.Info("paused wrapper executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) stakeAddressesOnContract(ctx context.Context, contract *MvxAddress, allKeys []KeysHolder) { + for _, keys := range allKeys { + hash, txResult := handler.ChainSimulator.SendTx( + ctx, + keys.MvxSk, + contract, + minRelayerStake, + setCallsGasLimit, + []byte(stakeFunction), + ) + log.Info(fmt.Sprintf("Address %s staked on contract %s with transaction hash %s, status %s", keys.MvxAddress, contract, hash, txResult.Status)) + } +} + +// IssueAndWhitelistToken will issue and whitelist the token on MultiversX +func (handler *MultiversxHandler) IssueAndWhitelistToken(ctx context.Context, params IssueTokenParams) { + if params.HasChainSpecificToken { + handler.issueAndWhitelistTokensWithChainSpecific(ctx, params) + } else { + handler.issueAndWhitelistTokens(ctx, params) + } +} + +func (handler *MultiversxHandler) issueAndWhitelistTokensWithChainSpecific(ctx context.Context, params IssueTokenParams) { + handler.issueUniversalToken(ctx, params) + handler.issueChainSpecificToken(ctx, params) + handler.setLocalRolesForUniversalTokenOnWrapper(ctx, params) + handler.transferChainSpecificTokenToSCs(ctx, params) + handler.addUniversalTokenToWrapper(ctx, params) + handler.whitelistTokenOnWrapper(ctx, params) + handler.setRolesForSpecificTokenOnSafe(ctx, params) + handler.addMappingInMultisig(ctx, params) + handler.whitelistTokenOnMultisig(ctx, params) + handler.setInitialSupply(ctx, params) + handler.setPairDecimalsOnAggregator(ctx, params) + handler.setMaxBridgeAmountOnSafe(ctx, params) + handler.setMaxBridgeAmountOnMultitransfer(ctx, params) +} + +func (handler *MultiversxHandler) issueAndWhitelistTokens(ctx context.Context, params IssueTokenParams) { + handler.issueUniversalToken(ctx, params) + + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + handler.TokensRegistry.RegisterChainSpecificToken(params.AbstractTokenIdentifier, tkData.MvxUniversalToken) + + handler.setRolesForSpecificTokenOnSafe(ctx, params) + handler.addMappingInMultisig(ctx, params) + handler.whitelistTokenOnMultisig(ctx, params) + handler.setInitialSupply(ctx, params) + handler.setPairDecimalsOnAggregator(ctx, params) + handler.setMaxBridgeAmountOnSafe(ctx, params) + handler.setMaxBridgeAmountOnMultitransfer(ctx, params) +} + +func (handler *MultiversxHandler) issueUniversalToken(ctx context.Context, params IssueTokenParams) { + token := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + require.NotNil(handler, token) + + valueToMintInt, ok := big.NewInt(0).SetString(params.ValueToMintOnMvx, 10) + require.True(handler, ok) + + // issue universal token + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ESDTSystemContractAddress, + esdtIssueCost, + issueTokenGasLimit, + issueFunction, + []string{ + hex.EncodeToString([]byte(params.MvxUniversalTokenDisplayName)), + hex.EncodeToString([]byte(params.MvxUniversalTokenTicker)), + hex.EncodeToString(valueToMintInt.Bytes()), + fmt.Sprintf("%02x", params.NumOfDecimalsUniversal), + hex.EncodeToString([]byte(canAddSpecialRoles)), + hex.EncodeToString([]byte(trueStr))}) + mvxUniversalToken := handler.getTokenNameFromResult(*txResult) + require.Greater(handler, len(mvxUniversalToken), 0) + handler.TokensRegistry.RegisterUniversalToken(params.AbstractTokenIdentifier, mvxUniversalToken) + log.Info("issue universal token tx executed", "hash", hash, "status", txResult.Status, "token", mvxUniversalToken, "owner", handler.OwnerKeys.MvxAddress) +} + +func (handler *MultiversxHandler) issueChainSpecificToken(ctx context.Context, params IssueTokenParams) { + valueToMintInt, ok := big.NewInt(0).SetString(params.ValueToMintOnMvx, 10) + require.True(handler, ok) + + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ESDTSystemContractAddress, + esdtIssueCost, + issueTokenGasLimit, + issueFunction, + []string{ + hex.EncodeToString([]byte(params.MvxChainSpecificTokenDisplayName)), + hex.EncodeToString([]byte(params.MvxChainSpecificTokenTicker)), + hex.EncodeToString(valueToMintInt.Bytes()), + fmt.Sprintf("%02x", params.NumOfDecimalsChainSpecific), + hex.EncodeToString([]byte(canAddSpecialRoles)), + hex.EncodeToString([]byte(trueStr))}) + mvxChainSpecificToken := handler.getTokenNameFromResult(*txResult) + require.Greater(handler, len(mvxChainSpecificToken), 0) + handler.TokensRegistry.RegisterChainSpecificToken(params.AbstractTokenIdentifier, mvxChainSpecificToken) + log.Info("issue chain specific token tx executed", "hash", hash, "status", txResult.Status, "token", mvxChainSpecificToken, "owner", handler.OwnerKeys.MvxAddress) +} + +func (handler *MultiversxHandler) setLocalRolesForUniversalTokenOnWrapper(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // set local roles bridged tokens wrapper + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ESDTSystemContractAddress, + zeroStringValue, + setCallsGasLimit, + setSpecialRoleFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxUniversalToken)), + handler.WrapperAddress.Hex(), + hex.EncodeToString([]byte(esdtRoleLocalMint)), + hex.EncodeToString([]byte(esdtRoleLocalBurn))}) + log.Info("set local roles bridged tokens wrapper tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) transferChainSpecificTokenToSCs(ctx context.Context, params IssueTokenParams) { + valueToMintInt, ok := big.NewInt(0).SetString(params.ValueToMintOnMvx, 10) + require.True(handler, ok) + + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // transfer to wrapper sc + initialMintValue := valueToMintInt.Div(valueToMintInt, big.NewInt(3)) + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.WrapperAddress, + zeroStringValue, + setCallsGasLimit, + esdtTransferFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString(initialMintValue.Bytes()), + hex.EncodeToString([]byte(depositLiquidityFunction))}) + log.Info("transfer to wrapper sc tx executed", "hash", hash, "status", txResult.Status) + + // transfer to safe sc + hash, txResult = handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.SafeAddress, + zeroStringValue, + setCallsGasLimit, + esdtTransferFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString(initialMintValue.Bytes())}) + log.Info("transfer to safe sc tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) addUniversalTokenToWrapper(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // add wrapped token + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.WrapperAddress, + zeroStringValue, + setCallsGasLimit, + addWrappedTokenFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxUniversalToken)), + fmt.Sprintf("%02x", params.NumOfDecimalsUniversal), + }) + log.Info("add wrapped token tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) whitelistTokenOnWrapper(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // wrapper whitelist token + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.WrapperAddress, + zeroStringValue, + setCallsGasLimit, + whitelistTokenFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + fmt.Sprintf("%02x", params.NumOfDecimalsChainSpecific), + hex.EncodeToString([]byte(tkData.MvxUniversalToken))}) + log.Info("wrapper whitelist token tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) setRolesForSpecificTokenOnSafe(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // set local roles esdt safe + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.ESDTSystemContractAddress, + zeroStringValue, + setCallsGasLimit, + setSpecialRoleFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + handler.SafeAddress.Hex(), + hex.EncodeToString([]byte(esdtRoleLocalMint)), + hex.EncodeToString([]byte(esdtRoleLocalBurn))}) + log.Info("set local roles esdt safe tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) addMappingInMultisig(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // add mapping + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + setCallsGasLimit, + addMappingFunction, + []string{ + hex.EncodeToString(tkData.EthErc20Address.Bytes()), + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken))}) + log.Info("add mapping tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) whitelistTokenOnMultisig(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // whitelist token + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + setCallsGasLimit, + esdtSafeAddTokenToWhitelistFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString([]byte(params.MvxChainSpecificTokenTicker)), + getHexBool(params.IsMintBurnOnMvX), + getHexBool(params.IsNativeOnMvX), + hex.EncodeToString(zeroValueBigInt.Bytes()), // total_balance + hex.EncodeToString(zeroValueBigInt.Bytes()), // mint_balance + hex.EncodeToString(zeroValueBigInt.Bytes()), // burn_balance + }) + log.Info("whitelist token tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) setInitialSupply(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // set initial supply + if len(params.InitialSupplyValue) > 0 { + initialSupply, okConvert := big.NewInt(0).SetString(params.InitialSupplyValue, 10) + require.True(handler, okConvert) + + if params.IsMintBurnOnMvX { + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + setCallsGasLimit, + initSupplyMintBurnEsdtSafe, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString(initialSupply.Bytes()), + hex.EncodeToString([]byte{0}), + }, + ) + log.Info("initial supply tx executed", "hash", hash, "status", txResult.Status, + "initial mint", params.InitialSupplyValue, "initial burned", "0") + } else { + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + setCallsGasLimit, + esdtTransferFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString(initialSupply.Bytes()), + hex.EncodeToString([]byte(initSupplyEsdtSafe)), + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString(initialSupply.Bytes()), + }) + + log.Info("initial supply tx executed", "hash", hash, "status", txResult.Status, + "initial value", params.InitialSupplyValue) + } + } +} + +func (handler *MultiversxHandler) setPairDecimalsOnAggregator(ctx context.Context, params IssueTokenParams) { + // setPairDecimals on aggregator + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.AggregatorAddress, + zeroStringValue, + setCallsGasLimit, + setPairDecimalsFunction, + []string{ + hex.EncodeToString([]byte(gwei)), + hex.EncodeToString([]byte(params.MvxChainSpecificTokenTicker)), + fmt.Sprintf("%02x", params.NumOfDecimalsChainSpecific)}) + log.Info("setPairDecimals tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) setMaxBridgeAmountOnSafe(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // safe set max bridge amount for token + maxBridgedAmountForTokenInt, _ := big.NewInt(0).SetString(maxBridgedAmountForToken, 10) + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + setCallsGasLimit, + esdtSafeSetMaxBridgedAmountForTokenFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString(maxBridgedAmountForTokenInt.Bytes())}) + log.Info("safe set max bridge amount for token tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) setMaxBridgeAmountOnMultitransfer(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // multi-transfer set max bridge amount for token + maxBridgedAmountForTokenInt, _ := big.NewInt(0).SetString(maxBridgedAmountForToken, 10) + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + setCallsGasLimit, + multiTransferEsdtSetMaxBridgedAmountForTokenFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxChainSpecificToken)), + hex.EncodeToString(maxBridgedAmountForTokenInt.Bytes())}) + log.Info("multi-transfer set max bridge amount for token tx executed", "hash", hash, "status", txResult.Status) +} + +func (handler *MultiversxHandler) getTokenNameFromResult(txResult data.TransactionOnNetwork) string { + for _, event := range txResult.Logs.Events { + if event.Identifier == issueFunction { + require.Greater(handler, len(event.Topics), 1) + + return string(event.Topics[0]) + } + } + + require.Fail(handler, "did not find the event with the issue identifier") + return "" +} + +// SubmitAggregatorBatch will submit the aggregator batch +func (handler *MultiversxHandler) SubmitAggregatorBatch(ctx context.Context, params IssueTokenParams) { + txHashes := make([]string, 0, len(handler.OraclesKeys)) + for _, key := range handler.OraclesKeys { + hash := handler.submitAggregatorBatchForKey(ctx, key, params) + txHashes = append(txHashes, hash) + } + + for _, hash := range txHashes { + txResult := handler.ChainSimulator.GetTransactionResult(ctx, hash) + log.Info("submit aggregator batch tx", "hash", hash, "status", txResult.Status) + } +} + +func (handler *MultiversxHandler) submitAggregatorBatchForKey(ctx context.Context, key KeysHolder, params IssueTokenParams) string { + timestamp := handler.ChainSimulator.GetBlockchainTimeStamp(ctx) + require.Greater(handler, timestamp, uint64(0), "something went wrong and the chain simulator returned 0 for the current timestamp") + + timestampAsBigInt := big.NewInt(0).SetUint64(timestamp) + + hash := handler.ChainSimulator.ScCallWithoutGenerateBlocks( + ctx, + key.MvxSk, + handler.AggregatorAddress, + zeroStringValue, + setCallsGasLimit, + submitBatchFunction, + []string{ + hex.EncodeToString([]byte(gwei)), + hex.EncodeToString([]byte(params.MvxChainSpecificTokenTicker)), + hex.EncodeToString(timestampAsBigInt.Bytes()), + hex.EncodeToString(feeInt.Bytes()), + fmt.Sprintf("%02x", params.NumOfDecimalsChainSpecific)}) + + log.Info("submit aggregator batch tx sent", "transaction hash", hash, "submitter", key.MvxAddress.Bech32()) + + return hash +} + +// SendDepositTransactionFromMultiversx will send the deposit transaction from MultiversX +func (handler *MultiversxHandler) SendDepositTransactionFromMultiversx(ctx context.Context, token *TokenData, params TestTokenParams, value *big.Int) { + if params.HasChainSpecificToken { + handler.unwrapCreateTransaction(ctx, token, value) + return + } + + handler.createTransactionWithoutUnwrap(ctx, token, value) +} + +func (handler *MultiversxHandler) createTransactionWithoutUnwrap(ctx context.Context, token *TokenData, value *big.Int) { + // create transaction params + params := []string{ + hex.EncodeToString([]byte(token.MvxUniversalToken)), + hex.EncodeToString(value.Bytes()), + hex.EncodeToString([]byte(createTransactionFunction)), + hex.EncodeToString(handler.TestKeys.EthAddress.Bytes()), + } + dataField := strings.Join(params, "@") + + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.TestKeys.MvxSk, + handler.SafeAddress, + zeroStringValue, + createDepositGasLimit+gasLimitPerDataByte*uint64(len(dataField)), + esdtTransferFunction, + params, + ) + log.Info("MultiversX->Ethereum createTransaction sent", "hash", hash, "token", token.MvxUniversalToken, "status", txResult.Status) +} + +func (handler *MultiversxHandler) unwrapCreateTransaction(ctx context.Context, token *TokenData, value *big.Int) { + // create transaction params + params := []string{ + hex.EncodeToString([]byte(token.MvxUniversalToken)), + hex.EncodeToString(value.Bytes()), + hex.EncodeToString([]byte(unwrapTokenCreateTransactionFunction)), + hex.EncodeToString([]byte(token.MvxChainSpecificToken)), + hex.EncodeToString(handler.SafeAddress.Bytes()), + hex.EncodeToString(handler.TestKeys.EthAddress.Bytes()), + } + dataField := strings.Join(params, "@") + + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + handler.TestKeys.MvxSk, + handler.WrapperAddress, + zeroStringValue, + createDepositGasLimit+gasLimitPerDataByte*uint64(len(dataField)), + esdtTransferFunction, + params, + ) + log.Info("MultiversX->Ethereum unwrapCreateTransaction sent", "hash", hash, "token", token.MvxUniversalToken, "status", txResult.Status) +} + +// TestWithdrawFees will try to withdraw the fees for the provided token from the safe contract to the owner +func (handler *MultiversxHandler) TestWithdrawFees( + ctx context.Context, + token string, + expectedDeltaForRefund *big.Int, + expectedDeltaForAccumulated *big.Int, +) { + handler.withdrawFees(ctx, token, expectedDeltaForRefund, getRefundFeesForEthereumFunction, withdrawRefundFeesForEthereumFunction) + handler.withdrawFees(ctx, token, expectedDeltaForAccumulated, getTransactionFeesFunction, withdrawTransactionFeesFunction) +} + +func (handler *MultiversxHandler) withdrawFees(ctx context.Context, + token string, + expectedDelta *big.Int, + getFunction string, + withdrawFunction string, +) { + queryParams := []string{ + hex.EncodeToString([]byte(token)), + } + responseData := handler.ChainSimulator.ExecuteVMQuery(ctx, handler.SafeAddress, getFunction, queryParams) + value := big.NewInt(0).SetBytes(responseData[0]) + require.Equal(handler, expectedDelta.String(), value.String()) + if expectedDelta.Cmp(zeroValueBigInt) == 0 { + return + } + + handler.ChainSimulator.GenerateBlocks(ctx, 5) // ensure block finality + initialBalanceStr := handler.ChainSimulator.GetESDTBalance(ctx, handler.OwnerKeys.MvxAddress, token) + initialBalance, ok := big.NewInt(0).SetString(initialBalanceStr, 10) + require.True(handler, ok) + + handler.ChainSimulator.ScCall( + ctx, + handler.OwnerKeys.MvxSk, + handler.MultisigAddress, + zeroStringValue, + generalSCCallGasLimit, + withdrawFunction, + []string{ + hex.EncodeToString([]byte(token)), + }, + ) + + handler.ChainSimulator.GenerateBlocks(ctx, 5) // ensure block finality + finalBalanceStr := handler.ChainSimulator.GetESDTBalance(ctx, handler.OwnerKeys.MvxAddress, token) + finalBalance, ok := big.NewInt(0).SetString(finalBalanceStr, 10) + require.True(handler, ok) + + require.Equal(handler, expectedDelta, finalBalance.Sub(finalBalance, initialBalance), + fmt.Sprintf("mismatch on balance check after the call to %s: initial balance: %s, final balance %s, expected delta: %s", + withdrawFunction, initialBalanceStr, finalBalanceStr, expectedDelta.String())) +} + +// TransferToken is able to create an ESDT transfer +func (handler *MultiversxHandler) TransferToken(ctx context.Context, source KeysHolder, receiver KeysHolder, amount *big.Int, params TestTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + // transfer to the test key, so it will have funds to carry on with the deposits + hash, txResult := handler.ChainSimulator.ScCall( + ctx, + source.MvxSk, + receiver.MvxAddress, + zeroStringValue, + createDepositGasLimit, + esdtTransferFunction, + []string{ + hex.EncodeToString([]byte(tkData.MvxUniversalToken)), + hex.EncodeToString(amount.Bytes())}) + + log.Info("transfer to tx executed", + "source address", source.MvxAddress.Bech32(), + "receiver", receiver.MvxAddress.Bech32(), + "token", tkData.MvxUniversalToken, + "amount", amount.String(), + "hash", hash, "status", txResult.Status) +} + +func getHexBool(input bool) string { + if input { + return hexTrue + } + + return hexFalse +} diff --git a/integrationTests/relayers/slowTests/framework/testSetup.go b/integrationTests/relayers/slowTests/framework/testSetup.go new file mode 100644 index 00000000..51f3dd36 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/testSetup.go @@ -0,0 +1,385 @@ +package framework + +import ( + "context" + "fmt" + "math/big" + "os" + "path" + "sync" + "sync/atomic" + "testing" + + "github.com/multiversx/mx-bridge-eth-go/config" + "github.com/multiversx/mx-bridge-eth-go/executors/multiversx/module" + sdkCore "github.com/multiversx/mx-sdk-go/core" + "github.com/stretchr/testify/require" +) + +// framework constants +const ( + LogStepMarker = "#################################### %s ####################################" + proxyCacherExpirationSeconds = 600 + proxyMaxNoncesDelta = 7 + NumRelayers = 3 + NumOracles = 3 + quorum = "03" +) + +// TestSetup is the struct that holds all subcomponents for the testing infrastructure +type TestSetup struct { + testing.TB + TokensRegistry + *KeysStore + Bridge *BridgeComponents + EthereumHandler *EthereumHandler + MultiversxHandler *MultiversxHandler + WorkingDir string + ChainSimulator ChainSimulatorWrapper + ScCallerKeys KeysHolder + ScCallerModuleInstance SCCallerModule + + ctxCancel func() + Ctx context.Context + mutBalances sync.RWMutex + esdtBalanceForSafe map[string]*big.Int + ethBalanceTestAddress map[string]*big.Int + numScCallsInTest uint32 +} + +// NewTestSetup creates a new e2e test setup +func NewTestSetup(tb testing.TB) *TestSetup { + log.Info(fmt.Sprintf(LogStepMarker, "starting setup")) + + setup := &TestSetup{ + TB: tb, + TokensRegistry: NewTokenRegistry(tb), + WorkingDir: tb.TempDir(), + esdtBalanceForSafe: make(map[string]*big.Int), + ethBalanceTestAddress: make(map[string]*big.Int), + } + setup.KeysStore = NewKeysStore(tb, setup.WorkingDir, NumRelayers, NumOracles) + + // create a test context + setup.Ctx, setup.ctxCancel = context.WithCancel(context.Background()) + + setup.EthereumHandler = NewEthereumHandler(tb, setup.Ctx, setup.KeysStore, setup.TokensRegistry, quorum) + setup.EthereumHandler.DeployContracts(setup.Ctx) + + setup.createChainSimulatorWrapper() + setup.MultiversxHandler = NewMultiversxHandler(tb, setup.Ctx, setup.KeysStore, setup.TokensRegistry, setup.ChainSimulator, quorum) + setup.MultiversxHandler.DeployAndSetContracts(setup.Ctx) + + return setup +} + +func (setup *TestSetup) createChainSimulatorWrapper() { + // create a new working directory + tmpDir := path.Join(setup.TempDir(), "test") + err := os.MkdirAll(tmpDir, os.ModePerm) + require.NoError(setup, err) + + // start the chain simulator + args := ArgChainSimulatorWrapper{ + TB: setup.TB, + ProxyCacherExpirationSeconds: proxyCacherExpirationSeconds, + ProxyMaxNoncesDelta: proxyMaxNoncesDelta, + } + setup.ChainSimulator = CreateChainSimulatorWrapper(args) + require.NoError(setup, err) +} + +// StartRelayersAndScModule will start the bridge and the SC execution module +func (setup *TestSetup) StartRelayersAndScModule() { + log.Info(fmt.Sprintf(LogStepMarker, "starting relayers & sc execution module")) + + // start relayers + setup.Bridge = NewBridgeComponents( + setup.TB, + setup.WorkingDir, + setup.ChainSimulator, + setup.EthereumHandler.EthChainWrapper, + setup.EthereumHandler.Erc20ContractsHolder, + setup.EthereumHandler.SimulatedChain, + NumRelayers, + setup.EthereumHandler.SafeAddress.Hex(), + setup.MultiversxHandler.SafeAddress, + setup.MultiversxHandler.MultisigAddress, + ) + + setup.startScCallerModule() +} + +func (setup *TestSetup) startScCallerModule() { + cfg := config.ScCallsModuleConfig{ + ScProxyBech32Address: setup.MultiversxHandler.ScProxyAddress.Bech32(), + ExtraGasToExecute: 60_000_000, // 60 million: this ensures that a SC call with 0 gas limit is refunded + MaxGasLimitToUse: 249_999_999, // max cross shard limit + GasLimitForOutOfGasTransactions: 30_000_000, // gas to use when a higher than max allowed is encountered + NetworkAddress: setup.ChainSimulator.GetNetworkAddress(), + ProxyMaxNoncesDelta: 5, + ProxyFinalityCheck: false, + ProxyCacherExpirationSeconds: 60, // 1 minute + ProxyRestAPIEntityType: string(sdkCore.Proxy), + IntervalToResendTxsInSeconds: 1, + PrivateKeyFile: path.Join(setup.WorkingDir, SCCallerFilename), + PollingIntervalInMillis: 1000, // 1 second + Filter: config.PendingOperationsFilterConfig{ + AllowedEthAddresses: []string{"*"}, + AllowedMvxAddresses: []string{"*"}, + AllowedTokens: []string{"*"}, + }, + TransactionChecks: config.TransactionChecksConfig{ + CheckTransactionResults: true, + CloseAppOnError: false, + ExecutionTimeoutInSeconds: 2, + TimeInSecondsBetweenChecks: 1, + }, + } + + var err error + setup.ScCallerModuleInstance, err = module.NewScCallsModule(cfg, log, nil) + require.Nil(setup, err) + log.Info("started SC calls module", "monitoring SC proxy address", setup.MultiversxHandler.ScProxyAddress) +} + +// IssueAndConfigureTokens will issue and configure the provided tokens on both chains +func (setup *TestSetup) IssueAndConfigureTokens(tokens ...TestTokenParams) { + log.Info(fmt.Sprintf(LogStepMarker, fmt.Sprintf("issuing %d tokens", len(tokens)))) + + require.Greater(setup, len(tokens), 0) + + setup.EthereumHandler.PauseContractsForTokenChanges(setup.Ctx) + setup.MultiversxHandler.PauseContractsForTokenChanges(setup.Ctx) + + for _, token := range tokens { + setup.processNumScCallsOperations(token) + setup.AddToken(token.IssueTokenParams) + setup.EthereumHandler.IssueAndWhitelistToken(setup.Ctx, token.IssueTokenParams) + setup.MultiversxHandler.IssueAndWhitelistToken(setup.Ctx, token.IssueTokenParams) + + esdtBalanceForSafe := setup.MultiversxHandler.GetESDTChainSpecificTokenBalance(setup.Ctx, setup.MultiversxHandler.SafeAddress, token.AbstractTokenIdentifier) + ethBalanceForTestAddr := setup.EthereumHandler.GetBalance(setup.TestKeys.EthAddress, token.AbstractTokenIdentifier) + + setup.mutBalances.Lock() + setup.esdtBalanceForSafe[token.AbstractTokenIdentifier] = esdtBalanceForSafe + setup.ethBalanceTestAddress[token.AbstractTokenIdentifier] = ethBalanceForTestAddr + setup.mutBalances.Unlock() + + log.Info("recorded the ESDT balance for safe contract", "token", token.AbstractTokenIdentifier, "balance", esdtBalanceForSafe.String()) + log.Info("recorded the ETH balance for test address", "token", token.AbstractTokenIdentifier, "balance", ethBalanceForTestAddr.String()) + } + + setup.EthereumHandler.UnPauseContractsAfterTokenChanges(setup.Ctx) + setup.MultiversxHandler.UnPauseContractsAfterTokenChanges(setup.Ctx) + + for _, token := range tokens { + setup.MultiversxHandler.SubmitAggregatorBatch(setup.Ctx, token.IssueTokenParams) + } +} + +func (setup *TestSetup) processNumScCallsOperations(token TestTokenParams) { + for _, op := range token.TestOperations { + if len(op.MvxSCCallData) > 0 || op.MvxForceSCCall { + atomic.AddUint32(&setup.numScCallsInTest, 1) + } + } +} + +// GetNumScCallsOperations returns the number of SC calls in this test setup +func (setup *TestSetup) GetNumScCallsOperations() uint32 { + return atomic.LoadUint32(&setup.numScCallsInTest) +} + +// IsTransferDoneFromEthereum returns true if all provided tokens are bridged from Ethereum towards MultiversX +func (setup *TestSetup) IsTransferDoneFromEthereum(tokens ...TestTokenParams) bool { + isDone := true + for _, params := range tokens { + isDone = isDone && setup.isTransferDoneFromEthereumForToken(params) + } + + return isDone +} + +func (setup *TestSetup) isTransferDoneFromEthereumForToken(params TestTokenParams) bool { + expectedValueOnReceiver := big.NewInt(0) + expectedValueOnContract := big.NewInt(0) + for _, operation := range params.TestOperations { + if operation.ValueToTransferToMvx == nil { + continue + } + + if len(operation.MvxSCCallData) > 0 || operation.MvxForceSCCall { + if !operation.MvxFaultySCCall { + expectedValueOnContract.Add(expectedValueOnContract, operation.ValueToTransferToMvx) + } + } else { + expectedValueOnReceiver.Add(expectedValueOnReceiver, operation.ValueToTransferToMvx) + } + } + + receiverBalance := setup.MultiversxHandler.GetESDTUniversalTokenBalance(setup.Ctx, setup.TestKeys.MvxAddress, params.AbstractTokenIdentifier) + if receiverBalance.String() != expectedValueOnReceiver.String() { + return false + } + + contractBalance := setup.MultiversxHandler.GetESDTUniversalTokenBalance(setup.Ctx, setup.MultiversxHandler.TestCallerAddress, params.AbstractTokenIdentifier) + return contractBalance.String() == expectedValueOnContract.String() +} + +// IsTransferDoneFromEthereumWithRefund returns true if all provided tokens are bridged from Ethereum towards MultiversX including refunds +func (setup *TestSetup) IsTransferDoneFromEthereumWithRefund(tokens ...TestTokenParams) bool { + isDone := true + for _, params := range tokens { + isDone = isDone && setup.isTransferDoneFromEthereumWithRefundForToken(params) + } + + return isDone +} + +func (setup *TestSetup) isTransferDoneFromEthereumWithRefundForToken(params TestTokenParams) bool { + expectedValueOnReceiver := big.NewInt(0) + for _, operation := range params.TestOperations { + valueToTransferToMvx := big.NewInt(0) + if operation.ValueToTransferToMvx != nil { + valueToTransferToMvx.Set(operation.ValueToTransferToMvx) + } + + valueToSendFromMvX := big.NewInt(0) + if operation.ValueToSendFromMvX != nil { + valueToSendFromMvX.Set(operation.ValueToSendFromMvX) + // we subtract the fee also + expectedValueOnReceiver.Sub(expectedValueOnReceiver, feeInt) + } + + expectedValueOnReceiver.Add(expectedValueOnReceiver, big.NewInt(0).Sub(valueToSendFromMvX, valueToTransferToMvx)) + if len(operation.MvxSCCallData) > 0 || operation.MvxForceSCCall { + if operation.MvxFaultySCCall { + // the balance should be bridged back to the receiver on Ethereum - fee + expectedValueOnReceiver.Add(expectedValueOnReceiver, valueToTransferToMvx) + expectedValueOnReceiver.Sub(expectedValueOnReceiver, feeInt) + } + } + } + + receiverBalance := setup.EthereumHandler.GetBalance(setup.TestKeys.EthAddress, params.AbstractTokenIdentifier) + return receiverBalance.String() == expectedValueOnReceiver.String() +} + +// IsTransferDoneFromMultiversX returns true if all provided tokens are bridged from MultiversX towards Ethereum +func (setup *TestSetup) IsTransferDoneFromMultiversX(tokens ...TestTokenParams) bool { + isDone := true + for _, params := range tokens { + isDone = isDone && setup.isTransferDoneFromMultiversXForToken(params) + } + + return isDone +} + +func (setup *TestSetup) isTransferDoneFromMultiversXForToken(params TestTokenParams) bool { + setup.mutBalances.Lock() + initialBalanceForSafe := setup.esdtBalanceForSafe[params.AbstractTokenIdentifier] + expectedReceiver := big.NewInt(0).Set(setup.ethBalanceTestAddress[params.AbstractTokenIdentifier]) + expectedReceiver.Add(expectedReceiver, params.EthTestAddrExtraBalance) + setup.mutBalances.Unlock() + + ethTestBalance := setup.EthereumHandler.GetBalance(setup.TestKeys.EthAddress, params.AbstractTokenIdentifier) + isTransferDoneFromMultiversX := ethTestBalance.String() == expectedReceiver.String() + + expectedEsdtSafe := big.NewInt(0).Add(initialBalanceForSafe, params.ESDTSafeExtraBalance) + balanceForSafe := setup.MultiversxHandler.GetESDTChainSpecificTokenBalance(setup.Ctx, setup.MultiversxHandler.SafeAddress, params.AbstractTokenIdentifier) + isSafeContractOnCorrectBalance := expectedEsdtSafe.String() == balanceForSafe.String() + + return isTransferDoneFromMultiversX && isSafeContractOnCorrectBalance +} + +// CreateBatchOnMultiversX will create deposits that will be gathered in a batch on MultiversX +func (setup *TestSetup) CreateBatchOnMultiversX(tokensParams ...TestTokenParams) { + for _, params := range tokensParams { + setup.createBatchOnMultiversXForToken(params) + } +} + +func (setup *TestSetup) createBatchOnMultiversXForToken(params TestTokenParams) { + token := setup.GetTokenData(params.AbstractTokenIdentifier) + require.NotNil(setup, token) + + setup.transferTokensToTestKey(params) + valueToMintOnEthereum := setup.sendFromMultiversxToEthereumForToken(params) + setup.EthereumHandler.Mint(setup.Ctx, params, valueToMintOnEthereum) +} + +func (setup *TestSetup) transferTokensToTestKey(params TestTokenParams) { + depositValue := big.NewInt(0) + for _, operation := range params.TestOperations { + if operation.ValueToSendFromMvX == nil { + continue + } + + depositValue.Add(depositValue, operation.ValueToSendFromMvX) + } + + setup.MultiversxHandler.TransferToken( + setup.Ctx, + setup.OwnerKeys, + setup.TestKeys, + depositValue, + params, + ) +} + +// SendFromMultiversxToEthereum will create the deposits that will be gathered in a batch on MultiversX (without mint on Ethereum) +func (setup *TestSetup) SendFromMultiversxToEthereum(tokensParams ...TestTokenParams) { + for _, params := range tokensParams { + _ = setup.sendFromMultiversxToEthereumForToken(params) + } +} + +func (setup *TestSetup) sendFromMultiversxToEthereumForToken(params TestTokenParams) *big.Int { + token := setup.GetTokenData(params.AbstractTokenIdentifier) + require.NotNil(setup, token) + + depositValue := big.NewInt(0) + for _, operation := range params.TestOperations { + if operation.ValueToSendFromMvX == nil { + continue + } + + depositValue.Add(depositValue, operation.ValueToSendFromMvX) + setup.MultiversxHandler.SendDepositTransactionFromMultiversx(setup.Ctx, token, params, operation.ValueToSendFromMvX) + } + + return depositValue +} + +// TestWithdrawTotalFeesOnEthereumForTokens will test the withdrawal functionality for the provided test tokens +func (setup *TestSetup) TestWithdrawTotalFeesOnEthereumForTokens(tokensParams ...TestTokenParams) { + for _, param := range tokensParams { + token := setup.TokensRegistry.GetTokenData(param.AbstractTokenIdentifier) + + expectedAccumulated := big.NewInt(0) + for _, operation := range param.TestOperations { + if operation.ValueToSendFromMvX == nil { + continue + } + if operation.ValueToSendFromMvX.Cmp(zeroValueBigInt) == 0 { + continue + } + + expectedAccumulated.Add(expectedAccumulated, feeInt) + } + + setup.MultiversxHandler.TestWithdrawFees(setup.Ctx, token.MvxChainSpecificToken, zeroValueBigInt, expectedAccumulated) + } +} + +// Close will close the test subcomponents +func (setup *TestSetup) Close() { + log.Info(fmt.Sprintf(LogStepMarker, "closing relayers & sc execution module")) + + setup.Bridge.CloseRelayers() + require.NoError(setup, setup.EthereumHandler.Close()) + + setup.ctxCancel() + _ = setup.ScCallerModuleInstance.Close() +} diff --git a/integrationTests/relayers/slowTests/framework/tokensRegistry.go b/integrationTests/relayers/slowTests/framework/tokensRegistry.go new file mode 100644 index 00000000..5ee3bc39 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/tokensRegistry.go @@ -0,0 +1,88 @@ +package framework + +import ( + "sync" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +type tokensRegistry struct { + testing.TB + mut sync.RWMutex + tokens map[string]*TokenData +} + +// NewTokenRegistry creates a new instance of type tokens registry +func NewTokenRegistry(tb testing.TB) *tokensRegistry { + return &tokensRegistry{ + TB: tb, + tokens: make(map[string]*TokenData, 100), + } +} + +// AddToken will add a new test token +func (registry *tokensRegistry) AddToken(params IssueTokenParams) { + registry.mut.Lock() + defer registry.mut.Unlock() + + _, found := registry.tokens[params.AbstractTokenIdentifier] + require.False(registry, found, "can not register more than one instance of the same abstract token identifier %s", params.AbstractTokenIdentifier) + + newToken := &TokenData{ + AbstractTokenIdentifier: params.AbstractTokenIdentifier, + MvxUniversalTokenTicker: params.MvxUniversalTokenTicker, + MvxChainSpecificTokenTicker: params.MvxChainSpecificTokenDisplayName, + EthTokenName: params.EthTokenName, + EthTokenSymbol: params.EthTokenSymbol, + } + + registry.tokens[params.AbstractTokenIdentifier] = newToken +} + +// RegisterUniversalToken will save the universal token identifier +func (registry *tokensRegistry) RegisterUniversalToken(abstractTokenIdentifier string, mvxUniversalToken string) { + registry.mut.Lock() + defer registry.mut.Unlock() + + data, found := registry.tokens[abstractTokenIdentifier] + require.True(registry, found, "abstract token identifier not registered %s", abstractTokenIdentifier) + + data.MvxUniversalToken = mvxUniversalToken +} + +// RegisterChainSpecificToken will save the chain specific token identifier +func (registry *tokensRegistry) RegisterChainSpecificToken(abstractTokenIdentifier string, mvxChainSpecificToken string) { + registry.mut.Lock() + defer registry.mut.Unlock() + + data, found := registry.tokens[abstractTokenIdentifier] + require.True(registry, found, "abstract token identifier not registered %s", abstractTokenIdentifier) + + data.MvxChainSpecificToken = mvxChainSpecificToken +} + +// RegisterEthAddressAndContract will save under the mutex lock the provided Ethereum address and contract +func (registry *tokensRegistry) RegisterEthAddressAndContract( + abstractTokenIdentifier string, + ethErc20Address common.Address, + ethErc20Contract ERC20Contract, +) { + registry.mut.Lock() + defer registry.mut.Unlock() + + data, found := registry.tokens[abstractTokenIdentifier] + require.True(registry, found, "abstract token identifier not registered %s", abstractTokenIdentifier) + + data.EthErc20Address = ethErc20Address + data.EthErc20Contract = ethErc20Contract +} + +// GetTokenData will return the token data based on the abstract identifier provided +func (registry *tokensRegistry) GetTokenData(abstractTokenIdentifier string) *TokenData { + registry.mut.RLock() + defer registry.mut.RUnlock() + + return registry.tokens[abstractTokenIdentifier] +} diff --git a/integrationTests/relayers/slowTests/framework/types.go b/integrationTests/relayers/slowTests/framework/types.go new file mode 100644 index 00000000..dc165fe4 --- /dev/null +++ b/integrationTests/relayers/slowTests/framework/types.go @@ -0,0 +1,69 @@ +package framework + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +// IssueTokenParams the parameters when issuing a new token +type IssueTokenParams struct { + InitialSupplyParams + AbstractTokenIdentifier string + + // MultiversX + NumOfDecimalsUniversal int + NumOfDecimalsChainSpecific byte + MvxUniversalTokenTicker string + MvxChainSpecificTokenTicker string + MvxUniversalTokenDisplayName string + MvxChainSpecificTokenDisplayName string + ValueToMintOnMvx string + IsMintBurnOnMvX bool + IsNativeOnMvX bool + HasChainSpecificToken bool + + // Ethereum + EthTokenName string + EthTokenSymbol string + ValueToMintOnEth string + IsMintBurnOnEth bool + IsNativeOnEth bool +} + +// InitialSupplyParams represents the initial supply parameters +type InitialSupplyParams struct { + InitialSupplyValue string +} + +// TokenOperations defines a token operation in a test. Usually this can define one or to deposits in a batch +type TokenOperations struct { + ValueToTransferToMvx *big.Int + ValueToSendFromMvX *big.Int + MvxSCCallData []byte + MvxFaultySCCall bool + MvxForceSCCall bool +} + +// TestTokenParams defines a token collection of operations in one or 2 batches +type TestTokenParams struct { + IssueTokenParams + TestOperations []TokenOperations + ESDTSafeExtraBalance *big.Int + EthTestAddrExtraBalance *big.Int +} + +// TokenData represents a test token data +type TokenData struct { + AbstractTokenIdentifier string + + MvxUniversalTokenTicker string + MvxChainSpecificTokenTicker string + EthTokenName string + EthTokenSymbol string + + MvxUniversalToken string + MvxChainSpecificToken string + EthErc20Address common.Address + EthErc20Contract ERC20Contract +} diff --git a/integrationTests/relayers/slowTests/refundWithChainSimulator_test.go b/integrationTests/relayers/slowTests/refundWithChainSimulator_test.go new file mode 100644 index 00000000..0a5d8b27 --- /dev/null +++ b/integrationTests/relayers/slowTests/refundWithChainSimulator_test.go @@ -0,0 +1,286 @@ +//go:build slow + +// To run these slow tests, simply add the slow tag on the go test command. Also, provide a chain simulator instance on the 8085 port +// example: go test -tags slow + +package slowTests + +import ( + "math/big" + "strings" + "testing" + + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" + "github.com/stretchr/testify/require" +) + +func TestRelayersShouldExecuteTransfersWithRefund(t *testing.T) { + t.Run("unknown marker and malformed SC call data should refund", func(t *testing.T) { + callData := []byte{5, 4, 55} + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("unknown marker and malformed SC call data should refund with MEX", func(t *testing.T) { + callData := []byte{5, 4, 55} + mexToken := GenerateTestMEXToken() + mexToken.TestOperations[2].MvxSCCallData = callData + mexToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + mexToken, + ) + }) + t.Run("malformed SC call data should refund", func(t *testing.T) { + callData := []byte{bridgeCore.DataPresentProtocolMarker, 4, 55} + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("unknown function should refund", func(t *testing.T) { + callData := createScCallData("unknownFunction", 50000000) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("wrong deposit with empty sc call data should refund", func(t *testing.T) { + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = nil + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.TestOperations[2].MvxForceSCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = nil + memeToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.TestOperations[2].MvxForceSCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("0 gas limit should refund", func(t *testing.T) { + callData := createScCallData("callPayable", 0) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("small gas limit should refund", func(t *testing.T) { + callData := createScCallData("callPayable", 2000) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("extra parameter should refund", func(t *testing.T) { + callData := createScCallData("callPayable", 50000000, "extra parameter") + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("no arguments should refund", func(t *testing.T) { + callData := createScCallData("callPayableWithParams", 50000000) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("wrong number of arguments should refund", func(t *testing.T) { + callData := createScCallData("callPayableWithParams", 50000000, string([]byte{37})) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("not an uint64 argument should refund", func(t *testing.T) { + malformedUint64String := string([]byte{37, 36, 35, 34, 33, 32, 31, 32, 33}) // 9 bytes instead of 8 + dummyAddress := strings.Repeat("2", 32) + + callData := createScCallData("callPayableWithParams", 50000000, malformedUint64String, dummyAddress) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) + t.Run("wrong arguments encoding should refund", func(t *testing.T) { + callData := createScCallData("callPayableWithParams", 50000000) + // the last byte is the data missing marker, we will replace that + callData[len(callData)-1] = bridgeCore.DataPresentProtocolMarker + // add garbage data + callData = append(callData, []byte{5, 4, 55}...) + + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + usdcToken.EthTestAddrExtraBalance = big.NewInt(-5000 + 2500 - 50 - 7000 + 300 - 50 - 1000 + 950) // -(eth->mvx) + (mvx->eth) - fees + revert after bad SC call + usdcToken.ESDTSafeExtraBalance = big.NewInt(150) // extra is just for the fees for the 2 transfers mvx->eth and the failed eth->mvx that needed refund + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + ) + }) +} + +func testRelayersWithChainSimulatorAndTokensAndRefund(tb testing.TB, manualStopChan chan error, tokens ...framework.TestTokenParams) { + startsFromEthFlow, startsFromMvXFlow := createFlowsBasedOnToken(tb, tokens...) + + setupFunc := func(tb testing.TB, setup *framework.TestSetup) { + startsFromMvXFlow.setup = setup + startsFromEthFlow.setup = setup + + setup.IssueAndConfigureTokens(tokens...) + setup.MultiversxHandler.CheckForZeroBalanceOnReceivers(setup.Ctx, tokens...) + if len(startsFromEthFlow.tokens) > 0 { + setup.EthereumHandler.CreateBatchOnEthereum(setup.Ctx, setup.MultiversxHandler.TestCallerAddress, startsFromEthFlow.tokens...) + } + if len(startsFromMvXFlow.tokens) > 0 { + setup.CreateBatchOnMultiversX(startsFromMvXFlow.tokens...) + } + } + + processFunc := func(tb testing.TB, setup *framework.TestSetup) bool { + if startsFromEthFlow.process() && startsFromMvXFlow.process() && startsFromMvXFlow.areTokensFullyRefunded() { + return true + } + + // commit blocks in order to execute incoming txs from relayers + setup.EthereumHandler.SimulatedChain.Commit() + setup.ChainSimulator.GenerateBlocks(setup.Ctx, 1) + require.LessOrEqual(tb, setup.ScCallerModuleInstance.GetNumSentTransaction(), setup.GetNumScCallsOperations()) + + return false + } + + _ = testRelayersWithChainSimulator(tb, + setupFunc, + processFunc, + manualStopChan, + ) +} diff --git a/integrationTests/relayers/slowTests/startsFromEthereumEdgecaseFlow.go b/integrationTests/relayers/slowTests/startsFromEthereumEdgecaseFlow.go new file mode 100644 index 00000000..214581af --- /dev/null +++ b/integrationTests/relayers/slowTests/startsFromEthereumEdgecaseFlow.go @@ -0,0 +1,49 @@ +//go:build slow + +package slowTests + +import ( + "fmt" + "testing" + + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" +) + +type startsFromEthereumEdgecaseFlow struct { + testing.TB + setup *framework.TestSetup + ethToMvxDone bool + mvxToEthDone bool + tokens []framework.TestTokenParams +} + +func (flow *startsFromEthereumEdgecaseFlow) process() (finished bool) { + if len(flow.tokens) == 0 { + return true + } + if flow.mvxToEthDone && flow.ethToMvxDone { + return true + } + + isTransferDoneFromEthereum := flow.setup.IsTransferDoneFromEthereum(flow.tokens...) + if !flow.ethToMvxDone && isTransferDoneFromEthereum { + flow.ethToMvxDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "Ethereum->MultiversX transfer finished, now sending back to Ethereum & another round from Ethereum...")) + + flow.setup.SendFromMultiversxToEthereum(flow.tokens...) + flow.setup.EthereumHandler.SendFromEthereumToMultiversX(flow.setup.Ctx, flow.setup.MultiversxHandler.TestCallerAddress, flow.tokens...) + } + if !flow.ethToMvxDone { + // return here, no reason to check downwards + return false + } + + isTransferDoneFromMultiversX := flow.setup.IsTransferDoneFromMultiversX(flow.tokens...) + if !flow.mvxToEthDone && isTransferDoneFromMultiversX { + flow.mvxToEthDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "MultiversX<->Ethereum from Ethereum transfers done")) + return true + } + + return false +} diff --git a/integrationTests/relayers/slowTests/startsFromEthereumFlow.go b/integrationTests/relayers/slowTests/startsFromEthereumFlow.go new file mode 100644 index 00000000..76c13fe1 --- /dev/null +++ b/integrationTests/relayers/slowTests/startsFromEthereumFlow.go @@ -0,0 +1,48 @@ +//go:build slow + +package slowTests + +import ( + "fmt" + "testing" + + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" +) + +type startsFromEthereumFlow struct { + testing.TB + setup *framework.TestSetup + ethToMvxDone bool + mvxToEthDone bool + tokens []framework.TestTokenParams +} + +func (flow *startsFromEthereumFlow) process() (finished bool) { + if len(flow.tokens) == 0 { + return true + } + if flow.mvxToEthDone && flow.ethToMvxDone { + return true + } + + isTransferDoneFromEthereum := flow.setup.IsTransferDoneFromEthereum(flow.tokens...) + if !flow.ethToMvxDone && isTransferDoneFromEthereum { + flow.ethToMvxDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "Ethereum->MultiversX transfer finished, now sending back to Ethereum...")) + + flow.setup.SendFromMultiversxToEthereum(flow.tokens...) + } + if !flow.ethToMvxDone { + // return here, no reason to check downwards + return false + } + + isTransferDoneFromMultiversX := flow.setup.IsTransferDoneFromMultiversX(flow.tokens...) + if !flow.mvxToEthDone && isTransferDoneFromMultiversX { + flow.mvxToEthDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "MultiversX<->Ethereum from Ethereum transfers done")) + return true + } + + return false +} diff --git a/integrationTests/relayers/slowTests/startsFromMultiversxFlow.go b/integrationTests/relayers/slowTests/startsFromMultiversxFlow.go new file mode 100644 index 00000000..caa4dd06 --- /dev/null +++ b/integrationTests/relayers/slowTests/startsFromMultiversxFlow.go @@ -0,0 +1,59 @@ +//go:build slow + +package slowTests + +import ( + "fmt" + "testing" + + "github.com/multiversx/mx-bridge-eth-go/integrationTests/relayers/slowTests/framework" +) + +type startsFromMultiversXFlow struct { + testing.TB + setup *framework.TestSetup + ethToMvxDone bool + mvxToEthDone bool + tokens []framework.TestTokenParams +} + +func (flow *startsFromMultiversXFlow) process() (finished bool) { + if len(flow.tokens) == 0 { + return true + } + if flow.mvxToEthDone && flow.ethToMvxDone { + return true + } + + isTransferDoneFromMultiversX := flow.setup.IsTransferDoneFromMultiversX(flow.tokens...) + if !flow.mvxToEthDone && isTransferDoneFromMultiversX { + flow.mvxToEthDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "MultiversX->Ethereum transfer finished, now sending back to MultiversX...")) + + flow.setup.EthereumHandler.SendFromEthereumToMultiversX(flow.setup.Ctx, flow.setup.MultiversxHandler.TestCallerAddress, flow.tokens...) + } + if !flow.mvxToEthDone { + // return here, no reason to check downwards + return false + } + + isTransferDoneFromEthereum := flow.setup.IsTransferDoneFromEthereum(flow.tokens...) + if !flow.ethToMvxDone && isTransferDoneFromEthereum { + flow.ethToMvxDone = true + log.Info(fmt.Sprintf(framework.LogStepMarker, "MultiversX<->Ethereum from MultiversX transfers done")) + return true + } + + return false +} + +func (flow *startsFromMultiversXFlow) areTokensFullyRefunded() bool { + if len(flow.tokens) == 0 { + return true + } + if !flow.ethToMvxDone { + return false // regular flow is not completed + } + + return flow.setup.IsTransferDoneFromEthereumWithRefund(flow.tokens...) +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/Bridge.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/eth/Bridge.abi.json new file mode 100755 index 00000000..bf15d5f7 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/Bridge.abi.json @@ -0,0 +1,567 @@ +[ + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminRoleTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "isPause", + "type": "bool" + } + ], + "name": "Pause", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "quorum", + "type": "uint256" + } + ], + "name": "QuorumChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RelayerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RelayerRemoved", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "addRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSettleBlockCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "crossTransferStatuses", + "outputs": [ + { + "internalType": "uint256", + "name": "createdBlockNumber", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "depositNonces", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "batchNonceMvx", + "type": "uint256" + }, + { + "internalType": "bytes[]", + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "executeTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "executedBatches", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatch", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "internalType": "struct Batch", + "name": "", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "isBatchFinal", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatchDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "internalType": "struct Deposit[]", + "name": "", + "type": "tuple[]" + }, + { + "internalType": "bool", + "name": "areDepositsFinal", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRelayer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRelayers", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRelayersCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonceMvx", + "type": "uint256" + } + ], + "name": "getStatusesAfterExecution", + "outputs": [ + { + "internalType": "enum DepositStatus[]", + "name": "", + "type": "uint8[]" + }, + { + "internalType": "bool", + "name": "isFinal", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "board", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "initialQuorum", + "type": "uint256" + }, + { + "internalType": "contract ERC20Safe", + "name": "erc20Safe", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isRelayer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "quorum", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "removeRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchSettleLimit", + "type": "uint8" + } + ], + "name": "setBatchSettleLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newQuorum", + "type": "uint256" + } + ], + "name": "setQuorum", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonceMvx", + "type": "uint256" + } + ], + "name": "wasBatchExecuted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/Bridge.hex b/integrationTests/relayers/slowTests/testdata/contracts/eth/Bridge.hex new file mode 100755 index 00000000..a4fdae92 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/Bridge.hex @@ -0,0 +1 @@ +0x608060405234801561001057600080fd5b50612477806100206000396000f3fe608060405234801561001057600080fd5b50600436106101735760003560e01c806375829def116100de578063bee2e4dd11610097578063db626c2d11610071578063db626c2d14610391578063dd39f00d146103b2578063f2e0ec48146103c5578063f851a440146103d857600080fd5b8063bee2e4dd1461034b578063c1ba4e5914610376578063d3d9ec011461038957600080fd5b806375829def146102c15780638456cb59146102d457806384aa1ad0146102dc5780638bad0c0a146102ff57806390924da714610307578063b2c79ca31461032857600080fd5b8063541d554811610130578063541d5548146101e25780635ac44282146102055780635c975abb1461026d57806360f0a5ac146102785780637039e21b1461028b57806372483bf9146102ae57600080fd5b80631703a01814610178578063179ff4b2146101945780633f4ba83a146101a9578063475ed4d0146101b35780634ab3867f146101c657806351db0518146101cf575b600080fd5b61018160055481565b6040519081526020015b60405180910390f35b61019c6103e9565b60405161018b9190611ac7565b6101b161044e565b005b6101b16101c1366004611b29565b6104d1565b61018160045481565b6101b16101dd366004611b91565b61054a565b6101f56101f0366004611b29565b61091e565b604051901515815260200161018b565b610218610213366004611c8d565b610931565b6040805183516001600160701b031681526020808501516001600160401b039081169183019190915284830151169181019190915260609283015161ffff16928101929092521515608082015260a00161018b565b60035460ff166101f5565b6101b1610286366004611b29565b6109cc565b6101f5610299366004611c8d565b60116020526000908152604090205460ff1681565b6101b16102bc366004611d47565b610a05565b6101b16102cf366004611b29565b610b1f565b6101b1610c1a565b6101f56102ea366004611c8d565b60009081526011602052604090205460ff1690565b6101b1610c93565b61031a610315366004611c8d565b610d16565b60405161018b929190611e36565b610181610336366004611c8d565b60126020526000908152604090206001015481565b61035e610359366004611c8d565b610d8d565b6040516001600160a01b03909116815260200161018b565b6101b1610384366004611c8d565b610d9a565b610181610e54565b6103a461039f366004611c8d565b610e64565b60405161018b929190611ee0565b6101b16103c0366004611b29565b610f28565b6101b16103d3366004611f32565b610f6a565b6000546001600160a01b031661035e565b6060600160000180548060200260200160405190810160405280929190818152602001828054801561044457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610426575b5050505050905090565b336104616000546001600160a01b031690565b6001600160a01b0316146104905760405162461bcd60e51b815260040161048790611f55565b60405180910390fd5b6003805460ff19169055604051600081527f9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304906020015b60405180910390a1565b6001600160a01b038116331461053e5760405162461bcd60e51b815260206004820152602c60248201527f52656c61796572526f6c653a2063616e206f6e6c792072656e6f756e6365207260448201526b37b632903337b91039b2b63360a11b6064820152608401610487565b610547816110de565b50565b60035460ff16156105905760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610487565b6105993361091e565b6105f35760405162461bcd60e51b815260206004820152602560248201527f41636365737320436f6e74726f6c3a2073656e646572206973206e6f742052656044820152643630bcb2b960d91b6064820152608401610487565b6005548110156106555760405162461bcd60e51b815260206004820152602760248201527f4e6f7420656e6f756768207369676e61747572657320746f20616368696576656044820152662071756f72756d60c81b6064820152608401610487565b60008381526011602052604090205460ff16156106ad5760405162461bcd60e51b815260206004820152601660248201527510985d18da08185b1c9958591e48195e1958dd5d195960521b6044820152606401610487565b6000838152601160205260409020805460ff191660011790556107406106d38284611f98565b61073b8b8b8f8f8d8d8d8d8d6040518060400160405280601681526020017522bc32b1baba32a130ba31b432b22a3930b739b332b960511b8152506040516020016107279a99989796959493929190612101565b60405160208183030381529060405261117c565b6111ea565b60008a6001600160401b0381111561075a5761075a611ca6565b604051908082528060200260200182016040528015610783578160200160208202803683370190505b50905060005b8b8110156108e4576010546001600160a01b031663dbba0f018e8e848181106107b4576107b4612193565b90506020020160208101906107c99190611b29565b8b8b858181106107db576107db612193565b905060200201358e8e868181106107f4576107f4612193565b90506020020160208101906108099190611b29565b60405160e085901b6001600160e01b03191681526001600160a01b039384166004820152602481019290925290911660448201526064016020604051808303816000875af115801561085f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061088391906121b9565b61088e576004610891565b60035b8282815181106108a3576108a3612193565b602002602001019060048111156108bc576108bc611dfe565b908160048111156108cf576108cf611dfe565b905250806108dc816121ea565b915050610789565b506000848152601260209081526040909120825190916109089183918501906119fe565b5043600190910155505050505050505050505050565b600061092b60018361137c565b92915050565b604080516080810182526000808252602082018190529181018290526060810191909152601054604051632d62214160e11b8152600481018490526000916001600160a01b031690635ac442829060240160a060405180830381865afa15801561099f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c39190612231565b91509150915091565b336109df6000546001600160a01b031690565b6001600160a01b03161461053e5760405162461bcd60e51b815260040161048790611f55565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015610a4a5750825b90506000826001600160401b03166001148015610a665750303b155b905081158015610a74575080155b15610a925760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610abc57845460ff60401b1916600160401b1785555b610ac46113a1565b610acf8888886113bb565b8315610b1557845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b33610b326000546001600160a01b031690565b6001600160a01b031614610b585760405162461bcd60e51b815260040161048790611f55565b6001600160a01b038116610bbf5760405162461bcd60e51b815260206004820152602860248201527f41646d696e526f6c653a206e65772061646d696e20697320746865207a65726f604482015267206164647265737360c01b6064820152608401610487565b600080546040516001600160a01b03808516939216917fe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd91a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b33610c2d6000546001600160a01b031690565b6001600160a01b031614610c535760405162461bcd60e51b815260040161048790611f55565b6003805460ff191660019081179091556040519081527f9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304906020016104c7565b33610ca66000546001600160a01b031690565b6001600160a01b031614610ccc5760405162461bcd60e51b815260040161048790611f55565b600080546040516001600160a01b03909116907fe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd908390a3600080546001600160a01b0319169055565b60105460405163085c967f60e01b8152600481018390526060916000916001600160a01b039091169063085c967f90602401600060405180830381865afa158015610d65573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109c391908101906122e6565b600061092b6001836114a6565b33610dad6000546001600160a01b031690565b6001600160a01b031614610dd35760405162461bcd60e51b815260040161048790611f55565b6003811015610e195760405162461bcd60e51b815260206004820152601260248201527128bab7b93ab69034b9903a37b7903637bb9760711b6044820152606401610487565b60058190556040518181527f027863d12a407097e086a48e36475bfc859d0b200b7e6f65b5fd3b218e46632e9060200160405180910390a150565b6000610e5f60015490565b905090565b60008181526012602090815260408083208151815493840281016060908101845292810184815292949384939192918391839083890182828015610ef757602002820191906000526020600020906000905b82829054906101000a900460ff166004811115610ed557610ed5611dfe565b815260206001928301818104948501949093039092029101808411610eb65790505b5050505050815260200160018201548152505090508060000151610f1e8260200151611535565b9250925050915091565b33610f3b6000546001600160a01b031690565b6001600160a01b031614610f615760405162461bcd60e51b815260040161048790611f55565b6105478161155e565b33610f7d6000546001600160a01b031690565b6001600160a01b031614610fa35760405162461bcd60e51b815260040161048790611f55565b60035460ff16610fec5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610487565b601060009054906101000a90046001600160a01b03166001600160a01b031663821461386040518163ffffffff1660e01b8152600401602060405180830381865afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106391906121b9565b156110d65760405162461bcd60e51b815260206004820152603860248201527f43616e6e6f74206368616e6765206261746368536574746c65426c6f636b436f60448201527f756e7420776974682070656e64696e67206261746368657300000000000000006064820152608401610487565b60ff16600455565b6110e9600182611609565b6111435760405162461bcd60e51b815260206004820152602560248201527f52656c61796572526f6c653a2061646472657373206973206e6f7420612072656044820152643630bcb2b960d91b6064820152608401610487565b60405133906001600160a01b038316907f0bdcf1d6f29aa87af8131cc81dcbb295fcf98d71cfcdc79cc5d965317bae1d0a90600090a350565b60006040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a33320000000081525082805190602001206040516020016111cd9291906123f3565b604051602081830303815290604052805190602001209050919050565b60008083516001600160401b0381111561120657611206611ca6565b60405190808252806020026020018201604052801561122f578160200160208202803683370190505b50905060005b845181101561132e57600061126386838151811061125557611255612193565b60200260200101518661174b565b905061126e8161091e565b611278575061131c565b6000805b84518110156112d257826001600160a01b03168582815181106112a1576112a1612193565b60200260200101516001600160a01b0316036112c057600191506112d2565b806112ca816121ea565b91505061127c565b508061131957846112e2816121ea565b955050818484815181106112f8576112f8612193565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50505b80611326816121ea565b915050611235565b506005548210156113765760405162461bcd60e51b8152602060048201526012602482015271145d5bdc9d5b481dd85cc81b9bdd081b595d60721b6044820152606401610487565b50505050565b6001600160a01b038116600090815260018301602052604081205415155b9392505050565b6113a9611831565b6113b161187a565b6113b961188a565b565b6113c3611831565b60038210156114095760405162461bcd60e51b815260206004820152601260248201527128bab7b93ab69034b9903a37b7903637bb9760711b6044820152606401610487565b81835110156114705760405162461bcd60e51b815260206004820152602d60248201527f54686520626f6172642073686f756c64206265206174206c656173742074686560448201526c1038bab7b93ab69039b4bd329760991b6064820152608401610487565b61147983611892565b600591909155601080546001600160a01b0319166001600160a01b03909216919091179055506028600455565b815460009082106115045760405162461bcd60e51b815260206004820152602260248201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604482015261647360f01b6064820152608401610487565b82600001828154811061151957611519612193565b6000918252602090912001546001600160a01b03169392505050565b60008160000361154757506000919050565b43600454836115569190612415565b111592915050565b611567816118d6565b611572600182611941565b6115d05760405162461bcd60e51b815260206004820152602960248201527f52656c61796572526f6c653a206164647265737320697320616c72656164792060448201526830903932b630bcb2b960b91b6064820152608401610487565b60405133906001600160a01b038316907fd756b9aee10d6f2c80dc42c5031beb0e0847f6e1d6ba50199bdfc3f0de5cc0cc90600090a350565b6001600160a01b03811660009081526001830160205260408120548015611741576000611637600183612428565b855490915060009061164b90600190612428565b9050600086600001828154811061166457611664612193565b60009182526020909120015487546001600160a01b039091169150819088908590811061169357611693612193565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556116c7836001612415565b6001600160a01b038216600090815260018901602052604090205586548790806116f3576116f361243b565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b038816825260018981019091526040822091909155945061092b9350505050565b600091505061092b565b600082516041146117945760405162461bcd60e51b81526020600482015260136024820152724d616c666f726d6564207369676e617475726560681b6044820152606401610487565b60208301516040840151606085015160001a8015806117b657508060ff166001145b156117c9576117c6601b82612451565b90505b60408051600081526020810180835287905260ff831691810191909152606081018490526080810183905260019060a0016020604051602081039080840390855afa15801561181c573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166113b957604051631afcd79f60e31b815260040160405180910390fd5b611882611831565b6113b96119b3565b6113b9611831565b60005b81518110156118d2576118c08282815181106118b3576118b3612193565b602002602001015161155e565b806118ca816121ea565b915050611895565b5050565b6001600160a01b0381166105475760405162461bcd60e51b815260206004820152602c60248201527f52656c61796572526f6c653a206163636f756e742063616e6e6f74206265207460448201526b68652030206164647265737360a01b6064820152608401610487565b6001600160a01b03811660009081526001830160205260408120546119ab57508154600180820184556000848152602080822090930180546001600160a01b0319166001600160a01b0386169081179091558554908252828601909352604090209190915561092b565b50600061092b565b6119bb611831565b600080546001600160a01b031916339081178255604051909182917fe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd908290a350565b82805482825590600052602060002090601f01602090048101928215611aa25791602002820160005b83821115611a7357835183826101000a81548160ff02191690836004811115611a5257611a52611dfe565b02179055509260200192600101602081600001049283019260010302611a27565b8015611aa05782816101000a81549060ff0219169055600101602081600001049283019260010302611a73565b505b50611aae929150611ab2565b5090565b5b80821115611aae5760008155600101611ab3565b6020808252825182820181905260009190848201906040850190845b81811015611b085783516001600160a01b031683529284019291840191600101611ae3565b50909695505050505050565b6001600160a01b038116811461054757600080fd5b600060208284031215611b3b57600080fd5b813561139a81611b14565b60008083601f840112611b5857600080fd5b5081356001600160401b03811115611b6f57600080fd5b6020830191508360208260051b8501011115611b8a57600080fd5b9250929050565b600080600080600080600080600080600060c08c8e031215611bb257600080fd5b6001600160401b03808d351115611bc857600080fd5b611bd58e8e358f01611b46565b909c509a5060208d0135811015611beb57600080fd5b611bfb8e60208f01358f01611b46565b909a50985060408d0135811015611c1157600080fd5b611c218e60408f01358f01611b46565b909850965060608d0135811015611c3757600080fd5b611c478e60608f01358f01611b46565b909650945060808d0135935060a08d0135811015611c6457600080fd5b50611c758d60a08e01358e01611b46565b81935080925050509295989b509295989b9093969950565b600060208284031215611c9f57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b60405160c081016001600160401b0381118282101715611cde57611cde611ca6565b60405290565b604051601f8201601f191681016001600160401b0381118282101715611d0c57611d0c611ca6565b604052919050565b60006001600160401b03821115611d2d57611d2d611ca6565b5060051b60200190565b8035611d4281611b14565b919050565b600080600060608486031215611d5c57600080fd5b83356001600160401b03811115611d7257600080fd5b8401601f81018613611d8357600080fd5b80356020611d98611d9383611d14565b611ce4565b82815260059290921b83018101918181019089841115611db757600080fd5b938201935b83851015611dde578435611dcf81611b14565b82529382019390820190611dbc565b9650508601359350611df591505060408501611d37565b90509250925092565b634e487b7160e01b600052602160045260246000fd5b60058110611e3257634e487b7160e01b600052602160045260246000fd5b9052565b6040808252835182820181905260009190606090818501906020808901865b83811015611ec257815180516001600160701b03168652838101516001600160a01b039081168588015288820151898801528782015116878701526080808201519087015260a09081015190611ead81880183611e14565b505060c0949094019390820190600101611e55565b5050829550611ed48188018915159052565b50505050509392505050565b604080825283519082018190526000906020906060840190828701845b82811015611f2057611f10848351611e14565b9284019290840190600101611efd565b50505093151592019190915250919050565b600060208284031215611f4457600080fd5b813560ff8116811461139a57600080fd5b60208082526023908201527f41636365737320436f6e74726f6c3a2073656e646572206973206e6f7420416460408201526236b4b760e91b606082015260800190565b6000611fa6611d9384611d14565b80848252602080830192508560051b850136811115611fc457600080fd5b855b818110156120565780356001600160401b0380821115611fe65760008081fd5b90880190601f3681840112611ffb5760008081fd5b82358281111561200d5761200d611ca6565b61201e818301601f19168801611ce4565b9250808352368782860101111561203757600091508182fd5b8087850188850137600090830187015250865250938201938201611fc6565b50919695505050505050565b8183526000602080850194508260005b858110156120a057813561208581611b14565b6001600160a01b031687529582019590820190600101612072565b509495945050505050565b81835260006001600160fb1b038311156120c457600080fd5b8260051b80836020870137939093016020019392505050565b60005b838110156120f85781810151838201526020016120e0565b50506000910152565b60c08152600061211560c083018c8e612062565b8281036020840152612128818b8d612062565b9050828103604084015261213d81898b6120ab565b905082810360608401526121528187896120ab565b905084608084015282810360a084015283518082526121788160208401602088016120dd565b601f01601f1916016020019c9b505050505050505050505050565b634e487b7160e01b600052603260045260246000fd5b80518015158114611d4257600080fd5b6000602082840312156121cb57600080fd5b61139a826121a9565b634e487b7160e01b600052601160045260246000fd5b6000600182016121fc576121fc6121d4565b5060010190565b80516001600160701b0381168114611d4257600080fd5b80516001600160401b0381168114611d4257600080fd5b60008082840360a081121561224557600080fd5b608081121561225357600080fd5b50604051608081018181106001600160401b038211171561227657612276611ca6565b60405261228284612203565b81526122906020850161221a565b60208201526122a16040850161221a565b6040820152606084015161ffff811681146122bb57600080fd5b606082015291506122ce608084016121a9565b90509250929050565b805160058110611d4257600080fd5b60008060408084860312156122fa57600080fd5b83516001600160401b0381111561231057600080fd5b8401601f8101861361232157600080fd5b80516020612331611d9383611d14565b82815260c0928302840182019282820191908a85111561235057600080fd5b948301945b848610156123d65780868c03121561236d5760008081fd5b612375611cbc565b61237e87612203565b81528487015161238d81611b14565b8186015286880151888201526060808801516123a881611b14565b908201526080878101519082015260a06123c38189016122d7565b9082015283529485019491830191612355565b5096506123e690508782016121a9565b9450505050509250929050565b600083516124058184602088016120dd565b9190910191825250602001919050565b8082018082111561092b5761092b6121d4565b8181038181111561092b5761092b6121d4565b634e487b7160e01b600052603160045260246000fd5b60ff818116838216019081111561092b5761092b6121d456fea164736f6c6343000814000a diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/ERC20Safe.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/eth/ERC20Safe.abi.json new file mode 100755 index 00000000..0d59d245 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/ERC20Safe.abi.json @@ -0,0 +1,1006 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "AddressInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "SafeERC20FailedOperation", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminRoleTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousBridge", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newBridge", + "type": "address" + } + ], + "name": "BridgeTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "batchId", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "depositNonce", + "type": "uint112" + } + ], + "name": "ERC20Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint112", + "name": "batchId", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "depositNonce", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "name": "ERC20SCDeposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "isPause", + "type": "bool" + } + ], + "name": "Pause", + "type": "event" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchBlockLimit", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "batchDeposits", + "outputs": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSettleLimit", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchSize", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "batches", + "outputs": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "batchesCount", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bridge", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "burnBalances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "recipientAddress", + "type": "bytes32" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "recipientAddress", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "callData", + "type": "bytes" + } + ], + "name": "depositWithSCExecution", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositsCount", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getBatch", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "uint64", + "name": "blockNumber", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "lastUpdatedBlockNumber", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "depositsCount", + "type": "uint16" + } + ], + "internalType": "struct Batch", + "name": "", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "isBatchFinal", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "batchNonce", + "type": "uint256" + } + ], + "name": "getDeposits", + "outputs": [ + { + "components": [ + { + "internalType": "uint112", + "name": "nonce", + "type": "uint112" + }, + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "recipient", + "type": "bytes32" + }, + { + "internalType": "enum DepositStatus", + "name": "status", + "type": "uint8" + } + ], + "internalType": "struct Deposit[]", + "name": "", + "type": "tuple[]" + }, + { + "internalType": "bool", + "name": "areDepositsFinal", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenMaxLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "getTokenMinLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "initSupply", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "mintAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "burnAmount", + "type": "uint256" + } + ], + "name": "initSupplyMintBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "isAnyBatchInProgress", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "isTokenWhitelisted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "mintBalances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "mintBurnTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nativeTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "recoverLostFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "removeTokenFromWhitelist", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + } + ], + "name": "resetTotalBalance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchBlockLimit", + "type": "uint8" + } + ], + "name": "setBatchBlockLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "newBatchSettleLimit", + "type": "uint8" + } + ], + "name": "setBatchSettleLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "newBatchSize", + "type": "uint16" + } + ], + "name": "setBatchSize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newBridge", + "type": "address" + } + ], + "name": "setBridge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "setTokenMaxLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "setTokenMinLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMaxLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "tokenMinLimits", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "totalBalances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipientAddress", + "type": "address" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "transferAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "minimumAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximumAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "mintBurn", + "type": "bool" + }, + { + "internalType": "bool", + "name": "native", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "totalBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "mintBalance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "burnBalance", + "type": "uint256" + } + ], + "name": "whitelistToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "whitelistedTokens", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/ERC20Safe.hex b/integrationTests/relayers/slowTests/testdata/contracts/eth/ERC20Safe.hex new file mode 100755 index 00000000..f2de58fc --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/ERC20Safe.hex @@ -0,0 +1 @@ +0x608060405234801561001057600080fd5b50613099806100206000396000f3fe608060405234801561001057600080fd5b50600436106102745760003560e01c80639ab7cfaa11610151578063d2763186116100c3578063e8a70ee211610087578063e8a70ee214610717578063e9935b4a1461072a578063f2e0ec481461073d578063f4daaba114610750578063f6246ea114610778578063f851a4401461079857600080fd5b8063d276318614610696578063d4673de9146106a9578063daf9c210146106bc578063dbba0f01146106df578063e78cea92146106f257600080fd5b8063bc56602f11610115578063bc56602f146105d7578063c639651d146105f7578063c652a0b514610617578063c859b3fe14610640578063c86726f614610653578063cf6682a21461067657600080fd5b80639ab7cfaa146104b15780639f0ebb93146104c5578063aee9c872146104fc578063b32c4d8d1461051c578063b5af090f146105ab57600080fd5b806375829def116101ea5780638456cb59116101ae5780638456cb591461043e57806387ea0961146104465780638bad0c0a146104605780638dd148021461046857806390e0cfcb1461047b578063920b03081461049e57600080fd5b806375829def146103f5578063770be784146104085780637d7763ce1461041b5780638129fc1c1461042e578063821461381461043657600080fd5b80633f4ba83a1161023c5780633f4ba83a146103165780634013c89c1461031e57806341220439146103315780634506e935146103445780635ac442821461036f5780635c975abb146103d757600080fd5b8063085c967f146102795780632325b5f7146102a357806326b3293f146102c9578063284c0c44146102de578063306275be14610303575b600080fd5b61028c610287366004612ae1565b6107a9565b60405161029a929190612b32565b60405180910390f35b6002546102b790600160581b900460ff1681565b60405160ff909116815260200161029a565b6102dc6102d7366004612bf8565b61092c565b005b6102f16102ec366004612c2b565b6109dd565b60405161029a96959493929190612c4d565b6102dc610311366004612c9a565b610a4a565b6102dc610aa4565b6102dc61032c366004612cb5565b610b21565b6102dc61033f366004612ced565b610c7a565b600254610357906001600160401b031681565b6040516001600160401b03909116815260200161029a565b61038261037d366004612ae1565b610e82565b6040805183516001600160701b031681526020808501516001600160401b039081169183019190915284830151169181019190915260609283015161ffff16928101929092521515608082015260a00161029a565b600154600160a01b900460ff165b604051901515815260200161029a565b6102dc610403366004612c9a565b610f2f565b6102dc610416366004612c9a565b61102a565b6102dc610429366004612cb5565b61113c565b6102dc611191565b6103e56112a9565b6102dc611389565b60015461035790600160a81b90046001600160401b031681565b6102dc611405565b6102dc610476366004612c9a565b611488565b6103e5610489366004612c9a565b600f6020526000908152604090205460ff1681565b6102dc6104ac366004612cb5565b61164d565b6002546102b790600160501b900460ff1681565b6104ee6104d3366004612c9a565b6001600160a01b031660009081526011602052604090205490565b60405190815260200161029a565b6104ee61050a366004612c9a565b60136020526000908152604090205481565b61057061052a366004612ae1565b600d602052600090815260409020546001600160701b038116906001600160401b03600160701b8204811691600160b01b81049091169061ffff600160f01b9091041684565b604080516001600160701b039590951685526001600160401b039384166020860152919092169083015261ffff16606082015260800161029a565b6103e56105b9366004612c9a565b6001600160a01b03166000908152600e602052604090205460ff1690565b6104ee6105e5366004612c9a565b60146020526000908152604090205481565b6104ee610605366004612c9a565b60126020526000908152604090205481565b6104ee610625366004612c9a565b6001600160a01b031660009081526012602052604090205490565b6102dc61064e366004612d63565b6116a2565b6103e5610661366004612c9a565b60106020526000908152604090205460ff1681565b6104ee610684366004612c9a565b60156020526000908152604090205481565b6102dc6106a4366004612c9a565b611752565b6102dc6106b7366004612df6565b6118a7565b6103e56106ca366004612c9a565b600e6020526000908152604090205460ff1681565b6103e56106ed366004612e1a565b611952565b6001546001600160a01b03165b6040516001600160a01b03909116815260200161029a565b6102dc610725366004612e56565b611b3c565b6102dc610738366004612bf8565b611c1b565b6102dc61074b366004612e56565b611d21565b60025461076590600160401b900461ffff1681565b60405161ffff909116815260200161029a565b6104ee610786366004612c9a565b60116020526000908152604090205481565b6000546001600160a01b03166106ff565b6060600080600d816107bc600187612e8f565b815260208082019290925260409081016000908120825160808101845290546001600160701b0381168252600160701b81046001600160401b0390811695830195909552600160b01b810490941692810192909252600160f01b90920461ffff1660608201529150601690610832600187612e8f565b815260200190815260200160002061084982611ecf565b81805480602002602001604051908101604052809291908181526020016000905b8282101561091c5760008481526020908190206040805160c0810182526006860290920180546001600160701b0316835260018101546001600160a01b0390811694840194909452600281015491830191909152600381015490921660608201526004808301546080830152600583015491929160a084019160ff909116908111156108f8576108f8612afa565b600481111561090957610909612afa565b815250508152602001906001019061086a565b5050505091509250925050915091565b600154600160a01b900460ff161561097e5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064015b60405180910390fd5b60008061098c858585611f00565b604080516001600160701b038084168252841660208201529294509092507f6c15ce44793c685a79cde26a0bd5419ef4f3a337991f156be7b365962001b4a791015b60405180910390a15050505050565b601660205281600052604060002081815481106109f957600080fd5b60009182526020909120600690910201805460018201546002830154600384015460048501546005909501546001600160701b0390941696506001600160a01b039283169550909391169160ff1686565b33610a5d6000546001600160a01b031690565b6001600160a01b031614610a835760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b03166000908152600e60205260409020805460ff19169055565b33610ab76000546001600160a01b031690565b6001600160a01b031614610add5760405162461bcd60e51b815260040161097590612ea2565b6001805460ff60a01b19169055604051600081527f9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f459304906020015b60405180910390a1565b33610b346000546001600160a01b031690565b6001600160a01b031614610b5a5760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b0382166000908152600e602052604090205460ff16610b925760405162461bcd60e51b815260040161097590612ee5565b610b9b826124fc565b15610bf95760405162461bcd60e51b815260206004820152602860248201527f43616e6e6f7420696e697420666f72206d696e7461626c652f6275726e61626c6044820152676520746f6b656e7360c01b6064820152608401610975565b6001600160a01b03821660009081526010602052604090205460ff16610c315760405162461bcd60e51b815260040161097590612f10565b6001600160a01b03821660009081526013602052604081208054839290610c59908490612f51565b90915550829050610c756001600160a01b03821633308561251a565b505050565b33610c8d6000546001600160a01b031690565b6001600160a01b031614610cb35760405162461bcd60e51b815260040161097590612ea2565b84610cd55783610cd55760405162461bcd60e51b815260040161097590612f10565b6001600160a01b0388166000908152600e602090815260408083208054600160ff1991821617909155600f8352818420805482168a1580159190911790915560108452828520805490921689151517909155601183528184208b90556012909252909120879055610db2578215610da25760405162461bcd60e51b815260206004820152602b60248201527f4d696e742d6275726e20746f6b656e73206d7573742068617665203020746f7460448201526a616c2062616c616e63652160a81b6064820152608401610975565b610dad888383611c1b565b610e78565b8115610e105760405162461bcd60e51b815260206004820152602760248201527f53746f72656420746f6b656e73206d75737420686176652030206d696e742062604482015266616c616e63652160c81b6064820152608401610975565b8015610e6e5760405162461bcd60e51b815260206004820152602760248201527f53746f72656420746f6b656e73206d75737420686176652030206275726e2062604482015266616c616e63652160c81b6064820152608401610975565b610e788884610b21565b5050505050505050565b60408051608081018252600080825260208201819052918101829052606081018290529080600d81610eb5600187612e8f565b81526020808201929092526040908101600020815160808101835290546001600160701b0381168252600160701b81046001600160401b0390811694830194909452600160b01b810490931691810191909152600160f01b90910461ffff166060820152905080610f2581611ecf565b9250925050915091565b33610f426000546001600160a01b031690565b6001600160a01b031614610f685760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b038116610fcf5760405162461bcd60e51b815260206004820152602860248201527f41646d696e526f6c653a206e65772061646d696e20697320746865207a65726f604482015267206164647265737360c01b6064820152608401610975565b600080546040516001600160a01b03808516939216917fe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd91a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b3361103d6000546001600160a01b031690565b6001600160a01b0316146110635760405162461bcd60e51b815260040161097590612ea2565b6040516370a0823160e01b815230600482015281906000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156110ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d09190612f64565b6001600160a01b0384166000908152600e60205260408120549192509060ff161561111f576001600160a01b0384166000908152601360205260409020546111189083612e8f565b9050611122565b50805b6111366001600160a01b0384163383612581565b50505050565b3361114f6000546001600160a01b031690565b6001600160a01b0316146111755760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b03909116600090815260126020526040902055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03166000811580156111d65750825b90506000826001600160401b031660011480156111f25750303b155b905081158015611200575080155b1561121e5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561124857845460ff60401b1916600160401b1785555b6112506125b2565b6112586125cc565b6112606125e4565b83156112a257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2906020016109ce565b5050505050565b600154600090600160a81b90046001600160401b031681036112cb5750600090565b60018054600091600d9183916112f091600160a81b90046001600160401b0316612f7d565b6001600160401b0390811682526020808301939093526040918201600020825160808101845290546001600160701b0381168252600160701b8104831694820194909452600160b01b840490911691810191909152600160f01b90910461ffff1660608201529050611360612611565b61136c57600191505090565b61137581611ecf565b61138157600191505090565b600091505090565b3361139c6000546001600160a01b031690565b6001600160a01b0316146113c25760405162461bcd60e51b815260040161097590612ea2565b6001805460ff60a01b1916600160a01b1781556040519081527f9422424b175dda897495a07b091ef74a3ef715cf6d866fc972954c1c7f45930490602001610b17565b336114186000546001600160a01b031690565b6001600160a01b03161461143e5760405162461bcd60e51b815260040161097590612ea2565b600080546040516001600160a01b03909116907fe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd908390a3600080546001600160a01b0319169055565b3361149b6000546001600160a01b031690565b6001600160a01b0316146114c15760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b03811661152a5760405162461bcd60e51b815260206004820152602a60248201527f427269646765526f6c653a206e65772062726964676520697320746865207a65604482015269726f206164647265737360b01b6064820152608401610975565b6001546001600160a01b03908116908216036115885760405162461bcd60e51b815260206004820152601860248201527f427269646765526f6c653a2073616d65206164647265737300000000000000006044820152606401610975565b6001600160a01b0381163b6115f15760405162461bcd60e51b815260206004820152602960248201527f427269646765526f6c653a206e657720627269646765206d75737420626520616044820152680818dbdb9d1c9858dd60ba1b6064820152608401610975565b6001546040516001600160a01b038084169216907fcca5fddab921a878ddbd4edb737a2cf3ac6df70864f108606647d1b37a5e07a090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b336116606000546001600160a01b031690565b6001600160a01b0316146116865760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b03909116600090815260116020526040902055565b600154600160a01b900460ff16156116ef5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610975565b6000806116fd878787611f00565b60405191935091506001600160701b038316907fce848da21487607afba5c5a500c2ad1002d9b8db97ca5512671309df071113b29061174190849088908890612fa4565b60405180910390a250505050505050565b336117656000546001600160a01b031690565b6001600160a01b03161461178b5760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b0381166000908152600e602052604090205460ff166117c35760405162461bcd60e51b815260040161097590612ee5565b6117cc816124fc565b156118195760405162461bcd60e51b815260206004820152601a60248201527f546f6b656e206973206d696e7461626c652f6275726e61626c650000000000006044820152606401610975565b6040516370a0823160e01b815230600482015281906000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611862573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118869190612f64565b6001600160a01b039093166000908152601360205260409020929092555050565b336118ba6000546001600160a01b031690565b6001600160a01b0316146118e05760405162461bcd60e51b815260040161097590612ea2565b606461ffff8216111561192b5760405162461bcd60e51b8152602060048201526013602482015272084c2e8c6d040e6d2f4ca40e8dede40d0d2ced606b1b6044820152606401610975565b6002805461ffff909216600160401b0269ffff000000000000000019909216919091179055565b6000336119676001546001600160a01b031690565b6001600160a01b0316146119c95760405162461bcd60e51b8152602060048201526024808201527f41636365737320436f6e74726f6c3a2073656e646572206973206e6f742042726044820152636964676560e01b6064820152608401610975565b6119d2846124fc565b611a38578360006119ed6001600160a01b03831685876126f0565b9050806119ff57600092505050611b35565b6001600160a01b03861660009081526013602052604081208054879290611a27908490612e8f565b9091555060019350611b3592505050565b6001600160a01b03841660009081526010602052604090205460ff1615611ae4576001600160a01b038416600090815260146020526040902054611a7d908490612f51565b6001600160a01b0385166000908152601560205260409020541015611ae45760405162461bcd60e51b815260206004820152601860248201527f4e6f7420656e6f756768206275726e656420746f6b656e7300000000000000006044820152606401610975565b6000611af18584866127d7565b905080611b02576000915050611b35565b6001600160a01b03851660009081526014602052604081208054869290611b2a908490612f51565b909155506001925050505b9392505050565b33611b4f6000546001600160a01b031690565b6001600160a01b031614611b755760405162461bcd60e51b815260040161097590612ea2565b60025460ff600160581b90910481169082161115611bfb5760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f7420696e63726561736520626174636820626c6f636b206c696d6960448201527f74206f76657220736574746c656d656e74206c696d69740000000000000000006064820152608401610975565b6002805460ff909216600160501b0260ff60501b19909216919091179055565b33611c2e6000546001600160a01b031690565b6001600160a01b031614611c545760405162461bcd60e51b815260040161097590612ea2565b6001600160a01b0383166000908152600e602052604090205460ff16611c8c5760405162461bcd60e51b815260040161097590612ee5565b611c95836124fc565b611cf65760405162461bcd60e51b815260206004820152602c60248201527f43616e6e6f7420696e697420666f72206e6f6e206d696e7461626c652f62757260448201526b6e61626c6520746f6b656e7360a01b6064820152608401610975565b6001600160a01b03909216600090815260156020908152604080832094909455601490529190912055565b33611d346000546001600160a01b031690565b6001600160a01b031614611d5a5760405162461bcd60e51b815260040161097590612ea2565b600154600160a01b900460ff16611daa5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610975565b611db26112a9565b15611e1b5760405162461bcd60e51b815260206004820152603360248201527f43616e6e6f74206368616e6765206261746368536574746c654c696d697420776044820152726974682070656e64696e67206261746368657360681b6064820152608401610975565b60025460ff600160501b90910481169082161015611eaf5760405162461bcd60e51b815260206004820152604560248201527f43616e6e6f74206465637265617365206261746368536574746c654c696d697460448201527f20756e646572207468652076616c7565206f6620626174636820626c6f636b206064820152641b1a5b5a5d60da1b608482015260a401610975565b6002805460ff909216600160581b0260ff60581b19909216919091179055565b60025460408201516000914391611ef091600160581b900460ff1690612fe3565b6001600160401b03161092915050565b6001600160a01b0383166000908152600e6020526040812054819060ff16611f3a5760405162461bcd60e51b815260040161097590612ee5565b6001600160a01b038516600090815260116020526040902054841015611fc85760405162461bcd60e51b815260206004820152603c60248201527f547269656420746f206465706f73697420616e20616d6f756e742062656c6f7760448201527f20746865206d696e696d756d20737065636966696564206c696d6974000000006064820152608401610975565b6001600160a01b0385166000908152601260205260409020548411156120565760405162461bcd60e51b815260206004820152603c60248201527f547269656420746f206465706f73697420616e20616d6f756e742061626f766560448201527f20746865206d6178696d756d20737065636966696564206c696d6974000000006064820152608401610975565b436000612061612611565b156120fe575060018054600160a81b90046001600160401b03166000818152600d60205260409020916120949190612fe3565b81546001600160401b039182166001600160b01b031990911617600160701b8483160217825560018054600160a81b90049091169060156120d483613003565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505061213c565b60018054600d916000916121229190600160a81b90046001600160401b0316612f7d565b6001600160401b0316815260200190815260200160002090505b600254600090612156906001600160401b03166001612fe3565b6001600160401b031690506016600060018060159054906101000a90046001600160401b03166121869190612f7d565b6001600160401b031681526020019081526020016000206040518060c00160405280836001600160701b031681526020018a6001600160a01b03168152602001898152602001336001600160a01b03168152602001888152602001600160048111156121f4576121f4612afa565b90528154600181810184556000938452602093849020835160069093020180546001600160701b039093166dffffffffffffffffffffffffffff199093169290921782559282015181840180546001600160a01b039283166001600160a01b031991821617909155604084015160028401556060840151600384018054919093169116179055608082015160048083019190915560a0830151600583018054949593949193909260ff199092169184908111156122b3576122b3612afa565b021790555050825467ffffffffffffffff60b01b1916600160b01b6001600160401b0386160217808455600160f01b900461ffff16905082601e6122f683613029565b825461ffff9182166101009390930a928302919092021990911617905550600280546001600160401b031690600061232d83613003565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505061235b886124fc565b6123a9576001600160a01b03881660009081526013602052604081208054899290612387908490612f51565b909155508890506123a36001600160a01b03821633308b61251a565b506124e5565b6001600160a01b03881660009081526010602052604090205460ff16612454576001600160a01b0388166000908152601560205260409020546123ed908890612f51565b6001600160a01b03891660009081526014602052604090205410156124545760405162461bcd60e51b815260206004820152601860248201527f4e6f7420656e6f756768206d696e74656420746f6b656e7300000000000000006044820152606401610975565b6001600160a01b0388166000908152601560205260408120805489929061247c908490612f51565b909155505060405163079cc67960e41b81523360048201526024810188905288906001600160a01b038216906379cc679090604401600060405180830381600087803b1580156124cb57600080fd5b505af11580156124df573d6000803e3d6000fd5b50505050505b90546001600160701b031697909650945050505050565b6001600160a01b03166000908152600f602052604090205460ff1690565b6040516001600160a01b0384811660248301528381166044830152606482018390526111369186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b03838183161783525050505061284f565b6040516001600160a01b03838116602483015260448201839052610c7591859182169063a9059cbb9060640161254f565b6125ba6128b2565b6125c26128fb565b6125ca61290b565b565b6125d46128b2565b6125dc6128fb565b6125ca612913565b6125ec6128b2565b600280546bffffffff000000000000000019166b2828000a0000000000000000179055565b600154600090600160a81b90046001600160401b031681036126335750600190565b60018054600091600d91839161265891600160a81b90046001600160401b0316612f7d565b6001600160401b0390811682526020808301939093526040918201600020825160808101845290546001600160701b0381168252600160701b81048316948201859052600160b01b810490921692810192909252600160f01b900461ffff16606082018190529092506126ca91612930565b806126ea5750600254606082015161ffff600160401b9092048216911610155b91505090565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839283929188169161274e9190613040565b6000604051808303816000865af19150503d806000811461278b576040519150601f19603f3d011682016040523d82523d6000602084013e612790565b606091505b5091509150816127a557600092505050611b35565b80516000036127b957600192505050611b35565b808060200190518101906127cd919061306f565b9695505050505050565b6040516340c10f1960e01b81526001600160a01b0383811660048301526024820183905260009185918216906340c10f1990604401600060405180830381600087803b15801561282657600080fd5b505af1925050508015612837575060015b612845576000915050611b35565b6001915050611b35565b60006128646001600160a01b03841683612972565b90508051600014158015612889575080806020019051810190612887919061306f565b155b15610c7557604051635274afe760e01b81526001600160a01b0384166004820152602401610975565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166125ca57604051631afcd79f60e31b815260040160405180910390fd5b6129036128b2565b6125ca612980565b6125ca6128b2565b61291b6128b2565b6001805460ff60a01b1916600160a01b179055565b60008261ffff166000036129465750600061296c565b600254439061295f90600160501b900460ff1684612fe3565b6001600160401b03161090505b92915050565b6060611b35838360006129cb565b6129886128b2565b600080546001600160a01b031916339081178255604051909182917fe379ac64de02d8184ca1a871ac486cb8137de77e485ede140e97057b9c765ffd908290a350565b6060814710156129f05760405163cd78605960e01b8152306004820152602401610975565b600080856001600160a01b03168486604051612a0c9190613040565b60006040518083038185875af1925050503d8060008114612a49576040519150601f19603f3d011682016040523d82523d6000602084013e612a4e565b606091505b50915091506127cd868383606082612a6e57612a6982612ab5565b611b35565b8151158015612a8557506001600160a01b0384163b155b15612aae57604051639996b31560e01b81526001600160a01b0385166004820152602401610975565b5080611b35565b805115612ac55780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50565b600060208284031215612af357600080fd5b5035919050565b634e487b7160e01b600052602160045260246000fd5b60058110612b2e57634e487b7160e01b600052602160045260246000fd5b9052565b6040808252835182820181905260009190606090818501906020808901865b83811015612bbe57815180516001600160701b03168652838101516001600160a01b039081168588015288820151898801528782015116878701526080808201519087015260a09081015190612ba981880183612b10565b505060c0949094019390820190600101612b51565b5050829550612bd08188018915159052565b50505050509392505050565b80356001600160a01b0381168114612bf357600080fd5b919050565b600080600060608486031215612c0d57600080fd5b612c1684612bdc565b95602085013595506040909401359392505050565b60008060408385031215612c3e57600080fd5b50508035926020909101359150565b6001600160701b03871681526001600160a01b03868116602083015260408201869052841660608201526080810183905260c08101612c8f60a0830184612b10565b979650505050505050565b600060208284031215612cac57600080fd5b611b3582612bdc565b60008060408385031215612cc857600080fd5b612cd183612bdc565b946020939093013593505050565b8015158114612ade57600080fd5b600080600080600080600080610100898b031215612d0a57600080fd5b612d1389612bdc565b975060208901359650604089013595506060890135612d3181612cdf565b94506080890135612d4181612cdf565b979a969950949793969560a0850135955060c08501359460e001359350915050565b600080600080600060808688031215612d7b57600080fd5b612d8486612bdc565b9450602086013593506040860135925060608601356001600160401b0380821115612dae57600080fd5b818801915088601f830112612dc257600080fd5b813581811115612dd157600080fd5b896020828501011115612de357600080fd5b9699959850939650602001949392505050565b600060208284031215612e0857600080fd5b813561ffff81168114611b3557600080fd5b600080600060608486031215612e2f57600080fd5b612e3884612bdc565b925060208401359150612e4d60408501612bdc565b90509250925092565b600060208284031215612e6857600080fd5b813560ff81168114611b3557600080fd5b634e487b7160e01b600052601160045260246000fd5b8181038181111561296c5761296c612e79565b60208082526023908201527f41636365737320436f6e74726f6c3a2073656e646572206973206e6f7420416460408201526236b4b760e91b606082015260800190565b6020808252601190820152702ab739bab83837b93a32b2103a37b5b2b760791b604082015260600190565b60208082526021908201527f4f6e6c79206e617469766520746f6b656e732063616e2062652073746f7265646040820152602160f81b606082015260800190565b8082018082111561296c5761296c612e79565b600060208284031215612f7657600080fd5b5051919050565b6001600160401b03828116828216039080821115612f9d57612f9d612e79565b5092915050565b6001600160701b038416815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b6001600160401b03818116838216019080821115612f9d57612f9d612e79565b60006001600160401b0380831681810361301f5761301f612e79565b6001019392505050565b600061ffff80831681810361301f5761301f612e79565b6000825160005b818110156130615760208186018101518583015201613047565b506000920191825250919050565b60006020828403121561308157600080fd5b8151611b3581612cdf56fea164736f6c6343000814000a diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/GenericERC20.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/eth/GenericERC20.abi.json new file mode 100755 index 00000000..5bbc1c07 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/GenericERC20.abi.json @@ -0,0 +1,349 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "tokenName", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "providedNumDecimals", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipientAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/GenericERC20.hex b/integrationTests/relayers/slowTests/testdata/contracts/eth/GenericERC20.hex new file mode 100755 index 00000000..48c2d280 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/GenericERC20.hex @@ -0,0 +1 @@ +0x60806040523480156200001157600080fd5b5060405162000a8a38038062000a8a833981016040819052620000349162000139565b828260036200004483826200024d565b5060046200005382826200024d565b50506005805460ff191660ff93909316929092179091555062000319915050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200009c57600080fd5b81516001600160401b0380821115620000b957620000b962000074565b604051601f8301601f19908116603f01168101908282118183101715620000e457620000e462000074565b816040528381526020925086838588010111156200010157600080fd5b600091505b8382101562000125578582018301518183018401529082019062000106565b600093810190920192909252949350505050565b6000806000606084860312156200014f57600080fd5b83516001600160401b03808211156200016757600080fd5b62000175878388016200008a565b945060208601519150808211156200018c57600080fd5b506200019b868287016200008a565b925050604084015160ff81168114620001b357600080fd5b809150509250925092565b600181811c90821680620001d357607f821691505b602082108103620001f457634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200024857600081815260208120601f850160051c81016020861015620002235750805b601f850160051c820191505b8181101562000244578281556001016200022f565b5050505b505050565b81516001600160401b0381111562000269576200026962000074565b62000281816200027a8454620001be565b84620001fa565b602080601f831160018114620002b95760008415620002a05750858301515b600019600386901b1c1916600185901b17855562000244565b600085815260208120601f198616915b82811015620002ea57888601518255948401946001909101908401620002c9565b5085821015620003095787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61076180620003296000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c806340c10f191161006657806340c10f191461011e57806370a082311461013357806395d89b411461015c578063a9059cbb14610164578063dd62ed3e1461017757600080fd5b806306fdde03146100a3578063095ea7b3146100c157806318160ddd146100e457806323b872dd146100f6578063313ce56714610109575b600080fd5b6100ab6101b0565b6040516100b891906105d4565b60405180910390f35b6100d46100cf36600461063e565b610242565b60405190151581526020016100b8565b6002545b6040519081526020016100b8565b6100d4610104366004610668565b61025c565b60055460405160ff90911681526020016100b8565b61013161012c36600461063e565b610280565b005b6100e86101413660046106a4565b6001600160a01b031660009081526020819052604090205490565b6100ab61028e565b6100d461017236600461063e565b61029d565b6100e86101853660046106c6565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101bf906106f9565b80601f01602080910402602001604051908101604052809291908181526020018280546101eb906106f9565b80156102385780601f1061020d57610100808354040283529160200191610238565b820191906000526020600020905b81548152906001019060200180831161021b57829003601f168201915b5050505050905090565b6000336102508185856102ab565b60019150505b92915050565b60003361026a8582856102bd565b610275858585610340565b506001949350505050565b61028a828261039f565b5050565b6060600480546101bf906106f9565b600033610250818585610340565b6102b883838360016103d5565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461033a578181101561032b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61033a848484840360006103d5565b50505050565b6001600160a01b03831661036a57604051634b637e8f60e11b815260006004820152602401610322565b6001600160a01b0382166103945760405163ec442f0560e01b815260006004820152602401610322565b6102b88383836104aa565b6001600160a01b0382166103c95760405163ec442f0560e01b815260006004820152602401610322565b61028a600083836104aa565b6001600160a01b0384166103ff5760405163e602df0560e01b815260006004820152602401610322565b6001600160a01b03831661042957604051634a1406b160e11b815260006004820152602401610322565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561033a57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161049c91815260200190565b60405180910390a350505050565b6001600160a01b0383166104d55780600260008282546104ca9190610733565b909155506105479050565b6001600160a01b038316600090815260208190526040902054818110156105285760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610322565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b03821661056357600280548290039055610582565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516105c791815260200190565b60405180910390a3505050565b600060208083528351808285015260005b81811015610601578581018301518582016040015282016105e5565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461063957600080fd5b919050565b6000806040838503121561065157600080fd5b61065a83610622565b946020939093013593505050565b60008060006060848603121561067d57600080fd5b61068684610622565b925061069460208501610622565b9150604084013590509250925092565b6000602082840312156106b657600080fd5b6106bf82610622565b9392505050565b600080604083850312156106d957600080fd5b6106e283610622565b91506106f060208401610622565b90509250929050565b600181811c9082168061070d57607f821691505b60208210810361072d57634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561025657634e487b7160e01b600052601160045260246000fdfea164736f6c6343000814000a diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/MintBurnERC20.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/eth/MintBurnERC20.abi.json new file mode 100755 index 00000000..fa6b3691 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/MintBurnERC20.abi.json @@ -0,0 +1,654 @@ +[ + { + "inputs": [], + "name": "AccessControlBadConfirmation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "neededRole", + "type": "bytes32" + } + ], + "name": "AccessControlUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "CallerNotMinter", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINTER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "burnFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "uint8", + "name": "providedNumDecimals", + "type": "uint8" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callerConfirmation", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/MintBurnERC20.hex b/integrationTests/relayers/slowTests/testdata/contracts/eth/MintBurnERC20.hex new file mode 100755 index 00000000..ed784ab8 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/MintBurnERC20.hex @@ -0,0 +1 @@ +0x608060405234801561001057600080fd5b50611167806100206000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c806340c10f19116100b857806395d89b411161007c57806395d89b41146102b5578063a217fddf146102bd578063a9059cbb146102c5578063d5391393146102d8578063d547741f146102ff578063dd62ed3e1461031257600080fd5b806340c10f191461023357806342966c681461024657806370a082311461025957806379cc67901461028f57806391d14854146102a257600080fd5b806323b872dd116100ff57806323b872dd146101d2578063248a9ca3146101e55780632f2ff15d146101f8578063313ce5671461020b57806336568abe1461022057600080fd5b806301ffc9a71461013c57806306fdde0314610164578063095ea7b3146101795780631624f6c61461018c57806318160ddd146101a1575b600080fd5b61014f61014a366004610d05565b610325565b60405190151581526020015b60405180910390f35b61016c61035c565b60405161015b9190610d36565b61014f610187366004610da0565b61041f565b61019f61019a366004610e6d565b610437565b005b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02545b60405190815260200161015b565b61014f6101e0366004610eeb565b610563565b6101c46101f3366004610f27565b610587565b61019f610206366004610f40565b6105a9565b60005460405160ff909116815260200161015b565b61019f61022e366004610f40565b6105cb565b61019f610241366004610da0565b610603565b61019f610254366004610f27565b61065f565b6101c4610267366004610f6c565b6001600160a01b0316600090815260008051602061111b833981519152602052604090205490565b61019f61029d366004610da0565b61066c565b61014f6102b0366004610f40565b610681565b61016c6106b9565b6101c4600081565b61014f6102d3366004610da0565b6106f8565b6101c47f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b61019f61030d366004610f40565b610706565b6101c4610320366004610f87565b610722565b60006001600160e01b03198216637965db0b60e01b148061035657506301ffc9a760e01b6001600160e01b03198316145b92915050565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03805460609160008051602061111b8339815191529161039b90610fb1565b80601f01602080910402602001604051908101604052809291908181526020018280546103c790610fb1565b80156104145780601f106103e957610100808354040283529160200191610414565b820191906000526020600020905b8154815290600101906020018083116103f757829003601f168201915b505050505091505090565b60003361042d81858561076c565b5060019392505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff1660008115801561047d5750825b905060008267ffffffffffffffff16600114801561049a5750303b155b9050811580156104a8575080155b156104c65760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156104f057845460ff60401b1916600160401b1785555b6104fa8888610779565b61050261078b565b61050a61078b565b61051386610795565b831561055957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b6000336105718582856107b6565b61057c858585610816565b506001949350505050565b600090815260008051602061113b833981519152602052604090206001015490565b6105b282610587565b6105bb81610875565b6105c5838361087f565b50505050565b6001600160a01b03811633146105f45760405163334bd91960e11b815260040160405180910390fd5b6105fe8282610924565b505050565b61062d7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a633610681565b61065157604051632fdab94f60e11b81523360048201526024015b60405180910390fd5b61065b82826109a0565b5050565b61066933826109d6565b50565b6106778233836107b6565b61065b82826109d6565b600091825260008051602061113b833981519152602090815260408084206001600160a01b0393909316845291905290205460ff1690565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace04805460609160008051602061111b8339815191529161039b90610fb1565b60003361042d818585610816565b61070f82610587565b61071881610875565b6105c58383610924565b6001600160a01b0391821660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b6105fe8383836001610a0c565b610781610af4565b61065b8282610b3d565b610793610af4565b565b61079d610af4565b6000805460ff191660ff831617815561065b903361087f565b60006107c28484610722565b905060001981146105c5578181101561080757604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610648565b6105c584848484036000610a0c565b6001600160a01b03831661084057604051634b637e8f60e11b815260006004820152602401610648565b6001600160a01b03821661086a5760405163ec442f0560e01b815260006004820152602401610648565b6105fe838383610b8e565b6106698133610ccc565b600060008051602061113b83398151915261089a8484610681565b61091a576000848152602082815260408083206001600160a01b03871684529091529020805460ff191660011790556108d03390565b6001600160a01b0316836001600160a01b0316857f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a46001915050610356565b6000915050610356565b600060008051602061113b83398151915261093f8484610681565b1561091a576000848152602082815260408083206001600160a01b0387168085529252808320805460ff1916905551339287917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a46001915050610356565b6001600160a01b0382166109ca5760405163ec442f0560e01b815260006004820152602401610648565b61065b60008383610b8e565b6001600160a01b038216610a0057604051634b637e8f60e11b815260006004820152602401610648565b61065b82600083610b8e565b60008051602061111b8339815191526001600160a01b038516610a455760405163e602df0560e01b815260006004820152602401610648565b6001600160a01b038416610a6f57604051634a1406b160e11b815260006004820152602401610648565b6001600160a01b03808616600090815260018301602090815260408083209388168352929052208390558115610aed57836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051610ae491815260200190565b60405180910390a35b5050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661079357604051631afcd79f60e31b815260040160405180910390fd5b610b45610af4565b60008051602061111b8339815191527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03610b7f8482611039565b50600481016105c58382611039565b60008051602061111b8339815191526001600160a01b038416610bca5781816002016000828254610bbf91906110f9565b90915550610c3c9050565b6001600160a01b03841660009081526020829052604090205482811015610c1d5760405163391434e360e21b81526001600160a01b03861660048201526024810182905260448101849052606401610648565b6001600160a01b03851660009081526020839052604090209083900390555b6001600160a01b038316610c5a576002810180548390039055610c79565b6001600160a01b03831660009081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610cbe91815260200190565b60405180910390a350505050565b610cd68282610681565b61065b5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610648565b600060208284031215610d1757600080fd5b81356001600160e01b031981168114610d2f57600080fd5b9392505050565b600060208083528351808285015260005b81811015610d6357858101830151858201604001528201610d47565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610d9b57600080fd5b919050565b60008060408385031215610db357600080fd5b610dbc83610d84565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b600082601f830112610df157600080fd5b813567ffffffffffffffff80821115610e0c57610e0c610dca565b604051601f8301601f19908116603f01168101908282118183101715610e3457610e34610dca565b81604052838152866020858801011115610e4d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e8257600080fd5b833567ffffffffffffffff80821115610e9a57600080fd5b610ea687838801610de0565b94506020860135915080821115610ebc57600080fd5b50610ec986828701610de0565b925050604084013560ff81168114610ee057600080fd5b809150509250925092565b600080600060608486031215610f0057600080fd5b610f0984610d84565b9250610f1760208501610d84565b9150604084013590509250925092565b600060208284031215610f3957600080fd5b5035919050565b60008060408385031215610f5357600080fd5b82359150610f6360208401610d84565b90509250929050565b600060208284031215610f7e57600080fd5b610d2f82610d84565b60008060408385031215610f9a57600080fd5b610fa383610d84565b9150610f6360208401610d84565b600181811c90821680610fc557607f821691505b602082108103610fe557634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156105fe57600081815260208120601f850160051c810160208610156110125750805b601f850160051c820191505b818110156110315782815560010161101e565b505050505050565b815167ffffffffffffffff81111561105357611053610dca565b611067816110618454610fb1565b84610feb565b602080601f83116001811461109c57600084156110845750858301515b600019600386901b1c1916600185901b178555611031565b600085815260208120601f198616915b828110156110cb578886015182559484019460019091019084016110ac565b50858210156110e95787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b8082018082111561035657634e487b7160e01b600052601160045260246000fdfe52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0002dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800a164736f6c6343000814000a diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/Proxy.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/eth/Proxy.abi.json new file mode 100755 index 00000000..5ec0648f --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/Proxy.abi.json @@ -0,0 +1,107 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "target", + "type": "address" + } + ], + "name": "AddressEmptyCode", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "name": "ERC1967InvalidAdmin", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "ERC1967InvalidImplementation", + "type": "error" + }, + { + "inputs": [], + "name": "ERC1967NonPayable", + "type": "error" + }, + { + "inputs": [], + "name": "FailedInnerCall", + "type": "error" + }, + { + "inputs": [], + "name": "ProxyDeniedAdminAccess", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + } +] diff --git a/integrationTests/relayers/slowTests/testdata/contracts/eth/Proxy.hex b/integrationTests/relayers/slowTests/testdata/contracts/eth/Proxy.hex new file mode 100755 index 00000000..04533efd --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/eth/Proxy.hex @@ -0,0 +1 @@ +0x60a060405260405162000e6538038062000e658339810160408190526200002691620003d3565b8282828281620000378282620000a2565b50508160405162000048906200036c565b6001600160a01b039091168152602001604051809103906000f08015801562000075573d6000803e3d6000fd5b506001600160a01b0316608052620000966200009060805190565b62000108565b505050505050620004d1565b620000ad826200017a565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a2805115620000fa57620000f58282620001fa565b505050565b6200010462000277565b5050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6200014a60008051602062000e45833981519152546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a1620001778162000299565b50565b806001600160a01b03163b600003620001b657604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060600080846001600160a01b031684604051620002199190620004b3565b600060405180830381855af49150503d806000811462000256576040519150601f19603f3d011682016040523d82523d6000602084013e6200025b565b606091505b5090925090506200026e858383620002dc565b95945050505050565b3415620002975760405163b398979f60e01b815260040160405180910390fd5b565b6001600160a01b038116620002c557604051633173bdd160e11b815260006004820152602401620001ad565b8060008051602062000e45833981519152620001d9565b606082620002f557620002ef8262000342565b6200033b565b81511580156200030d57506001600160a01b0384163b155b156200033857604051639996b31560e01b81526001600160a01b0385166004820152602401620001ad565b50805b9392505050565b805115620003535780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6104fb806200094a83390190565b80516001600160a01b03811681146200039257600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620003ca578181015183820152602001620003b0565b50506000910152565b600080600060608486031215620003e957600080fd5b620003f4846200037a565b925062000404602085016200037a565b60408501519092506001600160401b03808211156200042257600080fd5b818601915086601f8301126200043757600080fd5b8151818111156200044c576200044c62000397565b604051601f8201601f19908116603f0116810190838211818310171562000477576200047762000397565b816040528281528960208487010111156200049157600080fd5b620004a4836020830160208801620003ad565b80955050505050509250925092565b60008251620004c7818460208701620003ad565b9190910192915050565b60805161045e620004ec60003960006010015261045e6000f3fe608060405261000c61000e565b005b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361007b576000356001600160e01b03191663278f794360e11b14610071576040516334ad5dbb60e21b815260040160405180910390fd5b610079610083565b565b6100796100b2565b6000806100933660048184610312565b8101906100a09190610352565b915091506100ae82826100c2565b5050565b6100796100bd61011d565b610155565b6100cb82610179565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156101155761011082826101f5565b505050565b6100ae61026b565b60006101507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e808015610174573d6000f35b3d6000fd5b806001600160a01b03163b6000036101b457604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516102129190610422565b600060405180830381855af49150503d806000811461024d576040519150601f19603f3d011682016040523d82523d6000602084013e610252565b606091505b509150915061026285838361028a565b95945050505050565b34156100795760405163b398979f60e01b815260040160405180910390fd5b60608261029f5761029a826102e9565b6102e2565b81511580156102b657506001600160a01b0384163b155b156102df57604051639996b31560e01b81526001600160a01b03851660048201526024016101ab565b50805b9392505050565b8051156102f95780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6000808585111561032257600080fd5b8386111561032f57600080fd5b5050820193919092039150565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561036557600080fd5b82356001600160a01b038116811461037c57600080fd5b9150602083013567ffffffffffffffff8082111561039957600080fd5b818501915085601f8301126103ad57600080fd5b8135818111156103bf576103bf61033c565b604051601f8201601f19908116603f011681019083821181831017156103e7576103e761033c565b8160405282815288602084870101111561040057600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000825160005b818110156104435760208186018101518583015201610429565b50600092019182525091905056fea164736f6c6343000814000a608060405234801561001057600080fd5b506040516104fb3803806104fb83398101604081905261002f916100be565b806001600160a01b03811661005e57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6100678161006e565b50506100ee565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156100d057600080fd5b81516001600160a01b03811681146100e757600080fd5b9392505050565b6103fe806100fd6000396000f3fe60806040526004361061004a5760003560e01c8063715018a61461004f5780638da5cb5b146100665780639623609d14610093578063ad3cb1cc146100a6578063f2fde38b146100e4575b600080fd5b34801561005b57600080fd5b50610064610104565b005b34801561007257600080fd5b506000546040516001600160a01b0390911681526020015b60405180910390f35b6100646100a1366004610272565b610118565b3480156100b257600080fd5b506100d7604051806040016040528060058152602001640352e302e360dc1b81525081565b60405161008a919061038e565b3480156100f057600080fd5b506100646100ff3660046103a8565b610187565b61010c6101ca565b61011660006101f7565b565b6101206101ca565b60405163278f794360e11b81526001600160a01b03841690634f1ef28690349061015090869086906004016103c5565b6000604051808303818588803b15801561016957600080fd5b505af115801561017d573d6000803e3d6000fd5b5050505050505050565b61018f6101ca565b6001600160a01b0381166101be57604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6101c7816101f7565b50565b6000546001600160a01b031633146101165760405163118cdaa760e01b81523360048201526024016101b5565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146101c757600080fd5b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561028757600080fd5b833561029281610247565b925060208401356102a281610247565b9150604084013567ffffffffffffffff808211156102bf57600080fd5b818601915086601f8301126102d357600080fd5b8135818111156102e5576102e561025c565b604051601f8201601f19908116603f0116810190838211818310171561030d5761030d61025c565b8160405282815289602084870101111561032657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000815180845260005b8181101561036e57602081850181015186830182015201610352565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006103a16020830184610348565b9392505050565b6000602082840312156103ba57600080fd5b81356103a181610247565b6001600160a01b03831681526040602082018190526000906103e990830184610348565b94935050505056fea164736f6c6343000814000ab53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103 diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.abi.json new file mode 100644 index 00000000..7b4939af --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.abi.json @@ -0,0 +1,252 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "bridge-proxy", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "name": "BridgeProxyContract", + "constructor": { + "inputs": [ + { + "name": "opt_multi_transfer_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "deposit", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "eth_tx", + "type": "EthTransaction" + }, + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [] + }, + { + "name": "execute", + "mutability": "mutable", + "inputs": [ + { + "name": "tx_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "getPendingTransactionById", + "mutability": "readonly", + "inputs": [ + { + "name": "tx_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "EthTransaction" + } + ] + }, + { + "name": "getPendingTransactions", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic>", + "multi_result": true + } + ] + }, + { + "name": "setMultiTransferAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_multi_transfer_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setBridgedTokensWrapperAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setEsdtSafeAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "getMultiTransferAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getBridgedTokensWrapperAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getEsdtSafeContractAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "highestTxId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + } + ], + "promisesCallbackNames": [ + "execution_callback" + ], + "events": [ + { + "identifier": "pauseContract", + "inputs": [] + }, + { + "identifier": "unpauseContract", + "inputs": [] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EthAddress": { + "type": "struct", + "docs": [ + "Wrapper over a 20-byte array" + ], + "fields": [ + { + "name": "raw_addr", + "type": "array20" + } + ] + }, + "EthTransaction": { + "type": "struct", + "fields": [ + { + "name": "from", + "type": "EthAddress" + }, + { + "name": "to", + "type": "Address" + }, + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "tx_nonce", + "type": "u64" + }, + { + "name": "call_data", + "type": "Option" + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.wasm new file mode 100755 index 00000000..57d10bd9 Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.wasm differ diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridged-tokens-wrapper.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridged-tokens-wrapper.abi.json new file mode 100644 index 00000000..4be70732 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridged-tokens-wrapper.abi.json @@ -0,0 +1,337 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "bridged-tokens-wrapper", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "name": "BridgedTokensWrapper", + "constructor": { + "inputs": [], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "addWrappedToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "universal_bridged_token_ids", + "type": "TokenIdentifier" + }, + { + "name": "num_decimals", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "updateWrappedToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "universal_bridged_token_ids", + "type": "TokenIdentifier" + }, + { + "name": "num_decimals", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "removeWrappedToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "universal_bridged_token_ids", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "whitelistToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "chain_specific_token_id", + "type": "TokenIdentifier" + }, + { + "name": "chain_specific_token_decimals", + "type": "u32" + }, + { + "name": "universal_bridged_token_ids", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "updateWhitelistedToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "chain_specific_token_id", + "type": "TokenIdentifier" + }, + { + "name": "chain_specific_token_decimals", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "blacklistToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "chain_specific_token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "depositLiquidity", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [] + }, + { + "docs": [ + "Will wrap what it can, and send back the rest unchanged" + ], + "name": "wrapTokens", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "name": "unwrapToken", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "requested_token", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "unwrapTokenCreateTransaction", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "requested_token", + "type": "TokenIdentifier" + }, + { + "name": "safe_address", + "type": "Address" + }, + { + "name": "to", + "type": "EthAddress" + } + ], + "outputs": [] + }, + { + "name": "getUniversalBridgedTokenIds", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "getTokenLiquidity", + "mutability": "readonly", + "inputs": [ + { + "name": "token", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getChainSpecificToUniversalMapping", + "mutability": "readonly", + "inputs": [ + { + "name": "token", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "getchainSpecificTokenIds", + "mutability": "readonly", + "inputs": [ + { + "name": "universal_token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + } + ], + "events": [ + { + "identifier": "pauseContract", + "inputs": [] + }, + { + "identifier": "unpauseContract", + "inputs": [] + }, + { + "identifier": "wrap_tokens", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "amount", + "type": "BigUint", + "indexed": true + } + ] + }, + { + "identifier": "unwrap_tokens", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "amount", + "type": "BigUint", + "indexed": true + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "EthAddress": { + "type": "struct", + "docs": [ + "Wrapper over a 20-byte array" + ], + "fields": [ + { + "name": "raw_addr", + "type": "array20" + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridged-tokens-wrapper.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridged-tokens-wrapper.wasm new file mode 100755 index 00000000..99dd4096 Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridged-tokens-wrapper.wasm differ diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/esdt-safe.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/esdt-safe.abi.json new file mode 100644 index 00000000..537cbf6c --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/esdt-safe.abi.json @@ -0,0 +1,1161 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "esdt-safe", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "name": "EsdtSafe", + "constructor": { + "docs": [ + "fee_estimator_contract_address - The address of a Price Aggregator contract,", + "which will get the price of token A in token B", + "", + "eth_tx_gas_limit - The gas limit that will be used for transactions on the ETH side.", + "Will be used to compute the fees for the transfer" + ], + "inputs": [ + { + "name": "fee_estimator_contract_address", + "type": "Address" + }, + { + "name": "multi_transfer_contract_address", + "type": "Address" + }, + { + "name": "eth_tx_gas_limit", + "type": "BigUint" + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [ + { + "name": "fee_estimator_contract_address", + "type": "Address" + }, + { + "name": "multi_transfer_contract_address", + "type": "Address" + }, + { + "name": "bridge_proxy_contract_address", + "type": "Address" + }, + { + "name": "eth_tx_gas_limit", + "type": "BigUint" + } + ], + "outputs": [] + }, + "endpoints": [ + { + "docs": [ + "Sets the statuses for the transactions, after they were executed on the Ethereum side.", + "", + "Only TransactionStatus::Executed (3) and TransactionStatus::Rejected (4) values are allowed.", + "Number of provided statuses must be equal to number of transactions in the batch." + ], + "name": "setTransactionBatchStatus", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + }, + { + "name": "tx_statuses", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Converts failed Ethereum -> MultiversX transactions to MultiversX -> Ethereum transaction.", + "This is done every now and then to refund the tokens.", + "", + "As with normal MultiversX -> Ethereum transactions, a part of the tokens will be", + "subtracted to pay for the fees" + ], + "name": "addRefundBatch", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "refund_transactions", + "type": "List" + } + ], + "outputs": [] + }, + { + "docs": [ + "Create an MultiversX -> Ethereum transaction. Only fungible tokens are accepted.", + "", + "Every transfer will have a part of the tokens subtracted as fees.", + "The fee amount depends on the global eth_tx_gas_limit", + "and the current GWEI price, respective to the bridged token", + "", + "fee_amount = price_per_gas_unit * eth_tx_gas_limit" + ], + "name": "createTransaction", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "to", + "type": "EthAddress" + }, + { + "name": "opt_refund_info", + "type": "optional", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Claim funds for failed MultiversX -> Ethereum transactions.", + "These are not sent automatically to prevent the contract getting stuck.", + "For example, if the receiver is a SC, a frozen account, etc." + ], + "name": "claimRefund", + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "EsdtTokenPayment" + } + ] + }, + { + "name": "setBridgedTokensWrapperAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setBridgeProxyContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_new_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "withdrawRefundFeesForEthereum", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "multisig_owner", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "withdrawTransactionFees", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "multisig_owner", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "computeTotalAmmountsFromIndex", + "mutability": "readonly", + "inputs": [ + { + "name": "start_index", + "type": "u64" + }, + { + "name": "end_index", + "type": "u64" + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "docs": [ + "Query function that lists all refund amounts for a user.", + "Useful for knowing which token IDs to pass to the claimRefund endpoint." + ], + "name": "getRefundAmounts", + "mutability": "readonly", + "inputs": [ + { + "name": "address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "variadic>", + "multi_result": true + } + ] + }, + { + "name": "getTotalRefundAmounts", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic>", + "multi_result": true + } + ] + }, + { + "name": "getRefundFeesForEthereum", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getTransactionFees", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getBridgedTokensWrapperAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getBridgeProxyContractAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "setFeeEstimatorContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "setEthTxGasLimit", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_limit", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "docs": [ + "Default price being used if the aggregator lacks a mapping for this token", + "or the aggregator address is not set" + ], + "name": "setDefaultPricePerGasUnit", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "default_price_per_gas_unit", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "docs": [ + "Token ticker being used when querying the aggregator for GWEI prices" + ], + "name": "setTokenTicker", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "ticker", + "type": "bytes" + } + ], + "outputs": [] + }, + { + "docs": [ + "Returns the fee for the given token ID (the fee amount is in the given token)" + ], + "name": "calculateRequiredFee", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getFeeEstimatorContractAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getDefaultPricePerGasUnit", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getEthTxGasLimit", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "docs": [ + "Distributes the accumulated fees to the given addresses.", + "Expected arguments are pairs of (address, percentage),", + "where percentages must add up to the PERCENTAGE_TOTAL constant" + ], + "name": "distributeFees", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "address_percentage_pairs", + "type": "List" + } + ], + "outputs": [] + }, + { + "name": "addTokenToWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "ticker", + "type": "bytes" + }, + { + "name": "mint_burn_token", + "type": "bool" + }, + { + "name": "native_token", + "type": "bool" + }, + { + "name": "total_balance", + "type": "BigUint" + }, + { + "name": "mint_balance", + "type": "BigUint" + }, + { + "name": "burn_balance", + "type": "BigUint" + }, + { + "name": "opt_default_price_per_gas_unit", + "type": "optional", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "removeTokenFromWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "getTokens", + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "initSupply", + "onlyOwner": true, + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "initSupplyMintBurn", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "mint_amount", + "type": "BigUint" + }, + { + "name": "burn_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "setMultiTransferContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_new_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "getAllKnownTokens", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "isNativeToken", + "mutability": "readonly", + "inputs": [ + { + "name": "token", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "isMintBurnToken", + "mutability": "readonly", + "inputs": [ + { + "name": "token", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "getMultiTransferContractAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getAccumulatedTransactionFees", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getTotalBalances", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getMintBalances", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getBurnBalances", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "setMaxTxBatchSize", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_size", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "setMaxTxBatchBlockDuration", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_block_duration", + "type": "u64" + } + ], + "outputs": [] + }, + { + "name": "getCurrentTxBatch", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "name": "getFirstBatchAnyStatus", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "name": "getBatch", + "mutability": "readonly", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "name": "getBatchStatus", + "mutability": "readonly", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [ + { + "type": "BatchStatus" + } + ] + }, + { + "name": "getFirstBatchId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getLastBatchId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "setMaxBridgedAmount", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "max_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "getMaxBridgedAmount", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + } + ], + "events": [ + { + "identifier": "createTransactionEvent", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + }, + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "amount", + "type": "BigUint", + "indexed": true + }, + { + "name": "fee", + "type": "BigUint", + "indexed": true + }, + { + "name": "sender", + "type": "bytes", + "indexed": true + }, + { + "name": "recipient", + "type": "bytes", + "indexed": true + } + ] + }, + { + "identifier": "createRefundTransactionEvent", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + }, + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "amount", + "type": "BigUint", + "indexed": true + }, + { + "name": "fee", + "type": "BigUint", + "indexed": true + }, + { + "name": "initial_batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "initial_tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "addRefundTransactionEvent", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + }, + { + "name": "original_tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "claimRefundTransactionEvent", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "caller", + "type": "Address", + "indexed": true + } + ] + }, + { + "identifier": "setStatusEvent", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "from", + "type": "bytes", + "indexed": true + }, + { + "name": "to", + "type": "bytes", + "indexed": true + }, + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "amount", + "type": "BigUint", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_status", + "type": "TransactionStatus", + "indexed": true + } + ] + }, + { + "identifier": "pauseContract", + "inputs": [] + }, + { + "identifier": "unpauseContract", + "inputs": [] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "AddressPercentagePair": { + "type": "struct", + "fields": [ + { + "name": "address", + "type": "Address" + }, + { + "name": "percentage", + "type": "u32" + } + ] + }, + "BatchStatus": { + "type": "enum", + "variants": [ + { + "name": "AlreadyProcessed", + "discriminant": 0 + }, + { + "name": "Empty", + "discriminant": 1 + }, + { + "name": "PartiallyFull", + "discriminant": 2, + "fields": [ + { + "name": "end_block_nonce", + "type": "u64" + }, + { + "name": "tx_ids", + "type": "List" + } + ] + }, + { + "name": "Full", + "discriminant": 3 + }, + { + "name": "WaitingForSignatures", + "discriminant": 4 + } + ] + }, + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "EthAddress": { + "type": "struct", + "docs": [ + "Wrapper over a 20-byte array" + ], + "fields": [ + { + "name": "raw_addr", + "type": "array20" + } + ] + }, + "RefundInfo": { + "type": "struct", + "fields": [ + { + "name": "address", + "type": "Address" + }, + { + "name": "initial_batch_id", + "type": "u64" + }, + { + "name": "initial_nonce", + "type": "u64" + } + ] + }, + "Transaction": { + "type": "struct", + "fields": [ + { + "name": "block_nonce", + "type": "u64" + }, + { + "name": "nonce", + "type": "u64" + }, + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + }, + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "is_refund_tx", + "type": "bool" + } + ] + }, + "TransactionStatus": { + "type": "enum", + "variants": [ + { + "name": "None", + "discriminant": 0 + }, + { + "name": "Pending", + "discriminant": 1 + }, + { + "name": "InProgress", + "discriminant": 2 + }, + { + "name": "Executed", + "discriminant": 3 + }, + { + "name": "Rejected", + "discriminant": 4 + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/esdt-safe.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/esdt-safe.wasm new file mode 100755 index 00000000..7e179467 Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/esdt-safe.wasm differ diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multi-transfer-esdt.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multi-transfer-esdt.abi.json new file mode 100644 index 00000000..aa20529a --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multi-transfer-esdt.abi.json @@ -0,0 +1,450 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "multi-transfer-esdt", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "name": "MultiTransferEsdt", + "constructor": { + "inputs": [], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "batchTransferEsdtToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + }, + { + "name": "transfers", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "moveRefundBatchToSafe", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "setWrappingContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_new_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setBridgeProxyContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_new_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "addUnprocessedRefundTxToBatch", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "tx_id", + "type": "u64" + } + ], + "outputs": [] + }, + { + "name": "setEsdtSafeContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_new_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "getWrappingContractAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getBridgeProxyContractAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getEsdtSafeContractAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "setMaxTxBatchSize", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_size", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "setMaxTxBatchBlockDuration", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_block_duration", + "type": "u64" + } + ], + "outputs": [] + }, + { + "name": "getCurrentTxBatch", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "name": "getFirstBatchAnyStatus", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "name": "getBatch", + "mutability": "readonly", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "name": "getBatchStatus", + "mutability": "readonly", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [ + { + "type": "BatchStatus" + } + ] + }, + { + "name": "getFirstBatchId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getLastBatchId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "setMaxBridgedAmount", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "max_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "getMaxBridgedAmount", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + } + ], + "events": [ + { + "identifier": "transferPerformedEvent", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "from", + "type": "EthAddress", + "indexed": true + }, + { + "name": "to", + "type": "Address", + "indexed": true + }, + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + }, + { + "name": "amount", + "type": "BigUint", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferFailedInvalidDestination", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferFailedInvalidToken", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferFailedFrozenDestinationAccount", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferOverMaxAmount", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "unprocessedRefundTxs", + "inputs": [ + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "BatchStatus": { + "type": "enum", + "variants": [ + { + "name": "AlreadyProcessed", + "discriminant": 0 + }, + { + "name": "Empty", + "discriminant": 1 + }, + { + "name": "PartiallyFull", + "discriminant": 2, + "fields": [ + { + "name": "end_block_nonce", + "type": "u64" + }, + { + "name": "tx_ids", + "type": "List" + } + ] + }, + { + "name": "Full", + "discriminant": 3 + }, + { + "name": "WaitingForSignatures", + "discriminant": 4 + } + ] + }, + "EthAddress": { + "type": "struct", + "docs": [ + "Wrapper over a 20-byte array" + ], + "fields": [ + { + "name": "raw_addr", + "type": "array20" + } + ] + }, + "EthTransaction": { + "type": "struct", + "fields": [ + { + "name": "from", + "type": "EthAddress" + }, + { + "name": "to", + "type": "Address" + }, + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "tx_nonce", + "type": "u64" + }, + { + "name": "call_data", + "type": "Option" + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multi-transfer-esdt.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multi-transfer-esdt.wasm new file mode 100755 index 00000000..15a73562 Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multi-transfer-esdt.wasm differ diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multisig.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multisig.abi.json new file mode 100644 index 00000000..f35ac561 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multisig.abi.json @@ -0,0 +1,1440 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "multisig", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "docs": [ + "Multi-signature smart contract implementation.", + "Acts like a wallet that needs multiple signers for any action performed." + ], + "name": "Multisig", + "constructor": { + "docs": [ + "EsdtSafe and MultiTransferEsdt are expected to be deployed and configured separately,", + "and then having their ownership changed to this Multisig SC." + ], + "inputs": [ + { + "name": "esdt_safe_sc_address", + "type": "Address" + }, + { + "name": "multi_transfer_sc_address", + "type": "Address" + }, + { + "name": "proxy_sc_address", + "type": "Address" + }, + { + "name": "required_stake", + "type": "BigUint" + }, + { + "name": "slash_amount", + "type": "BigUint" + }, + { + "name": "quorum", + "type": "u32" + }, + { + "name": "board", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [ + { + "name": "esdt_safe_sc_address", + "type": "Address" + }, + { + "name": "multi_transfer_sc_address", + "type": "Address" + }, + { + "name": "proxy_sc_address", + "type": "Address" + } + ], + "outputs": [] + }, + "endpoints": [ + { + "docs": [ + "Distributes the accumulated fees to the given addresses.", + "Expected arguments are pairs of (address, percentage),", + "where percentages must add up to the PERCENTAGE_TOTAL constant" + ], + "name": "distributeFeesFromChildContracts", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "dest_address_percentage_pairs", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Board members have to stake a certain amount of EGLD", + "before being allowed to sign actions" + ], + "name": "stake", + "mutability": "mutable", + "payableInTokens": [ + "EGLD" + ], + "inputs": [], + "outputs": [] + }, + { + "name": "unstake", + "mutability": "mutable", + "inputs": [ + { + "name": "amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "docs": [ + "After a batch is processed on the Ethereum side,", + "the EsdtSafe expects a list of statuses of said transactions (success or failure).", + "", + "This endpoint proposes an action to set the statuses to a certain list of values.", + "Nothing is changed in the EsdtSafe contract until the action is signed and executed." + ], + "name": "proposeEsdtSafeSetCurrentTransactionBatchStatus", + "mutability": "mutable", + "inputs": [ + { + "name": "esdt_safe_batch_id", + "type": "u64" + }, + { + "name": "tx_batch_status", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Proposes a batch of Ethereum -> MultiversX transfers.", + "Transactions have to be separated by fields, in the following order:", + "Sender Address, Destination Address, Token ID, Amount, Tx Nonce" + ], + "name": "proposeMultiTransferEsdtBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "eth_batch_id", + "type": "u64" + }, + { + "name": "transfers", + "type": "variadic>>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Failed Ethereum -> MultiversX transactions are saved in the MultiTransfer SC", + "as \"refund transactions\", and stored in batches, using the same mechanism as EsdtSafe.", + "", + "This function moves the first refund batch into the EsdtSafe SC,", + "converting the transactions into MultiversX -> Ethereum transactions", + "and adding them into EsdtSafe batches" + ], + "name": "moveRefundBatchToSafeFromChildContract", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "initSupplyFromChildContract", + "onlyOwner": true, + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "addUnprocessedRefundTxToBatch", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "tx_id", + "type": "u64" + } + ], + "outputs": [] + }, + { + "name": "withdrawRefundFeesForEthereum", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "withdrawTransactionFees", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "withdrawSlashedAmount", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "docs": [ + "Proposers and board members use this to launch signed actions." + ], + "name": "performAction", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Used by board members to sign actions." + ], + "name": "sign", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "upgradeChildContractFromSource", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "child_sc_address", + "type": "Address" + }, + { + "name": "source_address", + "type": "Address" + }, + { + "name": "is_payable", + "type": "bool" + }, + { + "name": "init_args", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "addBoardMember", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "board_member", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "removeUser", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "board_member", + "type": "Address" + } + ], + "outputs": [] + }, + { + "docs": [ + "Cuts a fixed amount from a board member's stake.", + "This should be used only in cases where the board member", + "is being actively malicious.", + "", + "After stake is cut, the board member would have to stake again", + "to be able to sign actions." + ], + "name": "slashBoardMember", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "board_member", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "changeQuorum", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_quorum", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Maps an ESDT token to an ERC20 address. Used by relayers." + ], + "name": "addMapping", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "erc20_address", + "type": "EthAddress" + }, + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "clearMapping", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "erc20_address", + "type": "EthAddress" + }, + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "pauseEsdtSafe", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpauseEsdtSafe", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "initSupplyEsdtSafe", + "onlyOwner": true, + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "initSupplyMintBurnEsdtSafe", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "mint_amount", + "type": "BigUint" + }, + { + "name": "burn_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "pauseProxy", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpauseProxy", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "changeFeeEstimatorContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "docs": [ + "Sets the gas limit being used for Ethereum transactions", + "This is used in the EsdtSafe contract to determine the fee amount", + "", + "fee_amount = eth_gas_limit * price_per_gas_unit", + "", + "where price_per_gas_unit is queried from the aggregator (fee estimator SC)" + ], + "name": "changeMultiversXToEthGasLimit", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_gas_limit", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "docs": [ + "Default price being used if the aggregator lacks a mapping for this token", + "or the aggregator address is not set" + ], + "name": "changeDefaultPricePerGasUnit", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "new_value", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "docs": [ + "Token ticker being used when querying the aggregator for GWEI prices" + ], + "name": "changeTokenTicker", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "new_ticker", + "type": "bytes" + } + ], + "outputs": [] + }, + { + "name": "esdtSafeAddTokenToWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "ticker", + "type": "bytes" + }, + { + "name": "mint_burn_allowed", + "type": "bool" + }, + { + "name": "is_native_token", + "type": "bool" + }, + { + "name": "total_balance", + "type": "BigUint" + }, + { + "name": "mint_balance", + "type": "BigUint" + }, + { + "name": "burn_balance", + "type": "BigUint" + }, + { + "name": "opt_default_price_per_gas_unit", + "type": "optional", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setMultiTransferOnEsdtSafe", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "setEsdtSafeOnMultiTransfer", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "esdtSafeRemoveTokenFromWhitelist", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "docs": [ + "Sets maximum batch size for the EsdtSafe SC.", + "If a batch reaches this amount of transactions, it is considered full,", + "and a new incoming transaction will be put into a new batch." + ], + "name": "esdtSafeSetMaxTxBatchSize", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_size", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Sets the maximum block duration in which an EsdtSafe batch accepts transactions", + "For a batch to be considered \"full\", it has to either reach `maxTxBatchSize` transactions,", + "or have txBatchBlockDuration blocks pass since the first tx was added in the batch" + ], + "name": "esdtSafeSetMaxTxBatchBlockDuration", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_block_duration", + "type": "u64" + } + ], + "outputs": [] + }, + { + "docs": [ + "Sets the maximum bridged amount for the token for the MultiversX -> Ethereum direction.", + "Any attempt to transfer over this amount will be rejected." + ], + "name": "esdtSafeSetMaxBridgedAmountForToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "max_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "docs": [ + "Same as the function above, but for Ethereum -> MultiversX transactions." + ], + "name": "multiTransferEsdtSetMaxBridgedAmountForToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "max_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "docs": [ + "Any failed Ethereum -> MultiversX transactions are added into so-called \"refund batches\\", + "This configures the size of a batch." + ], + "name": "multiTransferEsdtSetMaxRefundTxBatchSize", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_size", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Max block duration for refund batches. Default is \"infinite\" (u64::MAX)", + "and only max batch size matters" + ], + "name": "multiTransferEsdtSetMaxRefundTxBatchBlockDuration", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_block_duration", + "type": "u64" + } + ], + "outputs": [] + }, + { + "docs": [ + "Sets the wrapping contract address.", + "This contract is used to map multiple tokens to a universal one.", + "Useful in cases where a single token (USDC for example)", + "is being transferred from multiple chains.", + "", + "They will all have different token IDs, but can be swapped 1:1 in the wrapping SC.", + "The wrapping is done automatically, so the user only receives the universal token." + ], + "name": "multiTransferEsdtSetWrappingContractAddress", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "opt_wrapping_contract_address", + "type": "optional
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Minimum number of signatures needed to perform any action." + ], + "name": "getQuorum", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Denormalized board member count.", + "It is kept in sync with the user list by the contract." + ], + "name": "getNumBoardMembers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "The required amount to stake for accepting relayer position" + ], + "name": "getRequiredStakeAmount", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "docs": [ + "Staked amount by each board member." + ], + "name": "getAmountStaked", + "mutability": "readonly", + "inputs": [ + { + "name": "board_member_address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "docs": [ + "Amount of stake slashed if a relayer is misbehaving" + ], + "name": "getSlashAmount", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "docs": [ + "Total slashed tokens accumulated" + ], + "name": "getSlashedTokensAmount", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getLastExecutedEthBatchId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getLastExecutedEthTxId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "docs": [ + "Mapping between ERC20 Ethereum address and MultiversX ESDT Token Identifiers" + ], + "name": "getErc20AddressForTokenId", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "EthAddress" + } + ] + }, + { + "name": "getTokenIdForErc20Address", + "mutability": "readonly", + "inputs": [ + { + "name": "erc20_address", + "type": "EthAddress" + } + ], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "getEsdtSafeAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getMultiTransferEsdtAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "name": "getProxyAddress", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "Address" + } + ] + }, + { + "docs": [ + "Returns the current EsdtSafe batch.", + "", + "First result is the batch ID, then pairs of 6 results, representing transactions", + "split by fields:", + "", + "Block Nonce, Tx Nonce, Sender Address, Receiver Address, Token ID, Amount" + ], + "name": "getCurrentTxBatch", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "docs": [ + "Returns the EsdtSafe batch that has the provided batch_id.", + "", + "First result is the batch ID, then pairs of 6 results, representing transactions", + "split by fields:", + "", + "Block Nonce, Tx Nonce, Sender Address, Receiver Address, Token ID, Amount" + ], + "name": "getBatch", + "mutability": "readonly", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "docs": [ + "Returns a batch of failed Ethereum -> MultiversX transactions.", + "The result format is the same as getCurrentTxBatch" + ], + "name": "getCurrentRefundBatch", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional>>>", + "multi_result": true + } + ] + }, + { + "docs": [ + "Actions are cleared after execution, so an empty entry means the action was executed already", + "Returns \"false\" if the action ID is invalid" + ], + "name": "wasActionExecuted", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "docs": [ + "Used for Ethereum -> MultiversX batches.", + "If the mapping was made, it means that the transfer action was proposed in the past.", + "To check if it was executed as well, use the wasActionExecuted view" + ], + "name": "wasTransferActionProposed", + "mutability": "readonly", + "inputs": [ + { + "name": "eth_batch_id", + "type": "u64" + }, + { + "name": "transfers", + "type": "variadic>>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "docs": [ + "Used for Ethereum -> MultiversX batches.", + "If `wasActionExecuted` returns true, then this can be used to get the action ID.", + "Will return 0 if the transfers were not proposed" + ], + "name": "getActionIdForTransferBatch", + "mutability": "readonly", + "inputs": [ + { + "name": "eth_batch_id", + "type": "u64" + }, + { + "name": "transfers", + "type": "variadic>>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Used for MultiversX -> Ethereum batches.", + "Returns \"true\" if an action was already proposed for the given batch,", + "with these exact transaction statuses, in this exact order" + ], + "name": "wasSetCurrentTransactionBatchStatusActionProposed", + "mutability": "readonly", + "inputs": [ + { + "name": "esdt_safe_batch_id", + "type": "u64" + }, + { + "name": "expected_tx_batch_status", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "docs": [ + "If `wasSetCurrentTransactionBatchStatusActionProposed` return true,", + "this can be used to get the action ID.", + "Will return 0 if the set status action was not proposed" + ], + "name": "getActionIdForSetCurrentTransactionBatchStatus", + "mutability": "readonly", + "inputs": [ + { + "name": "esdt_safe_batch_id", + "type": "u64" + }, + { + "name": "expected_tx_batch_status", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Returns `true` (`1`) if the user has signed the action.", + "Does not check whether or not the user is still a board member and the signature valid." + ], + "name": "signed", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + }, + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "docs": [ + "Indicates user rights.", + "`0` = no rights,", + "`1` = can propose. Can also sign if they have enough stake." + ], + "name": "userRole", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "UserRole" + } + ] + }, + { + "docs": [ + "Lists all board members" + ], + "name": "getAllBoardMembers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ] + }, + { + "docs": [ + "Lists all board members that staked the correct amount.", + "A board member with not enough stake can propose, but cannot sign." + ], + "name": "getAllStakedRelayers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ] + }, + { + "docs": [ + "Gets the number of signatures for the action with the given ID" + ], + "name": "getActionSignerCount", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "It is possible for board members to lose their role.", + "They are not automatically removed from all actions when doing so,", + "therefore the contract needs to re-check every time when actions are performed.", + "This function is used to validate the signers before performing an action.", + "It also makes it easy to check before performing an action." + ], + "name": "getActionValidSignerCount", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Returns `true` (`1`) if `getActionValidSignerCount >= getQuorum`." + ], + "name": "quorumReached", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "docs": [ + "The index of the last proposed action.", + "0 means that no action was ever proposed yet." + ], + "name": "getActionLastIndex", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Serialized action data of an action with index." + ], + "name": "getActionData", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "Action" + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + } + ], + "events": [ + { + "identifier": "addMapping", + "inputs": [ + { + "name": "erc20_address", + "type": "EthAddress", + "indexed": true + }, + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + } + ] + }, + { + "identifier": "clearMapping", + "inputs": [ + { + "name": "erc20_address", + "type": "EthAddress", + "indexed": true + }, + { + "name": "token_id", + "type": "TokenIdentifier", + "indexed": true + } + ] + }, + { + "identifier": "moveRefundBatchToSafeEvent", + "inputs": [] + }, + { + "identifier": "addUnprocessedRefundTxToBatchEvent", + "inputs": [ + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "pauseBridgeProxyEvent", + "inputs": [] + }, + { + "identifier": "unpauseBridgeProxyEvent", + "inputs": [] + }, + { + "identifier": "pauseContract", + "inputs": [] + }, + { + "identifier": "unpauseContract", + "inputs": [] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "Action": { + "type": "enum", + "variants": [ + { + "name": "Nothing", + "discriminant": 0 + }, + { + "name": "SetCurrentTransactionBatchStatus", + "discriminant": 1, + "fields": [ + { + "name": "esdt_safe_batch_id", + "type": "u64" + }, + { + "name": "tx_batch_status", + "type": "List" + } + ] + }, + { + "name": "BatchTransferEsdtToken", + "discriminant": 2, + "fields": [ + { + "name": "eth_batch_id", + "type": "u64" + }, + { + "name": "transfers", + "type": "List" + } + ] + } + ] + }, + "EthAddress": { + "type": "struct", + "docs": [ + "Wrapper over a 20-byte array" + ], + "fields": [ + { + "name": "raw_addr", + "type": "array20" + } + ] + }, + "EthTransaction": { + "type": "struct", + "fields": [ + { + "name": "from", + "type": "EthAddress" + }, + { + "name": "to", + "type": "Address" + }, + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "tx_nonce", + "type": "u64" + }, + { + "name": "call_data", + "type": "Option" + } + ] + }, + "TransactionStatus": { + "type": "enum", + "variants": [ + { + "name": "None", + "discriminant": 0 + }, + { + "name": "Pending", + "discriminant": 1 + }, + { + "name": "InProgress", + "discriminant": 2 + }, + { + "name": "Executed", + "discriminant": 3 + }, + { + "name": "Rejected", + "discriminant": 4 + } + ] + }, + "UserRole": { + "type": "enum", + "variants": [ + { + "name": "None", + "discriminant": 0 + }, + { + "name": "BoardMember", + "discriminant": 1 + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multisig.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multisig.wasm new file mode 100755 index 00000000..ddc0cd82 Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multisig.wasm differ diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.abi.json new file mode 100644 index 00000000..ed796fed --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.abi.json @@ -0,0 +1,537 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "multiversx-price-aggregator-sc", + "version": "0.47.1", + "gitVersion": "v0.45.2.1-reproducible-378-ge72c201" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.53.2" + } + }, + "name": "PriceAggregator", + "constructor": { + "inputs": [ + { + "name": "staking_token", + "type": "EgldOrEsdtTokenIdentifier" + }, + { + "name": "staking_amount", + "type": "BigUint" + }, + { + "name": "slash_amount", + "type": "BigUint" + }, + { + "name": "slash_quorum", + "type": "u32" + }, + { + "name": "submission_count", + "type": "u32" + }, + { + "name": "oracles", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "changeAmounts", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "staking_amount", + "type": "BigUint" + }, + { + "name": "slash_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "addOracles", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "oracles", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Also receives submission count,", + "so the owner does not have to update it manually with setSubmissionCount before this call" + ], + "name": "removeOracles", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "submission_count", + "type": "u32" + }, + { + "name": "oracles", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "submit", + "mutability": "mutable", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + }, + { + "name": "submission_timestamp", + "type": "u64" + }, + { + "name": "price", + "type": "BigUint" + }, + { + "name": "decimals", + "type": "u8" + } + ], + "outputs": [] + }, + { + "name": "submitBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "submissions", + "type": "variadic>", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "latestRoundData", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "latestPriceFeed", + "mutability": "readonly", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "u32" + }, + { + "type": "bytes" + }, + { + "type": "bytes" + }, + { + "type": "u64" + }, + { + "type": "BigUint" + }, + { + "type": "u8" + } + ] + }, + { + "name": "latestPriceFeedOptional", + "mutability": "readonly", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "optional>", + "multi_result": true + } + ] + }, + { + "name": "setSubmissionCount", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "submission_count", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "getOracles", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ] + }, + { + "name": "setPairDecimals", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + }, + { + "name": "decimals", + "type": "u8" + } + ], + "outputs": [] + }, + { + "name": "getPairDecimals", + "mutability": "readonly", + "inputs": [ + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + } + ], + "outputs": [ + { + "type": "u8" + } + ] + }, + { + "name": "submission_count", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "stake", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [] + }, + { + "name": "unstake", + "mutability": "mutable", + "inputs": [ + { + "name": "unstake_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "voteSlashMember", + "mutability": "mutable", + "inputs": [ + { + "name": "member_to_slash", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "cancelVoteSlashMember", + "mutability": "mutable", + "inputs": [ + { + "name": "member_to_slash", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "slashMember", + "mutability": "mutable", + "inputs": [ + { + "name": "member_to_slash", + "type": "Address" + } + ], + "outputs": [] + } + ], + "events": [ + { + "identifier": "pauseContract", + "inputs": [] + }, + { + "identifier": "unpauseContract", + "inputs": [] + }, + { + "identifier": "new_round", + "inputs": [ + { + "name": "from", + "type": "bytes", + "indexed": true + }, + { + "name": "to", + "type": "bytes", + "indexed": true + }, + { + "name": "round", + "type": "u32", + "indexed": true + }, + { + "name": "new_round_event", + "type": "NewRoundEvent" + } + ] + }, + { + "identifier": "discard_submission", + "inputs": [ + { + "name": "from", + "type": "bytes", + "indexed": true + }, + { + "name": "to", + "type": "bytes", + "indexed": true + }, + { + "name": "round", + "type": "u32", + "indexed": true + }, + { + "name": "discard_submission_event", + "type": "DiscardSubmissionEvent" + } + ] + }, + { + "identifier": "discard_round", + "inputs": [ + { + "name": "from", + "type": "bytes", + "indexed": true + }, + { + "name": "to", + "type": "bytes", + "indexed": true + }, + { + "name": "round", + "type": "u32", + "indexed": true + } + ] + }, + { + "identifier": "add_submission", + "inputs": [ + { + "name": "from", + "type": "bytes", + "indexed": true + }, + { + "name": "to", + "type": "bytes", + "indexed": true + }, + { + "name": "round", + "type": "u32", + "indexed": true + }, + { + "name": "price", + "type": "BigUint" + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "DiscardSubmissionEvent": { + "type": "struct", + "fields": [ + { + "name": "submission_timestamp", + "type": "u64" + }, + { + "name": "first_submission_timestamp", + "type": "u64" + }, + { + "name": "has_caller_already_submitted", + "type": "bool" + } + ] + }, + "NewRoundEvent": { + "type": "struct", + "fields": [ + { + "name": "price", + "type": "BigUint" + }, + { + "name": "timestamp", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + }, + { + "name": "block", + "type": "u64" + }, + { + "name": "epoch", + "type": "u64" + } + ] + }, + "PriceFeed": { + "type": "struct", + "fields": [ + { + "name": "round_id", + "type": "u32" + }, + { + "name": "from", + "type": "bytes" + }, + { + "name": "to", + "type": "bytes" + }, + { + "name": "timestamp", + "type": "u64" + }, + { + "name": "price", + "type": "BigUint" + }, + { + "name": "decimals", + "type": "u8" + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm new file mode 100644 index 00000000..ac1668fa Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/multiversx-price-aggregator-sc.wasm differ diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/test-caller.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/test-caller.abi.json new file mode 100644 index 00000000..e7c1a897 --- /dev/null +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/test-caller.abi.json @@ -0,0 +1,95 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.78.0", + "commitHash": "9b00956e56009bab2aa15d7bff10916599e3d6d6", + "commitDate": "2024-04-29", + "channel": "Stable", + "short": "rustc 1.78.0 (9b00956e5 2024-04-29)" + }, + "contractCrate": { + "name": "test-caller", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.52.3" + } + }, + "name": "TestCallerContract", + "constructor": { + "inputs": [], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "name": "callPayable", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [] + }, + { + "name": "callNonPayable", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "callPayableWithParams", + "mutability": "readonly", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "size", + "type": "u64" + }, + { + "name": "address", + "type": "Address" + } + ], + "outputs": [] + }, + { + "name": "getCalledDataParams", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + } + ], + "esdtAttributes": [], + "hasCallback": false, + "types": { + "CalledData": { + "type": "struct", + "fields": [ + { + "name": "size", + "type": "u64" + }, + { + "name": "address", + "type": "Address" + }, + { + "name": "token_identifier", + "type": "TokenIdentifier" + } + ] + } + } +} diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/test-caller.wasm b/integrationTests/relayers/slowTests/testdata/contracts/mvx/test-caller.wasm new file mode 100755 index 00000000..8419ee51 Binary files /dev/null and b/integrationTests/relayers/slowTests/testdata/contracts/mvx/test-caller.wasm differ diff --git a/integrationTests/relayers/testdata/multiversx0.pem b/integrationTests/relayers/testdata/multiversx0.pem index 2f4b0e7f..d37b3fa1 100644 --- a/integrationTests/relayers/testdata/multiversx0.pem +++ b/integrationTests/relayers/testdata/multiversx0.pem @@ -2,4 +2,4 @@ YWVjY2VkOGM3YWUwNDgxOWRkNGVkOGUxODQ3OGUxNjhiZWM4NTkzNTA5OWZmZTk4 ZDExNDRlODRhY2VlZWQxOGE0YzJhMmFhM2E3NDRlNGQ3N2Y2YWYzN2VmMWUzY2Fi MTBkNGY2Y2YwMzBlZGQxMWY3M2UzZWI1OGExODBlZTE= ------END PRIVATE KEY for erd15np29236w38y6alk4um7783u4vgdfak0qv8d6y0h8clttzscpmssfp6u37----- \ No newline at end of file +-----END PRIVATE KEY for erd15np29236w38y6alk4um7783u4vgdfak0qv8d6y0h8clttzscpmssfp6u37----- diff --git a/integrationTests/relayers/testdata/multiversx1.pem b/integrationTests/relayers/testdata/multiversx1.pem index 7a69443c..086cc575 100644 --- a/integrationTests/relayers/testdata/multiversx1.pem +++ b/integrationTests/relayers/testdata/multiversx1.pem @@ -2,4 +2,4 @@ ZTY3YjJhYzMwMWE4NDU2YjRhOTUwNWRkMzYwNmQxYTFmYzM3NTMwNjVlODkzZTkz ZmQzMDY3MTIyZTE0ZTZmY2JhNjNjNGI1Y2Y1NDVhMjNiMWJmODE2MDgwNjNjOTI0 MDk0MWEzNTdjZDNhMDk4MTAyNjY5Y2MzM2RhZDNkZTE= ------END PRIVATE KEY for erd1hf3ufdw023dz8vdls9sgqc7fysy5rg6he5aqnqgzv6wvx0dd8hsskdgfkh----- \ No newline at end of file +-----END PRIVATE KEY for erd1hf3ufdw023dz8vdls9sgqc7fysy5rg6he5aqnqgzv6wvx0dd8hsskdgfkh----- diff --git a/integrationTests/relayers/testdata/multiversx2.pem b/integrationTests/relayers/testdata/multiversx2.pem index f69b48ee..594b7713 100644 --- a/integrationTests/relayers/testdata/multiversx2.pem +++ b/integrationTests/relayers/testdata/multiversx2.pem @@ -2,4 +2,4 @@ OTk1ZjQyNmJkYjIwOWVkNzhjYjk0Y2I5OTIwMGNiMTIxYjkxOWYwMDdmNGZmNmVm NWExNmEwNWI5MTFlYzJmNTBhZjJmYWViNjBlZDEyZjAxNTkxNzk1MmMyZDhjOTI1 YmJjYTc5MDY4OGQ3NmU2MTg2ZDg2N2Q5ZWVmM2RlZTE= ------END PRIVATE KEY for erd1pte046mqa5f0q9v309fv9kxfykau57gx3rtkucvxmpnanmhnmmsstjcrl2----- \ No newline at end of file +-----END PRIVATE KEY for erd1pte046mqa5f0q9v309fv9kxfykau57gx3rtkucvxmpnanmhnmmsstjcrl2----- diff --git a/integrationTests/testscommon.go b/integrationTests/testscommon.go index 13bed95a..57ca5e8c 100644 --- a/integrationTests/testscommon.go +++ b/integrationTests/testscommon.go @@ -5,6 +5,7 @@ import ( "github.com/multiversx/mx-bridge-eth-go/core" "github.com/multiversx/mx-bridge-eth-go/testsCommon/crypto" + "github.com/multiversx/mx-chain-communication-go/p2p/libp2p" "github.com/multiversx/mx-chain-core-go/hashing/blake2b" "github.com/multiversx/mx-chain-core-go/marshal" "github.com/multiversx/mx-chain-crypto-go/signing" @@ -14,7 +15,6 @@ import ( p2pConfig "github.com/multiversx/mx-chain-go/p2p/config" "github.com/multiversx/mx-chain-go/testscommon/p2pmocks" logger "github.com/multiversx/mx-chain-logger-go" - "github.com/multiversx/mx-chain-p2p-go/libp2p" ) // Log - @@ -93,6 +93,16 @@ func CreateMessengerWithNoDiscovery() p2p.Messenger { p2pCfg := p2pConfig.P2PConfig{ Node: p2pConfig.NodeConfig{ Port: "0", + Transports: p2pConfig.P2PTransportConfig{ + TCP: p2pConfig.P2PTCPTransport{ + ListenAddress: "/ip4/127.0.0.1/tcp/%d", + }, + }, + ResourceLimiter: p2pConfig.P2PResourceLimiterConfig{ + Type: "default autoscale", + ManualSystemMemoryInMB: 0, + ManualMaximumFD: 0, + }, }, KadDhtPeerDiscovery: p2pConfig.KadDhtPeerDiscoveryConfig{ Enabled: false, @@ -109,22 +119,16 @@ func CreateMessengerWithNoDiscovery() p2p.Messenger { // CreateMessengerFromConfig creates a new libp2p messenger with provided configuration func CreateMessengerFromConfig(p2pConfig p2pConfig.P2PConfig) p2p.Messenger { arg := libp2p.ArgsNetworkMessenger{ - Marshalizer: &marshal.JsonMarshalizer{}, - ListenAddress: p2p.ListenLocalhostAddrWithIp4AndTcp, + Marshaller: &marshal.JsonMarshalizer{}, P2pConfig: p2pConfig, SyncTimer: &libp2p.LocalSyncTimer{}, PreferredPeersHolder: &p2pmocks.PeersHolderStub{}, - NodeOperationMode: p2p.NormalOperation, ConnectionWatcherType: "disabled", PeersRatingHandler: &p2pmocks.PeersRatingHandlerStub{}, P2pPrivateKey: crypto.NewPrivateKeyMock(), P2pSingleSigner: &crypto.SingleSignerStub{}, P2pKeyGenerator: &crypto.KeyGenStub{}, - } - - if p2pConfig.Sharding.AdditionalConnections.MaxFullHistoryObservers > 0 { - // we deliberately set this, automatically choose full archive node mode - arg.NodeOperationMode = p2p.FullArchiveMode + Logger: Log, } libP2PMes, err := libp2p.NewNetworkMessenger(arg) diff --git a/p2p/broadcaster.go b/p2p/broadcaster.go index e59d773d..16688eb9 100644 --- a/p2p/broadcaster.go +++ b/p2p/broadcaster.go @@ -139,7 +139,7 @@ func (b *broadcaster) RegisterOnTopics() error { } // ProcessReceivedMessage will be called by the network messenger whenever a new message is received -func (b *broadcaster) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer chainCore.PeerID) error { +func (b *broadcaster) ProcessReceivedMessage(message p2p.MessageP2P, fromConnectedPeer chainCore.PeerID, _ p2p.MessageHandler) error { msg, err := b.preProcessMessage(message, fromConnectedPeer) if err != nil { b.log.Debug("got message", "topic", message.Topic(), "error", err) @@ -152,8 +152,9 @@ func (b *broadcaster) ProcessReceivedMessage(message p2p.MessageP2P, fromConnect return fmt.Errorf("%w for peer: %s", ErrPeerNotWhitelisted, hexPkBytes) } + address, _ := addr.AddressAsBech32String() b.log.Trace("got message", "topic", message.Topic(), - "msg.Payload", msg.Payload, "msg.Nonce", msg.Nonce, "msg.PublicKey", addr.AddressAsBech32String()) + "msg.Payload", msg.Payload, "msg.Nonce", msg.Nonce, "msg.PublicKey", address) err = b.processNonce(msg) if err != nil { @@ -165,7 +166,7 @@ func (b *broadcaster) ProcessReceivedMessage(message p2p.MessageP2P, fromConnect err = b.canProcessMessage(message, fromConnectedPeer) if err != nil { b.log.Debug("can't process message", "peer", fromConnectedPeer, "topic", message.Topic(), "msg.Payload", msg.Payload, - "msg.Nonce", msg.Nonce, "msg.PublicKey", addr.AddressAsBech32String(), "error", err) + "msg.Nonce", msg.Nonce, "msg.PublicKey", address, "error", err) return err } diff --git a/p2p/broadcaster_test.go b/p2p/broadcaster_test.go index 6b65a296..07fd7ecb 100644 --- a/p2p/broadcaster_test.go +++ b/p2p/broadcaster_test.go @@ -213,7 +213,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { DataField: []byte("gibberish"), } - err := b.ProcessReceivedMessage(p2pMsg, "") + err := b.ProcessReceivedMessage(p2pMsg, "", nil) assert.NotNil(t, err) }) t.Run("public key not whitelisted", func(t *testing.T) { @@ -234,7 +234,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { DataField: buff, } - err := b.ProcessReceivedMessage(p2pMsg, "") + err := b.ProcessReceivedMessage(p2pMsg, "", nil) assert.True(t, errors.Is(err, ErrPeerNotWhitelisted)) assert.True(t, isWhiteListedCalled) }) @@ -250,11 +250,11 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { DataField: buff, } - err := b.ProcessReceivedMessage(p2pMsg, "") + err := b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Equal(t, ErrNonceTooLowInReceivedMessage, err) b.nonces[string(msg.PublicKeyBytes)] = msg.Nonce - err = b.ProcessReceivedMessage(p2pMsg, "") + err = b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Equal(t, ErrNonceTooLowInReceivedMessage, err) }) t.Run("joined topic should send stored messages from clients", func(t *testing.T) { @@ -301,7 +301,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, PeerField: pid, } - _ = b.ProcessReceivedMessage(p2pMsg, "") + _ = b.ProcessReceivedMessage(p2pMsg, "", nil) msg2, buff2 := createSignedMessageAndMarshaledBytes(1) p2pMsg = &p2pMocks.P2PMessageMock{ @@ -310,7 +310,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { PeerField: pid, } - err = b.ProcessReceivedMessage(p2pMsg, "") + err = b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Nil(t, err) assert.True(t, sendWasCalled) @@ -349,7 +349,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, } - err := b.ProcessReceivedMessage(p2pMsg, "") + err := b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Nil(t, err) p2pMsg = &p2pMocks.P2PMessageMock{ @@ -357,7 +357,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, } - err = b.ProcessReceivedMessage(p2pMsg, "") + err = b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Nil(t, err) assert.Equal(t, 2, len(b.SortedPublicKeys())) @@ -383,7 +383,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, } - err := b.ProcessReceivedMessage(p2pMsg, "") + err := b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Nil(t, err) assert.Equal(t, 1, len(b.SortedPublicKeys())) @@ -422,7 +422,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, } - err = b.ProcessReceivedMessage(p2pMsg, "p1") + err = b.ProcessReceivedMessage(p2pMsg, "p1", nil) assert.Nil(t, err) p2pMsg = &p2pMocks.P2PMessageMock{ @@ -430,7 +430,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, } - err = b.ProcessReceivedMessage(p2pMsg, "p1") + err = b.ProcessReceivedMessage(p2pMsg, "p1", nil) assert.True(t, strings.Contains(err.Error(), "system busy")) }) t.Run("sign should store message", func(t *testing.T) { @@ -451,7 +451,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, } - err := b.ProcessReceivedMessage(p2pMsg, "") + err := b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Nil(t, err) p2pMsg = &p2pMocks.P2PMessageMock{ @@ -459,7 +459,7 @@ func TestBroadcaster_ProcessReceivedMessage(t *testing.T) { TopicField: args.Name + signTopicSuffix, } - err = b.ProcessReceivedMessage(p2pMsg, "") + err = b.ProcessReceivedMessage(p2pMsg, "", nil) assert.Nil(t, err) assert.Equal(t, [][]byte{msg1.PublicKeyBytes, msg2.PublicKeyBytes}, b.SortedPublicKeys()) diff --git a/parsers/errors.go b/parsers/errors.go new file mode 100644 index 00000000..0a774acd --- /dev/null +++ b/parsers/errors.go @@ -0,0 +1,16 @@ +package parsers + +import "errors" + +var ( + errBufferTooShortForMarker = errors.New("buffer too short for protocol indicator") + errUnexpectedMarker = errors.New("unexpected protocol indicator") + errBufferTooShortForLength = errors.New("buffer too short while extracting the length") + errBufferTooShortForString = errors.New("buffer too short while extracting the string data") + errBufferTooShortForUint64 = errors.New("buffer too short for uint64") + errBufferTooShortForUint32 = errors.New("buffer too short for uint32 length") + errBufferTooShortForEthAddress = errors.New("buffer too short for Ethereum address") + errBufferTooShortForMvxAddress = errors.New("buffer too short for MultiversX address") + errBufferTooShortForBigInt = errors.New("buffer too short while extracting the big.Int value") + errBufferLenMismatch = errors.New("buffer length mismatch") +) diff --git a/parsers/multiversxCodec.go b/parsers/multiversxCodec.go new file mode 100644 index 00000000..57adb016 --- /dev/null +++ b/parsers/multiversxCodec.go @@ -0,0 +1,176 @@ +package parsers + +import ( + "encoding/binary" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-sdk-go/data" +) + +const lenEthAddress = 20 +const lenMvxAddress = 32 + +// MultiversxCodec defines the codec operations to be used for MultiversX contracts +type MultiversxCodec struct { +} + +func partiallyDecodeCallData(buff []byte, marker byte) (CallData, error) { + buff, numChars, err := ExtractUint32(buff) + if err != nil { + return CallData{}, fmt.Errorf("%w for len of call data", err) + } + if numChars != len(buff) { + return CallData{}, fmt.Errorf("%w: actual %d, declared %d", errBufferLenMismatch, len(buff), numChars) + } + + buff, function, err := ExtractString(buff) + if err != nil { + return CallData{}, fmt.Errorf("%w for function", err) + } + + _, gasLimit, err := ExtractUint64(buff) + if err != nil { + return CallData{}, fmt.Errorf("%w for gas limit", err) + } + + return CallData{ + Type: marker, + Function: function, + GasLimit: gasLimit, + }, nil +} + +// ExtractString will return the string value after extracting the length of the string from the buffer. +// The buffer returned will be trimmed out of the 4 bytes + the length of the string +func ExtractString(buff []byte) ([]byte, string, error) { + // Ensure there's enough length for the 4 bytes for length + if len(buff) < bridgeCore.Uint32ArgBytes { + return nil, "", errBufferTooShortForLength + } + argumentLength := int(binary.BigEndian.Uint32(buff[:bridgeCore.Uint32ArgBytes])) + buff = buff[bridgeCore.Uint32ArgBytes:] // remove the len bytes + + // Check for the argument data + if len(buff) < argumentLength { + return nil, "", errBufferTooShortForString + } + endpointName := string(buff[:argumentLength]) + buff = buff[argumentLength:] // remove the string bytes + + return buff, endpointName, nil +} + +func extractBigInt(buff []byte) ([]byte, *big.Int, error) { + // Ensure there's enough length for the 4 bytes for length + if len(buff) < bridgeCore.Uint32ArgBytes { + return nil, nil, errBufferTooShortForLength + } + argumentLength := int(binary.BigEndian.Uint32(buff[:bridgeCore.Uint32ArgBytes])) + buff = buff[bridgeCore.Uint32ArgBytes:] // remove the len bytes + + // Check for the argument data + if len(buff) < argumentLength { + return nil, nil, errBufferTooShortForBigInt + } + + value := big.NewInt(0).SetBytes(buff[:argumentLength]) + buff = buff[argumentLength:] // remove the value bytes + + return buff, value, nil +} + +// ExtractUint64 will return the uint64 value after extracting 8 bytes from the buffer. +// The buffer returned will be trimmed out of the 8 bytes +func ExtractUint64(buff []byte) ([]byte, uint64, error) { + // Ensure there's enough length for the 8 bytes + if len(buff) < bridgeCore.Uint64ArgBytes { + return nil, 0, errBufferTooShortForUint64 + } + + value := binary.BigEndian.Uint64(buff[:bridgeCore.Uint64ArgBytes]) + buff = buff[bridgeCore.Uint64ArgBytes:] + + return buff, value, nil +} + +// ExtractUint32 will return the int value after extracting 4 bytes from the buffer. +// The buffer returned will be trimmed out of the 4 bytes +func ExtractUint32(buff []byte) ([]byte, int, error) { + // Ensure there's enough length for the 4 bytes + if len(buff) < bridgeCore.Uint32ArgBytes { + return nil, 0, errBufferTooShortForUint32 + } + value := int(binary.BigEndian.Uint32(buff[:bridgeCore.Uint32ArgBytes])) + buff = buff[bridgeCore.Uint32ArgBytes:] // remove the len bytes + + return buff, value, nil +} + +// DecodeProxySCCompleteCallData will try to decode the provided bytes into a ProxySCCompleteCallData struct +func (codec *MultiversxCodec) DecodeProxySCCompleteCallData(buff []byte) (ProxySCCompleteCallData, error) { + result := ProxySCCompleteCallData{} + + if len(buff) < lenEthAddress { + return ProxySCCompleteCallData{}, errBufferTooShortForEthAddress + } + result.From = common.Address{} + result.From.SetBytes(buff[:lenEthAddress]) + buff = buff[lenEthAddress:] + + if len(buff) < lenMvxAddress { + return ProxySCCompleteCallData{}, errBufferTooShortForMvxAddress + } + result.To = data.NewAddressFromBytes(buff[:lenMvxAddress]) + buff = buff[lenMvxAddress:] + + buff, token, err := ExtractString(buff) + if err != nil { + return ProxySCCompleteCallData{}, fmt.Errorf("%w for token", err) + } + result.Token = token + + buff, amount, err := extractBigInt(buff) + if err != nil { + return ProxySCCompleteCallData{}, fmt.Errorf("%w for amount", err) + } + result.Amount = amount + + buff, nonce, err := ExtractUint64(buff) + if err != nil { + return ProxySCCompleteCallData{}, fmt.Errorf("%w for nonce", err) + } + result.Nonce = nonce + + result.RawCallData = buff + + return result, nil +} + +// ExtractGasLimitFromRawCallData will try to extract the gas limit from the provided buffer +func (codec *MultiversxCodec) ExtractGasLimitFromRawCallData(buff []byte) (uint64, error) { + if len(buff) == 0 { + return 0, errBufferTooShortForMarker + } + + marker := buff[0] + buff = buff[1:] + + if marker != bridgeCore.DataPresentProtocolMarker { + return 0, fmt.Errorf("%w: %d", errUnexpectedMarker, marker) + } + + callData, err := partiallyDecodeCallData(buff, marker) + if err != nil { + return 0, err + } + + return callData.GasLimit, nil +} + +// IsInterfaceNil returns true if there is no value under the interface +func (codec *MultiversxCodec) IsInterfaceNil() bool { + return codec == nil +} diff --git a/parsers/multiversxCodec_test.go b/parsers/multiversxCodec_test.go new file mode 100644 index 00000000..b4ad8a4e --- /dev/null +++ b/parsers/multiversxCodec_test.go @@ -0,0 +1,281 @@ +package parsers + +import ( + "bytes" + "encoding/hex" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func createTestProxySCCompleteCallData() ProxySCCompleteCallData { + ethUnhexed, _ := hex.DecodeString("880ec53af800b5cd051531672ef4fc4de233bd5d") + completeCallData := ProxySCCompleteCallData{ + RawCallData: []byte{'A', 'B', 'C'}, + From: common.Address{}, + Token: "ETHUSDC-0ae8ee", + Amount: big.NewInt(20000), + Nonce: 1, + } + completeCallData.To, _ = data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqsudu3a3n9yu62k5qkgcpy4j9ywl2x2gl5smsy7t4uv") + completeCallData.From.SetBytes(ethUnhexed) + + return completeCallData +} + +func TestMultiversxCodec_IsInterfaceNil(t *testing.T) { + t.Parallel() + + var instance *MultiversxCodec + assert.True(t, instance.IsInterfaceNil()) + + instance = &MultiversxCodec{} + assert.False(t, instance.IsInterfaceNil()) +} + +func TestMultiversxCodec_ExtractGasLimitFromRawCallData(t *testing.T) { + t.Parallel() + + codec := &MultiversxCodec{} + + t.Run("empty buffer should error", func(t *testing.T) { + t.Parallel() + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(nil) + assert.Equal(t, errBufferTooShortForMarker, err) + assert.Zero(t, gasLimit) + + gasLimit, err = codec.ExtractGasLimitFromRawCallData(make([]byte, 0)) + assert.Equal(t, errBufferTooShortForMarker, err) + assert.Zero(t, gasLimit) + }) + t.Run("unexpected marker should error", func(t *testing.T) { + t.Parallel() + + gasLimit, err := codec.ExtractGasLimitFromRawCallData([]byte{0x03}) + assert.ErrorIs(t, err, errUnexpectedMarker) + assert.Contains(t, err.Error(), ": 3") + assert.Zero(t, gasLimit) + }) + t.Run("buffer contains missing data marker should error", func(t *testing.T) { + t.Parallel() + + buff := []byte{0} + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(buff) + assert.ErrorIs(t, err, errUnexpectedMarker) + assert.Contains(t, err.Error(), ": 0") + assert.Zero(t, gasLimit) + }) + t.Run("buffer to short for call data length should error", func(t *testing.T) { + t.Parallel() + + buff := []byte{1} + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForUint32) + assert.Contains(t, err.Error(), "for len of call data") + assert.Zero(t, gasLimit) + }) + t.Run("buffer len for call data mismatch should error", func(t *testing.T) { + t.Parallel() + + buff := []byte{ + 1, + 0, 0, 0, 1} + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(buff) + assert.ErrorIs(t, err, errBufferLenMismatch) + assert.Contains(t, err.Error(), "actual 0, declared 1") + assert.Zero(t, gasLimit) + }) + t.Run("buffer to short for function length should error", func(t *testing.T) { + t.Parallel() + + buff := []byte{ + 1, + 0, 0, 0, 0} + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForLength) + assert.Contains(t, err.Error(), "for function") + assert.Zero(t, gasLimit) + }) + t.Run("buffer to short for function should error", func(t *testing.T) { + t.Parallel() + + buff := []byte{ + 1, + 0, 0, 0, 4, + 0, 0, 0, 5} + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForString) + assert.Contains(t, err.Error(), "for function") + assert.Zero(t, gasLimit) + }) + t.Run("buffer to short for gas limit should error", func(t *testing.T) { + t.Parallel() + + buff := []byte{ + 1, + 0, 0, 0, 14, + 0, 0, 0, 3, 'a', 'b', 'c', + 0, 0, 0, 0, 0, 0, 0, // malformed gas limit (7 bytes for an uint64) + } + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForUint64) + assert.Contains(t, err.Error(), "for gas limit") + assert.Zero(t, gasLimit) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + buff := []byte{ + 1, + 0, 0, 0, 15, + 0, 0, 0, 3, 'a', 'b', 'c', + 0, 0, 1, 2, 3, 4, 5, 6, // gas limit is 1108152157446 + } + + gasLimit, err := codec.ExtractGasLimitFromRawCallData(buff) + assert.Nil(t, err) + assert.Equal(t, uint64(1108152157446), gasLimit) + }) +} + +func TestMultiversxCodec_DecodeProxySCCompleteCallData(t *testing.T) { + t.Parallel() + + codec := MultiversxCodec{} + emptyCompleteCallData := ProxySCCompleteCallData{} + + t.Run("buffer to short for Ethereum address should error", func(t *testing.T) { + t.Parallel() + + buff := []byte{0x01} + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForEthAddress) + assert.Equal(t, emptyCompleteCallData, completeCallData) + }) + t.Run("buffer to short for MultiversX address should error", func(t *testing.T) { + t.Parallel() + + buff := bytes.Repeat([]byte{0x01}, 20) + buff = append(buff, 0x1) + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForMvxAddress) + assert.Equal(t, emptyCompleteCallData, completeCallData) + }) + t.Run("invalid token size bytes should error", func(t *testing.T) { + t.Parallel() + + buff := bytes.Repeat([]byte{0x01}, 20) // Eth address + buff = append(buff, bytes.Repeat([]byte{0x01}, 32)...) // Mvx address + buff = append(buff, []byte{0x00, 0x01, 0x04}...) // invalid token + + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForLength) + assert.Contains(t, err.Error(), "for token") + assert.Equal(t, emptyCompleteCallData, completeCallData) + }) + t.Run("invalid token size should error", func(t *testing.T) { + t.Parallel() + + buff := bytes.Repeat([]byte{0x01}, 20) // Eth address + buff = append(buff, bytes.Repeat([]byte{0x01}, 32)...) // Mvx address + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x02}...) // token length + buff = append(buff, 0x04) // instead of 2 bytes for token we have only one + + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForString) + assert.Contains(t, err.Error(), "for token") + assert.Equal(t, emptyCompleteCallData, completeCallData) + }) + t.Run("invalid big int size should error", func(t *testing.T) { + t.Parallel() + + buff := bytes.Repeat([]byte{0x01}, 20) // Eth address + buff = append(buff, bytes.Repeat([]byte{0x01}, 32)...) // Mvx address + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x02}...) // token size + buff = append(buff, []byte{0x02, 0x03}...) // token + buff = append(buff, []byte{0x00, 0x00, 0x00}...) // invalid amount size + + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForLength) + assert.Contains(t, err.Error(), "for amount") + assert.Equal(t, emptyCompleteCallData, completeCallData) + }) + t.Run("invalid big int bytes should error", func(t *testing.T) { + t.Parallel() + + buff := bytes.Repeat([]byte{0x01}, 20) // Eth address + buff = append(buff, bytes.Repeat([]byte{0x01}, 32)...) // Mvx address + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x02}...) // token size + buff = append(buff, []byte{0x02, 0x03}...) // token + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x05}...) // amount size + buff = append(buff, []byte{0x00}...) // invalid amount + + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForBigInt) + assert.Contains(t, err.Error(), "for amount") + assert.Equal(t, emptyCompleteCallData, completeCallData) + }) + t.Run("invalid nonce should error", func(t *testing.T) { + t.Parallel() + + buff := bytes.Repeat([]byte{0x01}, 20) // Eth address + buff = append(buff, bytes.Repeat([]byte{0x01}, 32)...) // Mvx address + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x02}...) // token size + buff = append(buff, []byte{0x02, 0x03}...) // token + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x01}...) // amount size + buff = append(buff, []byte{0x01}...) // amount + buff = append(buff, []byte{0x03, 0x04}...) // invalid nonce + + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.ErrorIs(t, err, errBufferTooShortForUint64) + assert.Contains(t, err.Error(), "for nonce") + assert.Equal(t, emptyCompleteCallData, completeCallData) + }) + t.Run("invalid marker should work", func(t *testing.T) { + t.Parallel() + + buff := bytes.Repeat([]byte{0x01}, 20) // Eth address + buff = append(buff, bytes.Repeat([]byte{0x01}, 32)...) // Mvx address + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x02}...) // token size + buff = append(buff, []byte{0x02, 0x03}...) // token + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x00}...) // amount size = 0 => amount = 0 + buff = append(buff, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}...) // nonce + buff = append(buff, 0x03) // invalid marker + + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.Nil(t, err) + expectedCallData := ProxySCCompleteCallData{ + RawCallData: []byte{0x03}, + From: common.HexToAddress("0x0101010101010101010101010101010101010101"), + To: data.NewAddressFromBytes(bytes.Repeat([]byte{0x01}, 32)), + Token: string([]byte{2, 3}), + Amount: big.NewInt(0), + Nonce: 1, + } + assert.Equal(t, expectedCallData, completeCallData) + }) + t.Run("should work", func(t *testing.T) { + t.Parallel() + + // |--------------FROM---------------------|---------------------TO----------------------------------------|-len-TK|------ETHUSDC-0ae8ee-------|-len-A-|20k|--tx-nonce=1---|-raw-call-data| + hexedData := "880ec53af800b5cd051531672ef4fc4de233bd5d00000000000000000500871bc8f6332939a55a80b23012564523bea3291fa4370000000e455448555344432d306165386565000000024e200000000000000001414243" + buff, err := hex.DecodeString(hexedData) + require.Nil(t, err) + + expectedCompleteCallData := createTestProxySCCompleteCallData() + completeCallData, err := codec.DecodeProxySCCompleteCallData(buff) + assert.Equal(t, expectedCompleteCallData, completeCallData) + assert.Nil(t, err) + }) +} diff --git a/parsers/types.go b/parsers/types.go new file mode 100644 index 00000000..1b8d9326 --- /dev/null +++ b/parsers/types.go @@ -0,0 +1,53 @@ +package parsers + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-chain-core-go/core/check" + "github.com/multiversx/mx-sdk-go/core" +) + +// CallData defines the struct holding SC call data parameters +type CallData struct { + Type byte + Function string + GasLimit uint64 + Arguments []string +} + +// ProxySCCompleteCallData defines the struct holding Proxy SC complete call data +type ProxySCCompleteCallData struct { + RawCallData []byte + From common.Address + To core.AddressHandler + Token string + Amount *big.Int + Nonce uint64 +} + +// String returns the human-readable string version of the call data +func (callData ProxySCCompleteCallData) String() string { + toString := "" + var err error + if !check.IfNil(callData.To) { + toString, err = callData.To.AddressAsBech32String() + if err != nil { + toString = "" + } + } + amountString := "" + if callData.Amount != nil { + amountString = callData.Amount.String() + } + + return fmt.Sprintf("Eth address: %s, MvX address: %s, token: %s, amount: %s, nonce: %d, raw call data: %x", + callData.From.String(), + toString, + callData.Token, + amountString, + callData.Nonce, + callData.RawCallData, + ) +} diff --git a/parsers/types_test.go b/parsers/types_test.go new file mode 100644 index 00000000..e6403d73 --- /dev/null +++ b/parsers/types_test.go @@ -0,0 +1,62 @@ +package parsers + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-sdk-go/data" + "github.com/stretchr/testify/assert" +) + +func TestProxySCCompleteCallData_String(t *testing.T) { + t.Parallel() + + t.Run("nil fields should work", func(t *testing.T) { + t.Parallel() + + callData := ProxySCCompleteCallData{ + RawCallData: []byte{65, 66, 67}, + From: common.Address{}, + To: nil, + Token: "tkn", + Amount: nil, + Nonce: 1, + } + + expectedString := "Eth address: 0x0000000000000000000000000000000000000000, MvX address: , token: tkn, amount: , nonce: 1, raw call data: 414243" + assert.Equal(t, expectedString, callData.String()) + }) + t.Run("not a Valid MvX address should work", func(t *testing.T) { + t.Parallel() + + callData := ProxySCCompleteCallData{ + RawCallData: []byte{65, 66, 67}, + From: common.Address{}, + To: data.NewAddressFromBytes([]byte{0x1, 0x2}), + Token: "tkn", + Nonce: 1, + } + + expectedString := "Eth address: 0x0000000000000000000000000000000000000000, MvX address: , token: tkn, amount: , nonce: 1, raw call data: 414243" + assert.Equal(t, expectedString, callData.String()) + }) + t.Run("with valid data should work", func(t *testing.T) { + t.Parallel() + + callData := ProxySCCompleteCallData{ + RawCallData: []byte{65, 66, 67}, + From: common.Address{}, + Token: "tkn", + Amount: big.NewInt(37), + Nonce: 1, + } + ethUnhexed, _ := hex.DecodeString("880ec53af800b5cd051531672ef4fc4de233bd5d") + callData.From.SetBytes(ethUnhexed) + callData.To, _ = data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqsudu3a3n9yu62k5qkgcpy4j9ywl2x2gl5smsy7t4uv") + + expectedString := "Eth address: 0x880EC53Af800b5Cd051531672EF4fc4De233bD5d, MvX address: erd1qqqqqqqqqqqqqpgqsudu3a3n9yu62k5qkgcpy4j9ywl2x2gl5smsy7t4uv, token: tkn, amount: 37, nonce: 1, raw call data: 414243" + assert.Equal(t, expectedString, callData.String()) + }) +} diff --git a/scCallsExecutor.Dockerfile b/scCallsExecutor.Dockerfile new file mode 100644 index 00000000..f07085a6 --- /dev/null +++ b/scCallsExecutor.Dockerfile @@ -0,0 +1,26 @@ +FROM golang:1.20.7-bookworm AS builder +LABEL description="This Docker image builds the SC calls executor binary." + +WORKDIR /multiversx +COPY . . + +RUN go mod tidy + +WORKDIR /multiversx/cmd/scCallsExecutor + +RUN APPVERSION=$(git describe --tags --long --always | tail -c 11) && echo "package main\n\nfunc init() {\n\tappVersion = \"${APPVERSION}\"\n}" > local.go +RUN go mod tidy +RUN go build + +FROM ubuntu:22.04 AS runner +LABEL description="This Docker image runs SC calls executor binary." + +RUN apt-get update \ + && apt-get -y install git \ + && apt-get clean + +COPY --from=builder /multiversx/cmd/scCallsExecutor /multiversx + +WORKDIR /multiversx + +ENTRYPOINT ["./scCallsExecutor"] diff --git a/testsCommon/addressGenerators.go b/testsCommon/addressGenerators.go index 2cbbf4f8..e0d8c55d 100644 --- a/testsCommon/addressGenerators.go +++ b/testsCommon/addressGenerators.go @@ -23,3 +23,13 @@ func CreateRandomMultiversXAddress() sdkCore.AddressHandler { return data.NewAddressFromBytes(buff) } + +// CreateRandomMultiversXSCAddress will create a random MultiversX smart contract address +func CreateRandomMultiversXSCAddress() sdkCore.AddressHandler { + buff := make([]byte, 22) + _, _ = rand.Read(buff) + + firstPart := append(make([]byte, 8), []byte{5, 0}...) + + return data.NewAddressFromBytes(append(firstPart, buff...)) +} diff --git a/testsCommon/balanceValidatorStub.go b/testsCommon/balanceValidatorStub.go new file mode 100644 index 00000000..11fe6120 --- /dev/null +++ b/testsCommon/balanceValidatorStub.go @@ -0,0 +1,28 @@ +package testsCommon + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" +) + +// BalanceValidatorStub - +type BalanceValidatorStub struct { + CheckTokenCalled func(ctx context.Context, ethToken common.Address, mvxToken []byte, amount *big.Int, direction batchProcessor.Direction) error +} + +// CheckToken - +func (stub *BalanceValidatorStub) CheckToken(ctx context.Context, ethToken common.Address, mvxToken []byte, amount *big.Int, direction batchProcessor.Direction) error { + if stub.CheckTokenCalled != nil { + return stub.CheckTokenCalled(ctx, ethToken, mvxToken, amount, direction) + } + + return nil +} + +// IsInterfaceNil - +func (stub *BalanceValidatorStub) IsInterfaceNil() bool { + return stub == nil +} diff --git a/testsCommon/batchValidatorStub.go b/testsCommon/batchValidatorStub.go deleted file mode 100644 index 12a18c39..00000000 --- a/testsCommon/batchValidatorStub.go +++ /dev/null @@ -1,26 +0,0 @@ -package testsCommon - -import ( - "context" - "errors" - - "github.com/multiversx/mx-bridge-eth-go/clients" -) - -// BatchValidatorStub - -type BatchValidatorStub struct { - ValidateBatchCalled func(ctx context.Context, batch *clients.TransferBatch) (bool, error) -} - -// ValidateBatch - -func (bvs *BatchValidatorStub) ValidateBatch(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - if bvs.ValidateBatchCalled != nil { - return bvs.ValidateBatchCalled(ctx, batch) - } - return false, errors.New("method not implemented") -} - -// IsInterfaceNil - -func (bvs *BatchValidatorStub) IsInterfaceNil() bool { - return bvs == nil -} diff --git a/testsCommon/bridge/bridgeExecutorStub.go b/testsCommon/bridge/bridgeExecutorStub.go index 17ce991d..787eefae 100644 --- a/testsCommon/bridge/bridgeExecutorStub.go +++ b/testsCommon/bridge/bridgeExecutorStub.go @@ -3,11 +3,14 @@ package bridge import ( "context" "fmt" + "math/big" "runtime" "strings" "sync" - "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/ethereum/go-ethereum/common" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" logger "github.com/multiversx/mx-chain-logger-go" ) @@ -19,9 +22,9 @@ type BridgeExecutorStub struct { PrintInfoCalled func(logLevel logger.LogLevel, message string, extras ...interface{}) MyTurnAsLeaderCalled func() bool - GetBatchFromMultiversXCalled func(ctx context.Context) (*clients.TransferBatch, error) - StoreBatchFromMultiversXCalled func(batch *clients.TransferBatch) error - GetStoredBatchCalled func() *clients.TransferBatch + GetBatchFromMultiversXCalled func(ctx context.Context) (*bridgeCore.TransferBatch, error) + StoreBatchFromMultiversXCalled func(batch *bridgeCore.TransferBatch) error + GetStoredBatchCalled func() *bridgeCore.TransferBatch GetLastExecutedEthBatchIDFromMultiversXCalled func(ctx context.Context) (uint64, error) VerifyLastDepositNonceExecutedOnEthereumBatchCalled func(ctx context.Context) error GetAndStoreActionIDForProposeTransferOnMultiversXCalled func(ctx context.Context) (uint64, error) @@ -52,9 +55,9 @@ type BridgeExecutorStub struct { ProcessMaxQuorumRetriesOnEthereumCalled func() bool ResetRetriesCountOnEthereumCalled func() ClearStoredP2PSignaturesForEthereumCalled func() - ValidateBatchCalled func(ctx context.Context, batch *clients.TransferBatch) (bool, error) CheckMultiversXClientAvailabilityCalled func(ctx context.Context) error CheckEthereumClientAvailabilityCalled func(ctx context.Context) error + CheckAvailableTokensCalled func(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error } // NewBridgeExecutorStub creates a new BridgeExecutorStub instance @@ -83,7 +86,7 @@ func (stub *BridgeExecutorStub) MyTurnAsLeader() bool { } // GetBatchFromMultiversX - -func (stub *BridgeExecutorStub) GetBatchFromMultiversX(ctx context.Context) (*clients.TransferBatch, error) { +func (stub *BridgeExecutorStub) GetBatchFromMultiversX(ctx context.Context) (*bridgeCore.TransferBatch, error) { stub.incrementFunctionCounter() if stub.GetBatchFromMultiversXCalled != nil { return stub.GetBatchFromMultiversXCalled(ctx) @@ -92,7 +95,7 @@ func (stub *BridgeExecutorStub) GetBatchFromMultiversX(ctx context.Context) (*cl } // StoreBatchFromMultiversX - -func (stub *BridgeExecutorStub) StoreBatchFromMultiversX(batch *clients.TransferBatch) error { +func (stub *BridgeExecutorStub) StoreBatchFromMultiversX(batch *bridgeCore.TransferBatch) error { stub.incrementFunctionCounter() if stub.StoreBatchFromMultiversXCalled != nil { return stub.StoreBatchFromMultiversXCalled(batch) @@ -101,7 +104,7 @@ func (stub *BridgeExecutorStub) StoreBatchFromMultiversX(batch *clients.Transfer } // GetStoredBatch - -func (stub *BridgeExecutorStub) GetStoredBatch() *clients.TransferBatch { +func (stub *BridgeExecutorStub) GetStoredBatch() *bridgeCore.TransferBatch { stub.incrementFunctionCounter() if stub.GetStoredBatchCalled != nil { return stub.GetStoredBatchCalled() @@ -373,14 +376,6 @@ func (stub *BridgeExecutorStub) ClearStoredP2PSignaturesForEthereum() { } } -// ValidateBatch - -func (stub *BridgeExecutorStub) ValidateBatch(ctx context.Context, batch *clients.TransferBatch) (bool, error) { - if stub.ValidateBatchCalled != nil { - return stub.ValidateBatchCalled(ctx, batch) - } - return false, notImplemented -} - // CheckMultiversXClientAvailability - func (stub *BridgeExecutorStub) CheckMultiversXClientAvailability(ctx context.Context) error { if stub.CheckMultiversXClientAvailabilityCalled != nil { @@ -421,3 +416,12 @@ func (stub *BridgeExecutorStub) GetFunctionCounter(function string) int { return stub.functionCalledCounter[function] } + +// CheckAvailableTokens - +func (stub *BridgeExecutorStub) CheckAvailableTokens(ctx context.Context, ethTokens []common.Address, mvxTokens [][]byte, amounts []*big.Int, direction batchProcessor.Direction) error { + if stub.CheckAvailableTokensCalled != nil { + return stub.CheckAvailableTokensCalled(ctx, ethTokens, mvxTokens, amounts, direction) + } + + return nil +} diff --git a/testsCommon/bridge/constants.go b/testsCommon/bridge/constants.go index b8d37008..d6a661e7 100644 --- a/testsCommon/bridge/constants.go +++ b/testsCommon/bridge/constants.go @@ -1,5 +1,35 @@ package bridge -import "errors" +import ( + "errors" +) var notImplemented = errors.New("method not implemented") + +// CallDataMock - +var CallDataMock = func() []byte { + b := []byte{ + 1, + 0, 0, 0, 28, + 0, 0, 0, 3, 'a', 'b', 'c', + 0x00, 0x00, 0x00, 0x00, 0x1D, 0xCD, 0x65, 0x00, // gas limit + 0, 0, 0, 1, // numArguments + 0, 0, 0, 5, // argument 0 length + 'd', 'e', 'f', 'g', 'h', // argument 0 data + } + + return b +}() + +// EthCallDataMock - +var EthCallDataMock = func() []byte { + b := []byte{ + 0, 0, 0, 3, 'a', 'b', 'c', + 0x00, 0x00, 0x00, 0x00, 0x1D, 0xCD, 0x65, 0x00, // gas limit + 0, 0, 0, 1, // numArguments + 0, 0, 0, 5, // argument 0 length + 'd', 'e', 'f', 'g', 'h', // argument 0 data + } + + return b +}() diff --git a/testsCommon/bridge/cryptoHandlerStub.go b/testsCommon/bridge/cryptoHandlerStub.go new file mode 100644 index 00000000..262ce7ff --- /dev/null +++ b/testsCommon/bridge/cryptoHandlerStub.go @@ -0,0 +1,48 @@ +package bridge + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" +) + +// CryptoHandlerStub - +type CryptoHandlerStub struct { + SignCalled func(msgHash common.Hash) ([]byte, error) + GetAddressCalled func() common.Address + CreateKeyedTransactorCalled func(chainId *big.Int) (*bind.TransactOpts, error) +} + +// Sign - +func (stub *CryptoHandlerStub) Sign(msgHash common.Hash) ([]byte, error) { + if stub.SignCalled != nil { + return stub.SignCalled(msgHash) + } + + return make([]byte, 0), nil +} + +// GetAddress - +func (stub *CryptoHandlerStub) GetAddress() common.Address { + if stub.GetAddressCalled != nil { + return stub.GetAddressCalled() + } + + return common.BytesToAddress(make([]byte, 0)) +} + +// CreateKeyedTransactor - +func (stub *CryptoHandlerStub) CreateKeyedTransactor(chainId *big.Int) (*bind.TransactOpts, error) { + if stub.CreateKeyedTransactorCalled != nil { + return stub.CreateKeyedTransactorCalled(chainId) + } + + return nil, fmt.Errorf("not implemented") +} + +// IsInterfaceNil - +func (stub *CryptoHandlerStub) IsInterfaceNil() bool { + return stub == nil +} diff --git a/testsCommon/bridge/dataGetterStub.go b/testsCommon/bridge/dataGetterStub.go index 5badf503..f3209009 100644 --- a/testsCommon/bridge/dataGetterStub.go +++ b/testsCommon/bridge/dataGetterStub.go @@ -9,6 +9,7 @@ type DataGetterStub struct { GetTokenIdForErc20AddressCalled func(ctx context.Context, erc20Address []byte) ([][]byte, error) GetERC20AddressForTokenIdCalled func(ctx context.Context, tokenId []byte) ([][]byte, error) GetAllStakedRelayersCalled func(ctx context.Context) ([][]byte, error) + GetAllKnownTokensCalled func(ctx context.Context) ([][]byte, error) } // GetTokenIdForErc20Address - @@ -36,6 +37,15 @@ func (stub *DataGetterStub) GetAllStakedRelayers(ctx context.Context) ([][]byte, return make([][]byte, 0), nil } +// GetAllKnownTokens - +func (stub *DataGetterStub) GetAllKnownTokens(ctx context.Context) ([][]byte, error) { + if stub.GetAllKnownTokensCalled != nil { + return stub.GetAllKnownTokensCalled(ctx) + } + + return make([][]byte, 0), nil +} + // IsInterfaceNil - func (stub *DataGetterStub) IsInterfaceNil() bool { return stub == nil diff --git a/testsCommon/bridge/erc20ContractsHolderStub.go b/testsCommon/bridge/erc20ContractsHolderStub.go index 35cffe97..ba52db19 100644 --- a/testsCommon/bridge/erc20ContractsHolderStub.go +++ b/testsCommon/bridge/erc20ContractsHolderStub.go @@ -10,6 +10,7 @@ import ( // ERC20ContractsHolderStub - type ERC20ContractsHolderStub struct { BalanceOfCalled func(ctx context.Context, erc20Address common.Address, address common.Address) (*big.Int, error) + DecimalsCalled func(ctx context.Context, erc20Address common.Address) (uint8, error) } // BalanceOf - @@ -21,6 +22,15 @@ func (stub *ERC20ContractsHolderStub) BalanceOf(ctx context.Context, erc20Addres return big.NewInt(0), nil } +// Decimals - +func (stub *ERC20ContractsHolderStub) Decimals(ctx context.Context, erc20Address common.Address) (uint8, error) { + if stub.DecimalsCalled != nil { + return stub.DecimalsCalled(ctx, erc20Address) + } + + return 0, nil +} + // IsInterfaceNil - func (stub *ERC20ContractsHolderStub) IsInterfaceNil() bool { return stub == nil diff --git a/testsCommon/bridge/ethereumClientStub.go b/testsCommon/bridge/ethereumClientStub.go index f715fc95..3d55eb47 100644 --- a/testsCommon/bridge/ethereumClientStub.go +++ b/testsCommon/bridge/ethereumClientStub.go @@ -5,29 +5,39 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/multiversx/mx-bridge-eth-go/clients" + "github.com/multiversx/mx-bridge-eth-go/clients/ethereum/contract" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/core/batchProcessor" ) // EthereumClientStub - type EthereumClientStub struct { - GetBatchCalled func(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) + GetBatchCalled func(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) WasExecutedCalled func(ctx context.Context, batchID uint64) (bool, error) - GenerateMessageHashCalled func(batch *clients.TransferBatch) (common.Hash, error) + GenerateMessageHashCalled func(batch *batchProcessor.ArgListsBatch, batchID uint64) (common.Hash, error) BroadcastSignatureForMessageHashCalled func(msgHash common.Hash) - ExecuteTransferCalled func(ctx context.Context, msgHash common.Hash, batch *clients.TransferBatch, quorum int) (string, error) + ExecuteTransferCalled func(ctx context.Context, msgHash common.Hash, batch *batchProcessor.ArgListsBatch, batchId uint64, quorum int) (string, error) CheckClientAvailabilityCalled func(ctx context.Context) error GetTransactionsStatusesCalled func(ctx context.Context, batchId uint64) ([]byte, error) GetQuorumSizeCalled func(ctx context.Context) (*big.Int, error) IsQuorumReachedCalled func(ctx context.Context, msgHash common.Hash) (bool, error) + GetBatchSCMetadataCalled func(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) + CheckRequiredBalanceCalled func(ctx context.Context, erc20Address common.Address, value *big.Int) error + TotalBalancesCalled func(ctx context.Context, account common.Address) (*big.Int, error) + MintBalancesCalled func(ctx context.Context, account common.Address) (*big.Int, error) + BurnBalancesCalled func(ctx context.Context, account common.Address) (*big.Int, error) + MintBurnTokensCalled func(ctx context.Context, account common.Address) (bool, error) + NativeTokensCalled func(ctx context.Context, account common.Address) (bool, error) + WhitelistedTokensCalled func(ctx context.Context, account common.Address) (bool, error) } // GetBatch - -func (stub *EthereumClientStub) GetBatch(ctx context.Context, nonce uint64) (*clients.TransferBatch, error) { +func (stub *EthereumClientStub) GetBatch(ctx context.Context, nonce uint64) (*bridgeCore.TransferBatch, bool, error) { if stub.GetBatchCalled != nil { return stub.GetBatchCalled(ctx, nonce) } - return nil, errNotImplemented + return nil, false, errNotImplemented } // WasExecuted - @@ -40,9 +50,9 @@ func (stub *EthereumClientStub) WasExecuted(ctx context.Context, batchID uint64) } // GenerateMessageHash - -func (stub *EthereumClientStub) GenerateMessageHash(batch *clients.TransferBatch) (common.Hash, error) { +func (stub *EthereumClientStub) GenerateMessageHash(batch *batchProcessor.ArgListsBatch, batchID uint64) (common.Hash, error) { if stub.GenerateMessageHashCalled != nil { - return stub.GenerateMessageHashCalled(batch) + return stub.GenerateMessageHashCalled(batch, batchID) } return common.Hash{}, errNotImplemented @@ -56,9 +66,9 @@ func (stub *EthereumClientStub) BroadcastSignatureForMessageHash(msgHash common. } // ExecuteTransfer - -func (stub *EthereumClientStub) ExecuteTransfer(ctx context.Context, msgHash common.Hash, batch *clients.TransferBatch, quorum int) (string, error) { +func (stub *EthereumClientStub) ExecuteTransfer(ctx context.Context, msgHash common.Hash, batch *batchProcessor.ArgListsBatch, batchId uint64, quorum int) (string, error) { if stub.ExecuteTransferCalled != nil { - return stub.ExecuteTransferCalled(ctx, msgHash, batch, quorum) + return stub.ExecuteTransferCalled(ctx, msgHash, batch, batchId, quorum) } return "", errNotImplemented @@ -70,7 +80,7 @@ func (stub *EthereumClientStub) CheckClientAvailability(ctx context.Context) err return stub.CheckClientAvailabilityCalled(ctx) } - return nil + return errNotImplemented } // GetTransactionsStatuses - @@ -100,6 +110,78 @@ func (stub *EthereumClientStub) IsQuorumReached(ctx context.Context, msgHash com return false, errNotImplemented } +// GetBatchSCMetadata - +func (stub *EthereumClientStub) GetBatchSCMetadata(ctx context.Context, nonce uint64, blockNumber int64) ([]*contract.ERC20SafeERC20SCDeposit, error) { + if stub.GetBatchSCMetadataCalled != nil { + return stub.GetBatchSCMetadataCalled(ctx, nonce, blockNumber) + } + + return []*contract.ERC20SafeERC20SCDeposit{}, errNotImplemented +} + +// CheckRequiredBalance - +func (stub *EthereumClientStub) CheckRequiredBalance(ctx context.Context, erc20Address common.Address, value *big.Int) error { + if stub.CheckRequiredBalanceCalled != nil { + return stub.CheckRequiredBalanceCalled(ctx, erc20Address, value) + } + + return errNotImplemented +} + +// TotalBalances - +func (stub *EthereumClientStub) TotalBalances(ctx context.Context, account common.Address) (*big.Int, error) { + if stub.TotalBalancesCalled != nil { + return stub.TotalBalancesCalled(ctx, account) + } + + return nil, errNotImplemented +} + +// MintBalances - +func (stub *EthereumClientStub) MintBalances(ctx context.Context, account common.Address) (*big.Int, error) { + if stub.MintBalancesCalled != nil { + return stub.MintBalancesCalled(ctx, account) + } + + return nil, errNotImplemented +} + +// BurnBalances - +func (stub *EthereumClientStub) BurnBalances(ctx context.Context, account common.Address) (*big.Int, error) { + if stub.BurnBalancesCalled != nil { + return stub.BurnBalancesCalled(ctx, account) + } + + return nil, errNotImplemented +} + +// MintBurnTokens - +func (stub *EthereumClientStub) MintBurnTokens(ctx context.Context, account common.Address) (bool, error) { + if stub.MintBurnTokensCalled != nil { + return stub.MintBurnTokensCalled(ctx, account) + } + + return false, errNotImplemented +} + +// NativeTokens - +func (stub *EthereumClientStub) NativeTokens(ctx context.Context, account common.Address) (bool, error) { + if stub.NativeTokensCalled != nil { + return stub.NativeTokensCalled(ctx, account) + } + + return false, errNotImplemented +} + +// WhitelistedTokens - +func (stub *EthereumClientStub) WhitelistedTokens(ctx context.Context, account common.Address) (bool, error) { + if stub.WhitelistedTokensCalled != nil { + return stub.WhitelistedTokensCalled(ctx, account) + } + + return false, errNotImplemented +} + // IsInterfaceNil - func (stub *EthereumClientStub) IsInterfaceNil() bool { return stub == nil diff --git a/testsCommon/bridge/ethereumClientWrapperStub.go b/testsCommon/bridge/ethereumClientWrapperStub.go index 1a476500..b96615ca 100644 --- a/testsCommon/bridge/ethereumClientWrapperStub.go +++ b/testsCommon/bridge/ethereumClientWrapperStub.go @@ -5,6 +5,7 @@ import ( "errors" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" @@ -15,8 +16,8 @@ import ( // EthereumClientWrapperStub - type EthereumClientWrapperStub struct { core.StatusHandler - GetBatchCalled func(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) - GetBatchDepositsCalled func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) + GetBatchCalled func(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) + GetBatchDepositsCalled func(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) GetRelayersCalled func(ctx context.Context) ([]common.Address, error) WasBatchExecutedCalled func(ctx context.Context, batchNonce *big.Int) (bool, error) ChainIDCalled func(ctx context.Context) (*big.Int, error) @@ -25,8 +26,14 @@ type EthereumClientWrapperStub struct { ExecuteTransferCalled func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) QuorumCalled func(ctx context.Context) (*big.Int, error) - GetStatusesAfterExecutionCalled func(ctx context.Context, batchID *big.Int) ([]byte, error) + GetStatusesAfterExecutionCalled func(ctx context.Context, batchID *big.Int) ([]byte, bool, error) BalanceAtCalled func(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + TotalBalancesCalled func(ctx context.Context, account common.Address) (*big.Int, error) + MintBalancesCalled func(ctx context.Context, account common.Address) (*big.Int, error) + BurnBalancesCalled func(ctx context.Context, account common.Address) (*big.Int, error) + MintBurnTokensCalled func(ctx context.Context, account common.Address) (bool, error) + NativeTokensCalled func(ctx context.Context, account common.Address) (bool, error) + WhitelistedTokensCalled func(ctx context.Context, account common.Address) (bool, error) SetIntMetricCalled func(metric string, value int) AddIntMetricCalled func(metric string, delta int) @@ -34,6 +41,7 @@ type EthereumClientWrapperStub struct { GetAllMetricsCalled func() core.GeneralMetrics NameCalled func() string IsPausedCalled func(ctx context.Context) (bool, error) + FilterLogsCalled func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } // SetIntMetric - @@ -91,21 +99,21 @@ func (stub *EthereumClientWrapperStub) Name() string { } // GetBatch - -func (stub *EthereumClientWrapperStub) GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, error) { +func (stub *EthereumClientWrapperStub) GetBatch(ctx context.Context, batchNonce *big.Int) (contract.Batch, bool, error) { if stub.GetBatchCalled != nil { return stub.GetBatchCalled(ctx, batchNonce) } - return contract.Batch{}, nil + return contract.Batch{}, false, nil } // GetBatchDeposits - -func (stub *EthereumClientWrapperStub) GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, error) { +func (stub *EthereumClientWrapperStub) GetBatchDeposits(ctx context.Context, batchNonce *big.Int) ([]contract.Deposit, bool, error) { if stub.GetBatchCalled != nil { return stub.GetBatchDepositsCalled(ctx, batchNonce) } - return make([]contract.Deposit, 0), nil + return make([]contract.Deposit, 0), false, nil } // GetRelayers - @@ -172,12 +180,12 @@ func (stub *EthereumClientWrapperStub) Quorum(ctx context.Context) (*big.Int, er } // GetStatusesAfterExecution - -func (stub *EthereumClientWrapperStub) GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, error) { +func (stub *EthereumClientWrapperStub) GetStatusesAfterExecution(ctx context.Context, batchID *big.Int) ([]byte, bool, error) { if stub.GetStatusesAfterExecutionCalled != nil { return stub.GetStatusesAfterExecutionCalled(ctx, batchID) } - return make([]byte, 0), nil + return make([]byte, 0), false, nil } // BalanceAt - @@ -189,6 +197,15 @@ func (stub *EthereumClientWrapperStub) BalanceAt(ctx context.Context, account co return big.NewInt(0), nil } +// FilterLogs - +func (stub *EthereumClientWrapperStub) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if stub.FilterLogsCalled != nil { + return stub.FilterLogsCalled(ctx, q) + } + + return []types.Log{}, nil +} + // IsPaused - func (stub *EthereumClientWrapperStub) IsPaused(ctx context.Context) (bool, error) { if stub.IsPausedCalled != nil { @@ -198,6 +215,60 @@ func (stub *EthereumClientWrapperStub) IsPaused(ctx context.Context) (bool, erro return false, nil } +// TotalBalances - +func (stub *EthereumClientWrapperStub) TotalBalances(ctx context.Context, account common.Address) (*big.Int, error) { + if stub.TotalBalancesCalled != nil { + return stub.TotalBalancesCalled(ctx, account) + } + + return big.NewInt(0), nil +} + +// MintBalances - +func (stub *EthereumClientWrapperStub) MintBalances(ctx context.Context, account common.Address) (*big.Int, error) { + if stub.MintBalancesCalled != nil { + return stub.MintBalancesCalled(ctx, account) + } + + return big.NewInt(0), nil +} + +// BurnBalances - +func (stub *EthereumClientWrapperStub) BurnBalances(ctx context.Context, account common.Address) (*big.Int, error) { + if stub.BurnBalancesCalled != nil { + return stub.BurnBalancesCalled(ctx, account) + } + + return big.NewInt(0), nil +} + +// MintBurnTokens - +func (stub *EthereumClientWrapperStub) MintBurnTokens(ctx context.Context, account common.Address) (bool, error) { + if stub.MintBurnTokensCalled != nil { + return stub.MintBurnTokensCalled(ctx, account) + } + + return false, nil +} + +// NativeTokens - +func (stub *EthereumClientWrapperStub) NativeTokens(ctx context.Context, account common.Address) (bool, error) { + if stub.NativeTokensCalled != nil { + return stub.NativeTokensCalled(ctx, account) + } + + return false, nil +} + +// WhitelistedTokens - +func (stub *EthereumClientWrapperStub) WhitelistedTokens(ctx context.Context, account common.Address) (bool, error) { + if stub.WhitelistedTokensCalled != nil { + return stub.WhitelistedTokensCalled(ctx, account) + } + + return false, nil +} + // IsInterfaceNil - func (stub *EthereumClientWrapperStub) IsInterfaceNil() bool { return stub == nil diff --git a/testsCommon/bridge/multiSigContractStub.go b/testsCommon/bridge/multiSigContractStub.go index 4f57b4ad..8286ea80 100644 --- a/testsCommon/bridge/multiSigContractStub.go +++ b/testsCommon/bridge/multiSigContractStub.go @@ -11,33 +11,33 @@ import ( // MultiSigContractStub - type MultiSigContractStub struct { - GetBatchCalled func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) - GetBatchDepositsCalled func(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) + GetBatchCalled func(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, bool, error) + GetBatchDepositsCalled func(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, bool, error) GetRelayersCalled func(opts *bind.CallOpts) ([]common.Address, error) WasBatchExecutedCalled func(opts *bind.CallOpts, batchNonce *big.Int) (bool, error) ExecuteTransferCalled func(opts *bind.TransactOpts, tokens []common.Address, recipients []common.Address, amounts []*big.Int, nonces []*big.Int, batchNonce *big.Int, signatures [][]byte) (*types.Transaction, error) QuorumCalled func(opts *bind.CallOpts) (*big.Int, error) - GetStatusesAfterExecutionCalled func(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) + GetStatusesAfterExecutionCalled func(opts *bind.CallOpts, batchID *big.Int) ([]byte, bool, error) PausedCalled func(opts *bind.CallOpts) (bool, error) } // GetBatch - -func (stub *MultiSigContractStub) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, error) { +func (stub *MultiSigContractStub) GetBatch(opts *bind.CallOpts, batchNonce *big.Int) (contract.Batch, bool, error) { if stub.GetBatchCalled != nil { return stub.GetBatchCalled(opts, batchNonce) } - return contract.Batch{}, nil + return contract.Batch{}, false, nil } // GetBatchDeposits - -func (stub *MultiSigContractStub) GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, error) { +func (stub *MultiSigContractStub) GetBatchDeposits(opts *bind.CallOpts, batchNonce *big.Int) ([]contract.Deposit, bool, error) { if stub.GetBatchCalled != nil { return stub.GetBatchDepositsCalled(opts, batchNonce) } - return make([]contract.Deposit, 0), nil + return make([]contract.Deposit, 0), false, nil } // GetRelayers - @@ -85,12 +85,12 @@ func (stub *MultiSigContractStub) Quorum(opts *bind.CallOpts) (*big.Int, error) } // GetStatusesAfterExecution - -func (stub *MultiSigContractStub) GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, error) { +func (stub *MultiSigContractStub) GetStatusesAfterExecution(opts *bind.CallOpts, batchID *big.Int) ([]byte, bool, error) { if stub.GetStatusesAfterExecutionCalled != nil { return stub.GetStatusesAfterExecutionCalled(opts, batchID) } - return make([]byte, 0), nil + return make([]byte, 0), false, nil } // Paused - diff --git a/testsCommon/bridge/multiversxClientStub.go b/testsCommon/bridge/multiversxClientStub.go index 6c9a66ef..aeba233c 100644 --- a/testsCommon/bridge/multiversxClientStub.go +++ b/testsCommon/bridge/multiversxClientStub.go @@ -3,40 +3,58 @@ package bridge import ( "context" "errors" + "math/big" - "github.com/multiversx/mx-bridge-eth-go/clients" + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" ) var errNotImplemented = errors.New("not implemented") // MultiversXClientStub - type MultiversXClientStub struct { - GetPendingCalled func(ctx context.Context) (*clients.TransferBatch, error) + GetPendingBatchCalled func(ctx context.Context) (*bridgeCore.TransferBatch, error) + GetBatchCalled func(ctx context.Context, batchID uint64) (*bridgeCore.TransferBatch, error) GetCurrentBatchAsDataBytesCalled func(ctx context.Context) ([][]byte, error) - WasProposedTransferCalled func(ctx context.Context, batch *clients.TransferBatch) (bool, error) + WasProposedTransferCalled func(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) QuorumReachedCalled func(ctx context.Context, actionID uint64) (bool, error) WasExecutedCalled func(ctx context.Context, actionID uint64) (bool, error) - GetActionIDForProposeTransferCalled func(ctx context.Context, batch *clients.TransferBatch) (uint64, error) - WasProposedSetStatusCalled func(ctx context.Context, batch *clients.TransferBatch) (bool, error) + GetActionIDForProposeTransferCalled func(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) + WasProposedSetStatusCalled func(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) GetTransactionsStatusesCalled func(ctx context.Context, batchID uint64) ([]byte, error) - GetActionIDForSetStatusOnPendingTransferCalled func(ctx context.Context, batch *clients.TransferBatch) (uint64, error) + GetActionIDForSetStatusOnPendingTransferCalled func(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) GetLastExecutedEthBatchIDCalled func(ctx context.Context) (uint64, error) GetLastExecutedEthTxIDCalled func(ctx context.Context) (uint64, error) GetCurrentNonceCalled func(ctx context.Context) (uint64, error) - ProposeSetStatusCalled func(ctx context.Context, batch *clients.TransferBatch) (string, error) - ResolveNewDepositsCalled func(ctx context.Context, batch *clients.TransferBatch) error - ProposeTransferCalled func(ctx context.Context, batch *clients.TransferBatch) (string, error) + ProposeSetStatusCalled func(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) + ResolveNewDepositsCalled func(ctx context.Context, batch *bridgeCore.TransferBatch) error + ProposeTransferCalled func(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) SignCalled func(ctx context.Context, actionID uint64) (string, error) WasSignedCalled func(ctx context.Context, actionID uint64) (bool, error) - PerformActionCalled func(ctx context.Context, actionID uint64, batch *clients.TransferBatch) (string, error) + PerformActionCalled func(ctx context.Context, actionID uint64, batch *bridgeCore.TransferBatch) (string, error) CheckClientAvailabilityCalled func(ctx context.Context) error + IsMintBurnTokenCalled func(ctx context.Context, token []byte) (bool, error) + IsNativeTokenCalled func(ctx context.Context, token []byte) (bool, error) + TotalBalancesCalled func(ctx context.Context, token []byte) (*big.Int, error) + MintBalancesCalled func(ctx context.Context, token []byte) (*big.Int, error) + BurnBalancesCalled func(ctx context.Context, token []byte) (*big.Int, error) + CheckRequiredBalanceCalled func(ctx context.Context, token []byte, value *big.Int) error + GetLastMvxBatchIDCalled func(ctx context.Context) (uint64, error) CloseCalled func() error } -// GetPending - -func (stub *MultiversXClientStub) GetPending(ctx context.Context) (*clients.TransferBatch, error) { - if stub.GetPendingCalled != nil { - return stub.GetPendingCalled(ctx) +// GetPendingBatch - +func (stub *MultiversXClientStub) GetPendingBatch(ctx context.Context) (*bridgeCore.TransferBatch, error) { + if stub.GetPendingBatchCalled != nil { + return stub.GetPendingBatchCalled(ctx) + } + + return nil, errNotImplemented +} + +// GetBatch - +func (stub *MultiversXClientStub) GetBatch(ctx context.Context, batchID uint64) (*bridgeCore.TransferBatch, error) { + if stub.GetBatchCalled != nil { + return stub.GetBatchCalled(ctx, batchID) } return nil, errNotImplemented @@ -52,7 +70,7 @@ func (stub *MultiversXClientStub) GetCurrentBatchAsDataBytes(ctx context.Context } // WasProposedTransfer - -func (stub *MultiversXClientStub) WasProposedTransfer(ctx context.Context, batch *clients.TransferBatch) (bool, error) { +func (stub *MultiversXClientStub) WasProposedTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) { if stub.WasProposedTransferCalled != nil { return stub.WasProposedTransferCalled(ctx, batch) } @@ -79,7 +97,7 @@ func (stub *MultiversXClientStub) WasExecuted(ctx context.Context, actionID uint } // GetActionIDForProposeTransfer - -func (stub *MultiversXClientStub) GetActionIDForProposeTransfer(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { +func (stub *MultiversXClientStub) GetActionIDForProposeTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { if stub.GetActionIDForProposeTransferCalled != nil { return stub.GetActionIDForProposeTransferCalled(ctx, batch) } @@ -88,7 +106,7 @@ func (stub *MultiversXClientStub) GetActionIDForProposeTransfer(ctx context.Cont } // WasProposedSetStatus - -func (stub *MultiversXClientStub) WasProposedSetStatus(ctx context.Context, batch *clients.TransferBatch) (bool, error) { +func (stub *MultiversXClientStub) WasProposedSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (bool, error) { if stub.WasProposedSetStatusCalled != nil { return stub.WasProposedSetStatusCalled(ctx, batch) } @@ -106,7 +124,7 @@ func (stub *MultiversXClientStub) GetTransactionsStatuses(ctx context.Context, b } // GetActionIDForSetStatusOnPendingTransfer - -func (stub *MultiversXClientStub) GetActionIDForSetStatusOnPendingTransfer(ctx context.Context, batch *clients.TransferBatch) (uint64, error) { +func (stub *MultiversXClientStub) GetActionIDForSetStatusOnPendingTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (uint64, error) { if stub.GetActionIDForSetStatusOnPendingTransferCalled != nil { return stub.GetActionIDForSetStatusOnPendingTransferCalled(ctx, batch) } @@ -142,7 +160,7 @@ func (stub *MultiversXClientStub) GetCurrentNonce(ctx context.Context) (uint64, } // ProposeSetStatus - -func (stub *MultiversXClientStub) ProposeSetStatus(ctx context.Context, batch *clients.TransferBatch) (string, error) { +func (stub *MultiversXClientStub) ProposeSetStatus(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { if stub.ProposeSetStatusCalled != nil { return stub.ProposeSetStatusCalled(ctx, batch) } @@ -151,7 +169,7 @@ func (stub *MultiversXClientStub) ProposeSetStatus(ctx context.Context, batch *c } // ProposeTransfer - -func (stub *MultiversXClientStub) ProposeTransfer(ctx context.Context, batch *clients.TransferBatch) (string, error) { +func (stub *MultiversXClientStub) ProposeTransfer(ctx context.Context, batch *bridgeCore.TransferBatch) (string, error) { if stub.ProposeTransferCalled != nil { return stub.ProposeTransferCalled(ctx, batch) } @@ -178,7 +196,7 @@ func (stub *MultiversXClientStub) WasSigned(ctx context.Context, actionID uint64 } // PerformAction - -func (stub *MultiversXClientStub) PerformAction(ctx context.Context, actionID uint64, batch *clients.TransferBatch) (string, error) { +func (stub *MultiversXClientStub) PerformAction(ctx context.Context, actionID uint64, batch *bridgeCore.TransferBatch) (string, error) { if stub.PerformActionCalled != nil { return stub.PerformActionCalled(ctx, actionID, batch) } @@ -195,6 +213,63 @@ func (stub *MultiversXClientStub) CheckClientAvailability(ctx context.Context) e return nil } +// IsMintBurnToken - +func (stub *MultiversXClientStub) IsMintBurnToken(ctx context.Context, token []byte) (bool, error) { + if stub.IsMintBurnTokenCalled != nil { + return stub.IsMintBurnTokenCalled(ctx, token) + } + return false, notImplemented +} + +// IsNativeToken - +func (stub *MultiversXClientStub) IsNativeToken(ctx context.Context, token []byte) (bool, error) { + if stub.IsNativeTokenCalled != nil { + return stub.IsNativeTokenCalled(ctx, token) + } + return false, notImplemented +} + +// TotalBalances - +func (stub *MultiversXClientStub) TotalBalances(ctx context.Context, token []byte) (*big.Int, error) { + if stub.TotalBalancesCalled != nil { + return stub.TotalBalancesCalled(ctx, token) + } + return nil, notImplemented +} + +// MintBalances - +func (stub *MultiversXClientStub) MintBalances(ctx context.Context, token []byte) (*big.Int, error) { + if stub.MintBalancesCalled != nil { + return stub.MintBalancesCalled(ctx, token) + } + return nil, notImplemented +} + +// BurnBalances - +func (stub *MultiversXClientStub) BurnBalances(ctx context.Context, token []byte) (*big.Int, error) { + if stub.BurnBalancesCalled != nil { + return stub.BurnBalancesCalled(ctx, token) + } + return nil, notImplemented +} + +// CheckRequiredBalance - +func (stub *MultiversXClientStub) CheckRequiredBalance(ctx context.Context, token []byte, value *big.Int) error { + if stub.CheckRequiredBalanceCalled != nil { + return stub.CheckRequiredBalanceCalled(ctx, token, value) + } + return nil +} + +// GetLastMvxBatchID - +func (stub *MultiversXClientStub) GetLastMvxBatchID(ctx context.Context) (uint64, error) { + if stub.GetLastMvxBatchIDCalled != nil { + return stub.GetLastMvxBatchIDCalled(ctx) + } + + return 0, nil +} + // Close - func (stub *MultiversXClientStub) Close() error { if stub.CloseCalled != nil { diff --git a/testsCommon/bridge/nonceTransactionsHandlerStub.go b/testsCommon/bridge/nonceTransactionsHandlerStub.go index 1177a79b..213c5360 100644 --- a/testsCommon/bridge/nonceTransactionsHandlerStub.go +++ b/testsCommon/bridge/nonceTransactionsHandlerStub.go @@ -9,18 +9,18 @@ import ( // NonceTransactionsHandlerStub - type NonceTransactionsHandlerStub struct { - GetNonceCalled func(ctx context.Context, address core.AddressHandler) (uint64, error) - SendTransactionCalled func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) - CloseCalled func() error + ApplyNonceAndGasPriceCalled func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error + SendTransactionCalled func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) + CloseCalled func() error } -// GetNonce - -func (stub *NonceTransactionsHandlerStub) GetNonce(ctx context.Context, address core.AddressHandler) (uint64, error) { - if stub.GetNonceCalled != nil { - return stub.GetNonceCalled(ctx, address) +// ApplyNonceAndGasPrice - +func (stub *NonceTransactionsHandlerStub) ApplyNonceAndGasPrice(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + if stub.ApplyNonceAndGasPriceCalled != nil { + return stub.ApplyNonceAndGasPriceCalled(ctx, address, tx) } - return 0, nil + return nil } // SendTransaction - diff --git a/testsCommon/bridge/safeContractStub.go b/testsCommon/bridge/safeContractStub.go new file mode 100644 index 00000000..6ffa42fb --- /dev/null +++ b/testsCommon/bridge/safeContractStub.go @@ -0,0 +1,72 @@ +package bridge + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" +) + +// SafeContractStub - +type SafeContractStub struct { + TotalBalancesCalled func(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) + MintBalancesCalled func(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) + BurnBalancesCalled func(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) + MintBurnTokensCalled func(opts *bind.CallOpts, arg0 common.Address) (bool, error) + NativeTokensCalled func(opts *bind.CallOpts, arg0 common.Address) (bool, error) + WhitelistedTokensCalled func(opts *bind.CallOpts, arg0 common.Address) (bool, error) +} + +// TotalBalances - +func (stub *SafeContractStub) TotalBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + if stub.TotalBalancesCalled != nil { + return stub.TotalBalancesCalled(opts, arg0) + } + + return big.NewInt(0), nil +} + +// MintBalances - +func (stub *SafeContractStub) MintBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + if stub.MintBalancesCalled != nil { + return stub.MintBalancesCalled(opts, arg0) + } + + return big.NewInt(0), nil +} + +// BurnBalances - +func (stub *SafeContractStub) BurnBalances(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + if stub.BurnBalancesCalled != nil { + return stub.BurnBalancesCalled(opts, arg0) + } + + return big.NewInt(0), nil +} + +// MintBurnTokens - +func (stub *SafeContractStub) MintBurnTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + if stub.MintBurnTokensCalled != nil { + return stub.MintBurnTokensCalled(opts, arg0) + } + + return false, nil +} + +// NativeTokens - +func (stub *SafeContractStub) NativeTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + if stub.NativeTokensCalled != nil { + return stub.NativeTokensCalled(opts, arg0) + } + + return false, nil +} + +// WhitelistedTokens - +func (stub *SafeContractStub) WhitelistedTokens(opts *bind.CallOpts, arg0 common.Address) (bool, error) { + if stub.WhitelistedTokensCalled != nil { + return stub.WhitelistedTokensCalled(opts, arg0) + } + + return false, nil +} diff --git a/testsCommon/bridge/safeContractWrapperStub.go b/testsCommon/bridge/safeContractWrapperStub.go new file mode 100644 index 00000000..9da42c08 --- /dev/null +++ b/testsCommon/bridge/safeContractWrapperStub.go @@ -0,0 +1,27 @@ +package bridge + +import "github.com/ethereum/go-ethereum/accounts/abi/bind" + +// SafeContractWrapperStub - +type SafeContractWrapperStub struct { + DepositsCountCalled func(opts *bind.CallOpts) (uint64, error) + BatchesCountCalled func(opts *bind.CallOpts) (uint64, error) +} + +// DepositsCount - +func (stub *SafeContractWrapperStub) DepositsCount(opts *bind.CallOpts) (uint64, error) { + if stub.DepositsCountCalled != nil { + return stub.DepositsCountCalled(opts) + } + + return 0, nil +} + +// BatchesCount - +func (stub *SafeContractWrapperStub) BatchesCount(opts *bind.CallOpts) (uint64, error) { + if stub.BatchesCountCalled != nil { + return stub.BatchesCountCalled(opts) + } + + return 0, nil +} diff --git a/testsCommon/gasHandlerStub.go b/testsCommon/gasHandlerStub.go index d5e87953..1ddce15a 100644 --- a/testsCommon/gasHandlerStub.go +++ b/testsCommon/gasHandlerStub.go @@ -16,6 +16,11 @@ func (ghs *GasHandlerStub) GetCurrentGasPrice() (*big.Int, error) { return big.NewInt(0), nil } +// Close - +func (ghs *GasHandlerStub) Close() error { + return nil +} + // IsInterfaceNil - func (ghs *GasHandlerStub) IsInterfaceNil() bool { return ghs == nil diff --git a/testsCommon/gasMap.go b/testsCommon/gasMap.go index f2617cfd..39aa12c9 100644 --- a/testsCommon/gasMap.go +++ b/testsCommon/gasMap.go @@ -14,5 +14,7 @@ func CreateTestMultiversXGasMap() config.MultiversXGasMapConfig { ProposeStatusForEach: 105, PerformActionBase: 106, PerformActionForEach: 107, + ScCallPerByte: 108, + ScCallPerformForEach: 109, } } diff --git a/testsCommon/interactors/blockchainClientStub.go b/testsCommon/interactors/blockchainClientStub.go index 53cfd3f6..81de954e 100644 --- a/testsCommon/interactors/blockchainClientStub.go +++ b/testsCommon/interactors/blockchainClientStub.go @@ -4,7 +4,9 @@ import ( "context" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" ) // BlockchainClientStub - @@ -13,6 +15,7 @@ type BlockchainClientStub struct { NonceAtCalled func(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) ChainIDCalled func(ctx context.Context) (*big.Int, error) BalanceAtCalled func(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) + FilterLogsCalled func(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) } // BlockNumber - @@ -51,6 +54,15 @@ func (bcs *BlockchainClientStub) BalanceAt(ctx context.Context, account common.A return big.NewInt(0), nil } +// FilterLogs - +func (bcs *BlockchainClientStub) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if bcs.FilterLogsCalled != nil { + return bcs.FilterLogsCalled(ctx, q) + } + + return nil, nil +} + // IsInterfaceNil returns true if there is no value under the interface func (bcs *BlockchainClientStub) IsInterfaceNil() bool { return bcs == nil diff --git a/testsCommon/interactors/genericErc20ContractStub.go b/testsCommon/interactors/genericErc20ContractStub.go index 77495927..e9856a21 100644 --- a/testsCommon/interactors/genericErc20ContractStub.go +++ b/testsCommon/interactors/genericErc20ContractStub.go @@ -11,6 +11,7 @@ import ( // GenericErc20ContractStub - type GenericErc20ContractStub struct { BalanceOfCalled func(account common.Address) (*big.Int, error) + DecimalsCalled func() (uint8, error) } // BalanceOf - @@ -21,3 +22,12 @@ func (stub *GenericErc20ContractStub) BalanceOf(_ *bind.CallOpts, account common return nil, errors.New("GenericErc20ContractStub.BalanceOf not implemented") } + +// Decimals - +func (stub *GenericErc20ContractStub) Decimals(_ *bind.CallOpts) (uint8, error) { + if stub.DecimalsCalled != nil { + return stub.DecimalsCalled() + } + + return 0, errors.New("GenericErc20ContractStub.Decimals not implemented") +} diff --git a/testsCommon/interactors/proxyStub.go b/testsCommon/interactors/proxyStub.go index c7545487..8fc8221e 100644 --- a/testsCommon/interactors/proxyStub.go +++ b/testsCommon/interactors/proxyStub.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/multiversx/mx-chain-core-go/data/api" "github.com/multiversx/mx-chain-core-go/data/transaction" "github.com/multiversx/mx-sdk-go/core" "github.com/multiversx/mx-sdk-go/data" @@ -11,13 +12,16 @@ import ( // ProxyStub - type ProxyStub struct { - GetNetworkConfigCalled func(ctx context.Context) (*data.NetworkConfig, error) - SendTransactionCalled func(ctx context.Context, transaction *transaction.FrontendTransaction) (string, error) - SendTransactionsCalled func(ctx context.Context, txs []*transaction.FrontendTransaction) ([]string, error) - ExecuteVMQueryCalled func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) - GetAccountCalled func(ctx context.Context, address core.AddressHandler) (*data.Account, error) - GetNetworkStatusCalled func(ctx context.Context, shardID uint32) (*data.NetworkStatus, error) - GetShardOfAddressCalled func(ctx context.Context, bech32Address string) (uint32, error) + GetNetworkConfigCalled func(ctx context.Context) (*data.NetworkConfig, error) + SendTransactionCalled func(ctx context.Context, transaction *transaction.FrontendTransaction) (string, error) + SendTransactionsCalled func(ctx context.Context, txs []*transaction.FrontendTransaction) ([]string, error) + ExecuteVMQueryCalled func(ctx context.Context, vmRequest *data.VmValueRequest) (*data.VmValuesResponseData, error) + GetAccountCalled func(ctx context.Context, address core.AddressHandler) (*data.Account, error) + GetNetworkStatusCalled func(ctx context.Context, shardID uint32) (*data.NetworkStatus, error) + GetShardOfAddressCalled func(ctx context.Context, bech32Address string) (uint32, error) + GetESDTTokenDataCalled func(ctx context.Context, address core.AddressHandler, tokenIdentifier string, queryOptions api.AccountQueryOptions) (*data.ESDTFungibleTokenData, error) + GetTransactionInfoWithResultsCalled func(_ context.Context, _ string) (*data.TransactionInfo, error) + ProcessTransactionStatusCalled func(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) } // GetNetworkConfig - @@ -83,6 +87,33 @@ func (eps *ProxyStub) GetShardOfAddress(ctx context.Context, bech32Address strin return 0, fmt.Errorf("not implemented") } +// GetESDTTokenData - +func (eps *ProxyStub) GetESDTTokenData(ctx context.Context, address core.AddressHandler, tokenIdentifier string, queryOptions api.AccountQueryOptions) (*data.ESDTFungibleTokenData, error) { + if eps.GetESDTTokenDataCalled != nil { + return eps.GetESDTTokenDataCalled(ctx, address, tokenIdentifier, queryOptions) + } + + return nil, fmt.Errorf("not implemented") +} + +// GetTransactionInfoWithResults - +func (eps *ProxyStub) GetTransactionInfoWithResults(ctx context.Context, hash string) (*data.TransactionInfo, error) { + if eps.GetTransactionInfoWithResultsCalled != nil { + return eps.GetTransactionInfoWithResultsCalled(ctx, hash) + } + + return nil, fmt.Errorf("not implemented") +} + +// ProcessTransactionStatus - +func (eps *ProxyStub) ProcessTransactionStatus(ctx context.Context, hexTxHash string) (transaction.TxStatus, error) { + if eps.ProcessTransactionStatusCalled != nil { + return eps.ProcessTransactionStatusCalled(ctx, hexTxHash) + } + + return "", nil +} + // IsInterfaceNil - func (eps *ProxyStub) IsInterfaceNil() bool { return eps == nil diff --git a/testsCommon/multiversxCodecStub.go b/testsCommon/multiversxCodecStub.go new file mode 100644 index 00000000..1629f57a --- /dev/null +++ b/testsCommon/multiversxCodecStub.go @@ -0,0 +1,32 @@ +package testsCommon + +import "github.com/multiversx/mx-bridge-eth-go/parsers" + +// MultiversxCodecStub - +type MultiversxCodecStub struct { + DecodeProxySCCompleteCallDataCalled func(buff []byte) (parsers.ProxySCCompleteCallData, error) + ExtractGasLimitFromRawCallDataCalled func(buff []byte) (uint64, error) +} + +// DecodeProxySCCompleteCallData - +func (stub *MultiversxCodecStub) DecodeProxySCCompleteCallData(buff []byte) (parsers.ProxySCCompleteCallData, error) { + if stub.DecodeProxySCCompleteCallDataCalled != nil { + return stub.DecodeProxySCCompleteCallDataCalled(buff) + } + + return parsers.ProxySCCompleteCallData{}, nil +} + +// ExtractGasLimitFromRawCallData - +func (stub *MultiversxCodecStub) ExtractGasLimitFromRawCallData(buff []byte) (uint64, error) { + if stub.ExtractGasLimitFromRawCallDataCalled != nil { + return stub.ExtractGasLimitFromRawCallDataCalled(buff) + } + + return 0, nil +} + +// IsInterfaceNil - +func (stub *MultiversxCodecStub) IsInterfaceNil() bool { + return stub == nil +} diff --git a/testsCommon/p2p/p2pMessageMock.go b/testsCommon/p2p/p2pMessageMock.go index 3218fc94..6224f1c3 100644 --- a/testsCommon/p2p/p2pMessageMock.go +++ b/testsCommon/p2p/p2pMessageMock.go @@ -1,6 +1,7 @@ package p2p import ( + "github.com/multiversx/mx-chain-communication-go/p2p" "github.com/multiversx/mx-chain-core-go/core" ) @@ -62,6 +63,11 @@ func (msg *P2PMessageMock) Payload() []byte { return msg.PayloadField } +// BroadcastMethod - +func (msg *P2PMessageMock) BroadcastMethod() p2p.BroadcastMethod { + return p2p.Broadcast +} + // IsInterfaceNil returns true if there is no value under the interface func (msg *P2PMessageMock) IsInterfaceNil() bool { return msg == nil diff --git a/testsCommon/scCallsExecuteFilterStub.go b/testsCommon/scCallsExecuteFilterStub.go new file mode 100644 index 00000000..57f98c2f --- /dev/null +++ b/testsCommon/scCallsExecuteFilterStub.go @@ -0,0 +1,22 @@ +package testsCommon + +import "github.com/multiversx/mx-bridge-eth-go/parsers" + +// ScCallsExecuteFilterStub - +type ScCallsExecuteFilterStub struct { + ShouldExecuteCalled func(callData parsers.ProxySCCompleteCallData) bool +} + +// ShouldExecute - +func (stub *ScCallsExecuteFilterStub) ShouldExecute(callData parsers.ProxySCCompleteCallData) bool { + if stub.ShouldExecuteCalled != nil { + return stub.ShouldExecuteCalled(callData) + } + + return true +} + +// IsInterfaceNil - +func (stub *ScCallsExecuteFilterStub) IsInterfaceNil() bool { + return stub == nil +} diff --git a/testsCommon/testMultiversxCodec.go b/testsCommon/testMultiversxCodec.go new file mode 100644 index 00000000..588f91fe --- /dev/null +++ b/testsCommon/testMultiversxCodec.go @@ -0,0 +1,162 @@ +package testsCommon + +import ( + "encoding/binary" + "fmt" + + bridgeCore "github.com/multiversx/mx-bridge-eth-go/core" + "github.com/multiversx/mx-bridge-eth-go/parsers" +) + +// TestMultiversXCodec is the codec helper used in testing +type TestMultiversXCodec struct { +} + +// EncodeCallDataWithLenAndMarker will provide a valid data byte slice with encoded call data parameters along with the length and marker +func (codec *TestMultiversXCodec) EncodeCallDataWithLenAndMarker(callData parsers.CallData) []byte { + initialAlloc := 1024 * 1024 // 1MB initial buffer + result := make([]byte, 0, initialAlloc) + buff32Bits := make([]byte, 4) + + result = append(result, bridgeCore.DataPresentProtocolMarker) // marker + + callDataBuff := codec.EncodeCallDataStrict(callData) + binary.BigEndian.PutUint32(buff32Bits, uint32(len(callDataBuff))) + + result = append(result, buff32Bits...) + result = append(result, callDataBuff...) + + return result +} + +// EncodeCallDataStrict will encode just the provided call data. No length or marker will be added +func (codec *TestMultiversXCodec) EncodeCallDataStrict(callData parsers.CallData) []byte { + initialAlloc := 1024 * 1024 // 1MB initial buffer + result := make([]byte, 0, initialAlloc) + + buff32Bits := make([]byte, 4) + buff64Bits := make([]byte, 8) + + funcLen := len(callData.Function) + + binary.BigEndian.PutUint32(buff32Bits, uint32(funcLen)) + result = append(result, buff32Bits...) // append the function len + result = append(result, callData.Function...) // append the function as string + + binary.BigEndian.PutUint64(buff64Bits, callData.GasLimit) + result = append(result, buff64Bits...) // append the gas limit as 8 bytes + + if len(callData.Arguments) == 0 { + // in case of no arguments, the contract requires that the missing data protocol marker should be provided, not + // a 0 encoded on 4 bytes. + result = append(result, bridgeCore.MissingDataProtocolMarker) + return result + } + + result = append(result, bridgeCore.DataPresentProtocolMarker) + encodedArgs := codec.encodeArgs(callData.Arguments) + result = append(result, encodedArgs...) + + return result +} + +func (codec *TestMultiversXCodec) encodeArgs(args []string) []byte { + buff32Bits := make([]byte, 4) + + initialAlloc := 1024 * 1024 // 1MB initial buffer + result := make([]byte, 0, initialAlloc) + + binary.BigEndian.PutUint32(buff32Bits, uint32(len(args))) + result = append(result, buff32Bits...) // append the number of arguments + + for _, arg := range args { + lenArg := len(arg) + binary.BigEndian.PutUint32(buff32Bits, uint32(lenArg)) + result = append(result, buff32Bits...) // append the length of the current argument + result = append(result, arg...) // append the argument as string + } + + return result +} + +// DecodeCallData will try to decode the provided bytes into a CallData struct +func (codec *TestMultiversXCodec) DecodeCallData(buff []byte) parsers.CallData { + if len(buff) == 0 { + panic("empty buffer") + } + + marker := buff[0] + buff = buff[1:] + + switch marker { + case bridgeCore.MissingDataProtocolMarker: + return parsers.CallData{ + Type: bridgeCore.MissingDataProtocolMarker, + } + case bridgeCore.DataPresentProtocolMarker: + return decodeCallData(buff, marker) + default: + panic(fmt.Sprintf("unexpected marker: %d", marker)) + } +} + +func decodeCallData(buff []byte, marker byte) parsers.CallData { + buff, numChars, err := parsers.ExtractUint32(buff) + if err != nil { + panic(err) + } + if numChars != len(buff) { + panic("mismatch for len") + } + + buff, function, err := parsers.ExtractString(buff) + if err != nil { + panic(err) + } + + _, gasLimit, err := parsers.ExtractUint64(buff) + if err != nil { + panic(err) + } + + arguments, err := extractArguments(buff) + if err != nil { + panic(err) + } + + return parsers.CallData{ + Type: marker, + Function: function, + GasLimit: gasLimit, + Arguments: arguments, + } +} + +func extractArguments(buff []byte) ([]string, error) { + if len(buff) == 0 { + panic("empty buffer") + } + + if len(buff) == 1 && buff[0] == bridgeCore.MissingDataProtocolMarker { + // no arguments provided + return make([]string, 0), nil + } + + buff, numArgumentsLength, err := parsers.ExtractUint32(buff) + if err != nil { + panic(err) + } + + arguments := make([]string, 0) + for i := 0; i < numArgumentsLength; i++ { + var argument string + buff, argument, err = parsers.ExtractString(buff) + if err != nil { + return nil, fmt.Errorf("%w for argument %d", err, i) + } + + arguments = append(arguments, argument) + } + + return arguments, nil +} diff --git a/testsCommon/txNonceHandlerV2Stub.go b/testsCommon/txNonceHandlerV2Stub.go new file mode 100644 index 00000000..5c8876d4 --- /dev/null +++ b/testsCommon/txNonceHandlerV2Stub.go @@ -0,0 +1,48 @@ +package testsCommon + +import ( + "context" + + "github.com/multiversx/mx-chain-core-go/data/transaction" + "github.com/multiversx/mx-sdk-go/core" +) + +// TxNonceHandlerV2Stub - +type TxNonceHandlerV2Stub struct { + ApplyNonceAndGasPriceCalled func(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error + SendTransactionCalled func(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) + ForceNonceReFetchCalled func(address core.AddressHandler) error + CloseCalled func() error +} + +// ApplyNonceAndGasPrice - +func (stub *TxNonceHandlerV2Stub) ApplyNonceAndGasPrice(ctx context.Context, address core.AddressHandler, tx *transaction.FrontendTransaction) error { + if stub.ApplyNonceAndGasPriceCalled != nil { + return stub.ApplyNonceAndGasPriceCalled(ctx, address, tx) + } + + return nil +} + +// SendTransaction - +func (stub *TxNonceHandlerV2Stub) SendTransaction(ctx context.Context, tx *transaction.FrontendTransaction) (string, error) { + if stub.SendTransactionCalled != nil { + return stub.SendTransactionCalled(ctx, tx) + } + + return "", nil +} + +// Close - +func (stub *TxNonceHandlerV2Stub) Close() error { + if stub.CloseCalled != nil { + return stub.CloseCalled() + } + + return nil +} + +// IsInterfaceNil - +func (stub *TxNonceHandlerV2Stub) IsInterfaceNil() bool { + return stub == nil +}