diff --git a/cmd/bridge/config/config.toml b/cmd/bridge/config/config.toml index 70e3d547..1b0912e0 100644 --- a/cmd/bridge/config/config.toml +++ b/cmd/bridge/config/config.toml @@ -45,7 +45,7 @@ ProposeStatusBase = 10000000 ProposeStatusForEach = 7000000 PerformActionBase = 40000000 - PerformActionForEach = 5500000 + PerformActionForEach = 7000000 ScCallPerByte = 100000 # 1500 tx data field + the rest for the actual storage in the contract ScCallPerformForEach = 10000000 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 72ecc41b..ea08d073 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.9" services: chain-simulator: - image: multiversx/chainsimulator:v1.7.13-patch2 + image: multiversx/chainsimulator:v1.7.13-patch2-fix1 ports: - 8085:8085 volumes: diff --git a/integrationTests/relayers/slowTests/common.go b/integrationTests/relayers/slowTests/common.go index a47cb0fc..c9e1195b 100644 --- a/integrationTests/relayers/slowTests/common.go +++ b/integrationTests/relayers/slowTests/common.go @@ -6,6 +6,7 @@ import ( "bytes" "math/big" + "github.com/ethereum/go-ethereum/common" 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" @@ -16,6 +17,7 @@ import ( var ( log = logger.GetOrCreate("integrationTests/relayers/slowTests") mvxZeroAddress = bytes.Repeat([]byte{0x00}, 32) + ethZeroAddress = common.Address{} ) // GenerateTestUSDCToken will generate a test USDC token @@ -64,6 +66,12 @@ func GenerateTestUSDCToken() framework.TestTokenParams { ValueToSendFromMvX: nil, InvalidReceiver: mvxZeroAddress, }, + { + ValueToTransferToMvx: nil, + ValueToSendFromMvX: big.NewInt(730), + InvalidReceiver: ethZeroAddress, + IsFaultyDeposit: true, + }, }, DeltaBalances: map[framework.HalfBridgeIdentifier]framework.DeltaBalancesOnKeys{ framework.FirstHalfBridge: map[string]*framework.DeltaBalanceHolder{ @@ -206,7 +214,17 @@ func GenerateTestMEMEToken() framework.TestTokenParams { ValueToSendFromMvX: big.NewInt(38), IsFaultyDeposit: true, }, - // TODO: add a test where the receiver is the zero address + { + ValueToTransferToMvx: nil, + ValueToSendFromMvX: big.NewInt(420), + InvalidReceiver: ethZeroAddress, + IsFaultyDeposit: true, + }, + { + ValueToTransferToMvx: big.NewInt(1300), + ValueToSendFromMvX: nil, + InvalidReceiver: mvxZeroAddress, + }, }, DeltaBalances: map[framework.HalfBridgeIdentifier]framework.DeltaBalancesOnKeys{ framework.FirstHalfBridge: map[string]*framework.DeltaBalanceHolder{ @@ -243,7 +261,7 @@ func GenerateTestMEMEToken() framework.TestTokenParams { MvxToken: framework.UniversalToken, }, framework.Bob: { - OnEth: big.NewInt(4000 - 50 - 2400 + 6000 - 50 - 200 + 2000 - 50 - 1000), + OnEth: big.NewInt(4000 - 50 - 2400 + 6000 - 50 - 200 + 2000 - 50 - 1000 - 1300 + 1250), OnMvx: big.NewInt(0), MvxToken: framework.UniversalToken, }, @@ -254,7 +272,7 @@ func GenerateTestMEMEToken() framework.TestTokenParams { }, framework.SafeSC: { OnEth: big.NewInt(0), - OnMvx: big.NewInt(4000 - 2400 + 6000 - 200 + 2000 - 1000), + OnMvx: big.NewInt(4000 - 2400 + 6000 - 200 + 2000 - 1000 - 1300 + 1300), MvxToken: framework.ChainSpecificToken, }, framework.CalledTestSC: { @@ -277,8 +295,8 @@ func GenerateTestMEMEToken() framework.TestTokenParams { MvxSafeMintValue: big.NewInt(0), MvxSafeBurnValue: big.NewInt(0), - EthSafeMintValue: big.NewInt(4000 - 50 + 6000 - 50 + 2000 - 50), - EthSafeBurnValue: big.NewInt(2400 + 200 + 1000), + EthSafeMintValue: big.NewInt(4000 - 50 + 6000 - 50 + 2000 - 50 + 1300 - 50), + EthSafeBurnValue: big.NewInt(2400 + 200 + 1000 + 1300), }, SpecialChecks: &framework.SpecialBalanceChecks{ WrapperDeltaLiquidityCheck: big.NewInt(0), @@ -289,13 +307,13 @@ func GenerateTestMEMEToken() framework.TestTokenParams { // ApplyMEMERefundBalances will apply the refund balances on the involved entities for the MEME token func ApplyMEMERefundBalances(token *framework.TestTokenParams) { // we need to add the 1000 MEME tokens as the third bridge was done that include the refund on the Ethereum side - token.DeltaBalances[framework.SecondHalfBridge][framework.SafeSC].OnMvx = big.NewInt(4000 - 2400 + 6000 - 200 + 2000 - 1000 + 1000) + token.DeltaBalances[framework.SecondHalfBridge][framework.SafeSC].OnMvx = big.NewInt(4000 - 2400 + 6000 - 200 + 2000 - 1300 + 1300 - 1000 + 1000) // Bob will get his tokens back from the refund - token.DeltaBalances[framework.SecondHalfBridge][framework.Bob].OnEth = big.NewInt(4000 - 50 - 2400 + 6000 - 50 - 200 + 2000 - 50 - 1000 + 950) + token.DeltaBalances[framework.SecondHalfBridge][framework.Bob].OnEth = big.NewInt(4000 - 50 - 2400 + 6000 - 50 - 200 + 2000 - 50 - 1300 + 1250 - 1000 + 950) // no funds remain in the test caller SC token.DeltaBalances[framework.SecondHalfBridge][framework.CalledTestSC].OnMvx = big.NewInt(0) - token.MintBurnChecks.EthSafeMintValue = big.NewInt(4000 - 50 + 6000 - 50 + 2000 - 50 + 1000 - 50) + token.MintBurnChecks.EthSafeMintValue = big.NewInt(4000 - 50 + 6000 - 50 + 2000 - 50 + 1300 - 50 + 1000 - 50) } // GenerateTestEUROCToken will generate a test EUROC token @@ -344,6 +362,12 @@ func GenerateTestEUROCToken() framework.TestTokenParams { ValueToSendFromMvX: nil, InvalidReceiver: mvxZeroAddress, }, + { + ValueToTransferToMvx: nil, + ValueToSendFromMvX: big.NewInt(853), + InvalidReceiver: ethZeroAddress, + IsFaultyDeposit: true, + }, }, DeltaBalances: map[framework.HalfBridgeIdentifier]framework.DeltaBalancesOnKeys{ framework.FirstHalfBridge: map[string]*framework.DeltaBalanceHolder{ @@ -480,7 +504,17 @@ func GenerateTestMEXToken() framework.TestTokenParams { ValueToSendFromMvX: nil, IsFaultyDeposit: true, }, - // TODO: add a test where the receiver is the zero address + { + ValueToTransferToMvx: nil, + ValueToSendFromMvX: big.NewInt(500), + InvalidReceiver: ethZeroAddress, + IsFaultyDeposit: true, + }, + { + ValueToTransferToMvx: big.NewInt(3000), + ValueToSendFromMvX: nil, + InvalidReceiver: mvxZeroAddress, + }, }, DeltaBalances: map[framework.HalfBridgeIdentifier]framework.DeltaBalancesOnKeys{ framework.FirstHalfBridge: map[string]*framework.DeltaBalanceHolder{ @@ -517,7 +551,7 @@ func GenerateTestMEXToken() framework.TestTokenParams { MvxToken: framework.UniversalToken, }, framework.Bob: { - OnEth: big.NewInt(4010 - 50 - 2410 + 6010 - 50 - 210 + 2010 - 50 - 1010), + OnEth: big.NewInt(4010 - 50 - 2410 + 6010 - 50 - 210 + 2010 - 50 - 1010 - 3000 + 2950), OnMvx: big.NewInt(0), MvxToken: framework.UniversalToken, }, @@ -528,7 +562,7 @@ func GenerateTestMEXToken() framework.TestTokenParams { }, framework.SafeSC: { OnEth: big.NewInt(0), - OnMvx: big.NewInt(50 + 50 + 50), + OnMvx: big.NewInt(50 + 50 + 50 + 50), MvxToken: framework.ChainSpecificToken, }, framework.CalledTestSC: { @@ -544,15 +578,15 @@ func GenerateTestMEXToken() framework.TestTokenParams { }, }, MintBurnChecks: &framework.MintBurnBalances{ - MvxTotalUniversalMint: big.NewInt(2410 + 210 + 1010), + MvxTotalUniversalMint: big.NewInt(2410 + 210 + 1010 + 3000), MvxTotalChainSpecificMint: big.NewInt(0), - MvxTotalUniversalBurn: big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50), + MvxTotalUniversalBurn: big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 3000 - 50), MvxTotalChainSpecificBurn: big.NewInt(0), - MvxSafeMintValue: big.NewInt(2410 + 210 + 1010), - MvxSafeBurnValue: big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50), + MvxSafeMintValue: big.NewInt(2410 + 210 + 1010 + 3000), + MvxSafeBurnValue: big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 3000 - 50), - EthSafeMintValue: big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50), - EthSafeBurnValue: big.NewInt(2410 + 210 + 1010), + EthSafeMintValue: big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 3000 - 50), + EthSafeBurnValue: big.NewInt(2410 + 210 + 1010 + 3000), }, SpecialChecks: &framework.SpecialBalanceChecks{ WrapperDeltaLiquidityCheck: big.NewInt(0), @@ -563,15 +597,15 @@ func GenerateTestMEXToken() framework.TestTokenParams { // ApplyMEXRefundBalances will apply the refund balances on the involved entities for the MEX token func ApplyMEXRefundBalances(token *framework.TestTokenParams) { // 3 normal swaps + the refund one - token.DeltaBalances[framework.SecondHalfBridge][framework.SafeSC].OnMvx = big.NewInt(50 + 50 + 50 + 50) + token.DeltaBalances[framework.SecondHalfBridge][framework.SafeSC].OnMvx = big.NewInt(50 + 50 + 50 + 50 + 50) // Bob will get his tokens back from the refund - token.DeltaBalances[framework.SecondHalfBridge][framework.Bob].OnEth = big.NewInt(4010 - 50 - 2410 + 6010 - 50 - 210 + 2010 - 50 - 1010 + 960) + token.DeltaBalances[framework.SecondHalfBridge][framework.Bob].OnEth = big.NewInt(4010 - 50 - 2410 + 6010 - 50 - 210 + 2010 - 50 - 1010 + 960 - 3000 + 2950) // no funds remain in the test caller SC token.DeltaBalances[framework.SecondHalfBridge][framework.CalledTestSC].OnMvx = big.NewInt(0) - token.MintBurnChecks.MvxTotalUniversalBurn = big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 1010 - 50) - token.MintBurnChecks.MvxSafeBurnValue = big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 1010 - 50) - token.MintBurnChecks.EthSafeMintValue = big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 1010 - 50) + token.MintBurnChecks.MvxTotalUniversalBurn = big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 3000 - 50 + 1010 - 50) + token.MintBurnChecks.MvxSafeBurnValue = big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 3000 - 50 + 1010 - 50) + token.MintBurnChecks.EthSafeMintValue = big.NewInt(4010 - 50 + 6010 - 50 + 2010 - 50 + 3000 - 50 + 1010 - 50) } // GenerateUnlistedTokenFromEth will generate an unlisted token on Eth @@ -788,6 +822,93 @@ func GenerateUnlistedTokenFromMvx() framework.TestTokenParams { } } +// GenerateFrozenToken will generate a token that will be frozen +func GenerateFrozenToken() framework.TestTokenParams { + return framework.TestTokenParams{ + IssueTokenParams: framework.IssueTokenParams{ + AbstractTokenIdentifier: "FROZEN", + NumOfDecimalsUniversal: 2, + NumOfDecimalsChainSpecific: 2, + MvxUniversalTokenTicker: "FROZEN", + MvxChainSpecificTokenTicker: "FROZEN", + MvxUniversalTokenDisplayName: "TestFROZEN", + MvxChainSpecificTokenDisplayName: "TestFROZEN", + ValueToMintOnMvx: "10000000000", + IsMintBurnOnMvX: true, + IsNativeOnMvX: false, + HasChainSpecificToken: false, + EthTokenName: "EthFROZEN", + EthTokenSymbol: "FROZEN", + ValueToMintOnEth: "10000000000", + IsMintBurnOnEth: true, + IsNativeOnEth: true, + IsFrozen: true, + }, + TestOperations: []framework.TokenOperations{ + { + ValueToTransferToMvx: big.NewInt(2000), + ValueToSendFromMvX: nil, + }, + { + ValueToTransferToMvx: big.NewInt(1500), + ValueToSendFromMvX: nil, + MvxSCCallData: createScCallData("callPayable", 50000000), + }, + }, + DeltaBalances: map[framework.HalfBridgeIdentifier]framework.DeltaBalancesOnKeys{ + framework.FirstHalfBridge: map[string]*framework.DeltaBalanceHolder{ + framework.Alice: { + OnEth: big.NewInt(-2000 - 1500), + OnMvx: big.NewInt(0), + MvxToken: framework.UniversalToken, + }, + framework.Bob: { + OnEth: big.NewInt(0), + OnMvx: big.NewInt(0), + MvxToken: framework.UniversalToken, + }, + framework.SafeSC: { + OnEth: big.NewInt(0), + OnMvx: big.NewInt(0), + MvxToken: framework.ChainSpecificToken, + }, + framework.CalledTestSC: { + OnEth: big.NewInt(0), + OnMvx: big.NewInt(1500), + MvxToken: framework.UniversalToken, + }, + }, + framework.SecondHalfBridge: map[string]*framework.DeltaBalanceHolder{ + framework.Alice: { + OnEth: big.NewInt(-2000 - 1500 + 1950), + OnMvx: big.NewInt(0), + MvxToken: framework.UniversalToken, + }, + framework.Bob: { + OnEth: big.NewInt(0), + OnMvx: big.NewInt(0), + MvxToken: framework.UniversalToken, + }, + framework.Charlie: { + OnEth: big.NewInt(0), + OnMvx: big.NewInt(0), + MvxToken: framework.UniversalToken, + }, + framework.SafeSC: { + OnEth: big.NewInt(0), + OnMvx: big.NewInt(50), + MvxToken: framework.ChainSpecificToken, + }, + framework.CalledTestSC: { + OnEth: big.NewInt(0), + OnMvx: big.NewInt(0), + MvxToken: framework.UniversalToken, + }, + }, + }, + } +} + func createScCallData(function string, gasLimit uint64, args ...string) []byte { codec := testsCommon.TestMultiversXCodec{} callData := parsers.CallData{ @@ -797,5 +918,8 @@ func createScCallData(function string, gasLimit uint64, args ...string) []byte { Arguments: args, } - return codec.EncodeCallDataStrict(callData) + buff := codec.EncodeCallDataStrict(callData) + log.Info("working with SC call data", "buff", buff) + + return buff } diff --git a/integrationTests/relayers/slowTests/edgeCases_test.go b/integrationTests/relayers/slowTests/edgeCases_test.go index 2ca0f7b8..4af7bd4b 100644 --- a/integrationTests/relayers/slowTests/edgeCases_test.go +++ b/integrationTests/relayers/slowTests/edgeCases_test.go @@ -40,6 +40,7 @@ func TestRelayerShouldExecuteSimultaneousSwapsAndNotCatchErrors(t *testing.T) { }() usdcToken := GenerateTestUSDCToken() + usdcToken.MultipleSpendings = big.NewInt(2) usdcToken.TestOperations = []framework.TokenOperations{ { ValueToTransferToMvx: big.NewInt(5000), diff --git a/integrationTests/relayers/slowTests/framework/bridgeComponents.go b/integrationTests/relayers/slowTests/framework/bridgeComponents.go index e13f2ae5..ec1c92d4 100644 --- a/integrationTests/relayers/slowTests/framework/bridgeComponents.go +++ b/integrationTests/relayers/slowTests/framework/bridgeComponents.go @@ -90,7 +90,7 @@ func NewBridgeComponents( ProposeStatusBase: 10000000, ProposeStatusForEach: 7000000, PerformActionBase: 40000000, - PerformActionForEach: 5500000, + PerformActionForEach: 7000000, ScCallPerByte: 100000, ScCallPerformForEach: 10000000, } diff --git a/integrationTests/relayers/slowTests/framework/chainSimulatorWrapper.go b/integrationTests/relayers/slowTests/framework/chainSimulatorWrapper.go index 843b2231..e2450e53 100644 --- a/integrationTests/relayers/slowTests/framework/chainSimulatorWrapper.go +++ b/integrationTests/relayers/slowTests/framework/chainSimulatorWrapper.go @@ -37,6 +37,7 @@ const ( generateBlocksUntilTxProcessedEndpoint = "simulator/generate-blocks-until-transaction-processed/%s" numProbeRetries = 10 networkConfigEndpointTemplate = "network/status/%d" + codeMetadata = "0502" esdtSupplyEndpointTemplate = "network/esdt/supply/%s" ) @@ -130,7 +131,7 @@ func (instance *chainSimulatorWrapper) DeploySC(ctx context.Context, wasmFilePat require.Nil(instance.TB, err) scCode := wasm.GetSCCode(wasmFilePath) - params := []string{scCode, wasm.VMTypeHex, wasm.DummyCodeMetadataHex} + params := []string{scCode, wasm.VMTypeHex, codeMetadata} params = append(params, parameters...) txData := strings.Join(params, "@") diff --git a/integrationTests/relayers/slowTests/framework/ethereumHandler.go b/integrationTests/relayers/slowTests/framework/ethereumHandler.go index 50e75b54..df9e432a 100644 --- a/integrationTests/relayers/slowTests/framework/ethereumHandler.go +++ b/integrationTests/relayers/slowTests/framework/ethereumHandler.go @@ -351,13 +351,6 @@ func (handler *EthereumHandler) deployTestERC20Contract(ctx context.Context, par require.NoError(handler, err) require.Equal(handler, mintAmount.String(), balance.String()) - if params.IsNativeOnEth { - tx, err = ethMintBurnContract.Mint(auth, handler.AliceKeys.EthAddress, mintAmount) - require.NoError(handler, err) - handler.SimulatedChain.Commit() - handler.checkEthTxResult(ctx, tx.Hash()) - } - return ethMintBurnAddress, ethMintBurnContract } @@ -374,8 +367,8 @@ func (handler *EthereumHandler) deployTestERC20Contract(ctx context.Context, par 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.AliceKeys.EthAddress) + // mint to the depositor + handler.mintTokens(ctx, ethGenericTokenContract, params.ValueToMintOnEth, handler.DepositorKeys.EthAddress) if len(params.InitialSupplyValue) > 0 { handler.mintTokens(ctx, ethGenericTokenContract, params.InitialSupplyValue, handler.SafeAddress) } @@ -465,15 +458,18 @@ func (handler *EthereumHandler) SettleBatchOnEthereum() { } } -// 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) +// TransferToken will transfer the amount of tokens from one address to another +func (handler *EthereumHandler) TransferToken( + ctx context.Context, + params TestTokenParams, + from KeysHolder, + to KeysHolder, + amount *big.Int, +) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) - // mint erc20 token into eth safe - auth, _ := bind.NewKeyedTransactorWithChainID(handler.DepositorKeys.EthSK, handler.ChainID) - tx, err := token.EthErc20Contract.Mint(auth, handler.SafeAddress, valueToMint) + auth, _ := bind.NewKeyedTransactorWithChainID(from.EthSK, handler.ChainID) + tx, err := tkData.EthErc20Contract.Transfer(auth, to.EthAddress, amount) require.NoError(handler, err) handler.SimulatedChain.Commit() handler.checkEthTxResult(ctx, tx.Hash()) diff --git a/integrationTests/relayers/slowTests/framework/interface.go b/integrationTests/relayers/slowTests/framework/interface.go index 1ad94510..b7116509 100644 --- a/integrationTests/relayers/slowTests/framework/interface.go +++ b/integrationTests/relayers/slowTests/framework/interface.go @@ -61,6 +61,7 @@ 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) + Transfer(opts *bind.TransactOpts, to common.Address, value *big.Int) (*types.Transaction, error) } // TokensRegistry defines the registry used for the tokens in tests diff --git a/integrationTests/relayers/slowTests/framework/multiversxHandler.go b/integrationTests/relayers/slowTests/framework/multiversxHandler.go index e965b190..e633d0a2 100644 --- a/integrationTests/relayers/slowTests/framework/multiversxHandler.go +++ b/integrationTests/relayers/slowTests/framework/multiversxHandler.go @@ -22,6 +22,7 @@ const ( slashAmount = "00" zeroStringValue = "0" canAddSpecialRoles = "canAddSpecialRoles" + canFreeze = "canFreeze" trueStr = "true" esdtRoleLocalMint = "ESDTRoleLocalMint" esdtRoleLocalBurn = "ESDTRoleLocalBurn" @@ -44,11 +45,7 @@ const ( bridgeProxyContractPath = "testdata/contracts/mvx/bridge-proxy.wasm" testCallerContractPath = "testdata/contracts/mvx/test-caller.wasm" - setBridgeProxyContractAddressFunction = "setBridgeProxyContractAddress" - setWrappingContractAddressFunction = "setWrappingContractAddress" changeOwnerAddressFunction = "ChangeOwnerAddress" - setEsdtSafeOnMultiTransferFunction = "setEsdtSafeOnMultiTransfer" - setEsdtSafeAddressFunction = "setEsdtSafeAddress" moveRefundBatchToSafeFromChildContractFunction = "moveRefundBatchToSafeFromChildContract" getCurrentRefundBatchFunction = "getCurrentRefundBatch" stakeFunction = "stake" @@ -59,6 +56,7 @@ const ( pauseFunction = "pause" issueFunction = "issue" setSpecialRoleFunction = "setSpecialRole" + freezeFunction = "freeze" esdtTransferFunction = "ESDTTransfer" setPairDecimalsFunction = "setPairDecimals" addWrappedTokenFunction = "addWrappedToken" @@ -71,8 +69,6 @@ const ( submitBatchFunction = "submitBatch" unwrapTokenCreateTransactionFunction = "unwrapTokenCreateTransaction" createTransactionFunction = "createTransaction" - setBridgedTokensWrapperAddressFunction = "setBridgedTokensWrapperAddress" - setMultiTransferAddressFunction = "setMultiTransferAddress" withdrawRefundFeesForEthereumFunction = "withdrawRefundFeesForEthereum" getRefundFeesForEthereumFunction = "getRefundFeesForEthereum" withdrawTransactionFeesFunction = "withdrawTransactionFees" @@ -83,6 +79,8 @@ const ( getBurnBalancesFunction = "getBurnBalances" getTotalBalancesFunction = "getTotalBalances" getTokenLiquidityFunction = "getTokenLiquidity" + getRefundTransactions = "getRefundTransactions" + executeRefundTransaction = "executeRefundTransaction" ) var ( @@ -138,10 +136,6 @@ func NewMultiversxHandler( 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) } @@ -201,8 +195,6 @@ func (handler *MultiversxHandler) deployContracts(ctx context.Context) { handler.OwnerKeys.MvxSk, deployGasLimit, []string{ - handler.AggregatorAddress.Hex(), - handler.MultiTransferAddress.Hex(), "01", }, ) @@ -215,9 +207,7 @@ func (handler *MultiversxHandler) deployContracts(ctx context.Context) { bridgeProxyContractPath, handler.OwnerKeys.MvxSk, deployGasLimit, - []string{ - handler.MultiTransferAddress.Hex(), - }, + make([]string, 0), ) require.NotEqual(handler, emptyAddress, handler.ScProxyAddress) log.Info("Deploy: SC proxy contract", "address", handler.ScProxyAddress, "transaction hash", hash) @@ -229,6 +219,8 @@ func (handler *MultiversxHandler) deployContracts(ctx context.Context) { handler.SafeAddress.Hex(), handler.MultiTransferAddress.Hex(), handler.ScProxyAddress.Hex(), + handler.WrapperAddress.Hex(), + handler.AggregatorAddress.Hex(), minRelayerStakeHex, slashAmount, handler.Quorum} @@ -257,117 +249,6 @@ func (handler *MultiversxHandler) deployContracts(ctx context.Context) { log.Info("Deploy: test-caller contract", "address", handler.CalleeScAddress, "transaction hash", hash) } -func (handler *MultiversxHandler) wireMultiTransfer(ctx context.Context) { - // setBridgeProxyContractAddress - params := []string{ - handler.ScProxyAddress.Hex(), - } - hash, txResult := handler.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - handler.MultiTransferAddress, - zeroStringValue, - setCallsGasLimit, - setBridgeProxyContractAddressFunction, - params) - - log.Info("Set in multi-transfer contract the SC proxy contract", "transaction hash", hash, "status", txResult.Status) - - // setWrappingContractAddress - params = []string{ - handler.WrapperAddress.Hex(), - } - hash, txResult = handler.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - handler.MultiTransferAddress, - zeroStringValue, - setCallsGasLimit, - setWrappingContractAddressFunction, - params) - - 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 - params := []string{ - handler.WrapperAddress.Hex(), - } - hash, txResult := handler.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - handler.ScProxyAddress, - zeroStringValue, - setCallsGasLimit, - setBridgedTokensWrapperAddressFunction, - params) - - log.Info("Set in SC proxy contract the wrapper contract", "transaction hash", hash, "status", txResult.Status) - - // setMultiTransferAddress in SC bridge proxy - params = []string{ - handler.MultiTransferAddress.Hex(), - } - hash, txResult = handler.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - handler.ScProxyAddress, - zeroStringValue, - setCallsGasLimit, - setMultiTransferAddressFunction, - params) - - log.Info("Set in SC proxy contract the multi-transfer contract", "transaction hash", hash, "status", txResult.Status) - - // setEsdtSafeAddress on bridge proxy - params = []string{ - handler.SafeAddress.Hex(), - } - hash, txResult = handler.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - handler.ScProxyAddress, - zeroStringValue, - setCallsGasLimit, - setEsdtSafeAddressFunction, - params) - - log.Info("Set in SC proxy contract the safe contract", "transaction hash", hash, "status", txResult.Status) -} - -func (handler *MultiversxHandler) wireSafe(ctx context.Context) { - // setBridgedTokensWrapperAddress - params := []string{ - handler.WrapperAddress.Hex(), - } - hash, txResult := handler.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - handler.SafeAddress, - zeroStringValue, - setCallsGasLimit, - setBridgedTokensWrapperAddressFunction, - params) - - log.Info("Set in safe contract the wrapper contract", "transaction hash", hash, "status", txResult.Status) - - //setBridgeProxyContractAddress - params = []string{ - handler.ScProxyAddress.Hex(), - } - hash, txResult = handler.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - handler.SafeAddress, - zeroStringValue, - setCallsGasLimit, - setBridgeProxyContractAddressFunction, - params) - - 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 params := []string{ @@ -420,18 +301,6 @@ func (handler *MultiversxHandler) finishSettings(ctx context.Context) { 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.scCallAndCheckTx( - ctx, - handler.OwnerKeys, - 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) @@ -565,10 +434,13 @@ func (handler *MultiversxHandler) issueAndWhitelistTokensWithChainSpecific(ctx c if params.PreventWhitelist { return } + if params.IsFrozen { + handler.freezeToken(ctx, params) + } handler.setLocalRolesForUniversalTokenOnWrapper(ctx, params) - handler.transferChainSpecificTokenToSCs(ctx, params) handler.addUniversalTokenToWrapper(ctx, params) handler.whitelistTokenOnWrapper(ctx, params) + handler.transferChainSpecificTokenToSCs(ctx, params) handler.setRolesForSpecificTokenOnSafe(ctx, params) handler.addMappingInMultisig(ctx, params) handler.whitelistTokenOnMultisig(ctx, params) @@ -587,6 +459,9 @@ func (handler *MultiversxHandler) issueAndWhitelistTokens(ctx context.Context, p if params.PreventWhitelist { return } + if params.IsFrozen { + handler.freezeToken(ctx, params) + } handler.setRolesForSpecificTokenOnSafe(ctx, params) handler.addMappingInMultisig(ctx, params) @@ -610,6 +485,8 @@ func (handler *MultiversxHandler) issueUniversalToken(ctx context.Context, param hex.EncodeToString([]byte(params.MvxUniversalTokenTicker)), hex.EncodeToString(valueToMintInt.Bytes()), fmt.Sprintf("%02x", params.NumOfDecimalsUniversal), + hex.EncodeToString([]byte(canFreeze)), + hex.EncodeToString([]byte(trueStr)), hex.EncodeToString([]byte(canAddSpecialRoles)), hex.EncodeToString([]byte(trueStr))} @@ -636,6 +513,8 @@ func (handler *MultiversxHandler) issueChainSpecificToken(ctx context.Context, p hex.EncodeToString([]byte(params.MvxChainSpecificTokenTicker)), hex.EncodeToString(valueToMintInt.Bytes()), fmt.Sprintf("%02x", params.NumOfDecimalsChainSpecific), + hex.EncodeToString([]byte(canFreeze)), + hex.EncodeToString([]byte(trueStr)), hex.EncodeToString([]byte(canAddSpecialRoles)), hex.EncodeToString([]byte(trueStr))} @@ -941,6 +820,26 @@ func (handler *MultiversxHandler) setMaxBridgeAmountOnMultitransfer(ctx context. log.Info("multi-transfer set max bridge amount for token tx executed", "hash", hash, "status", txResult.Status) } +func (handler *MultiversxHandler) freezeToken(ctx context.Context, params IssueTokenParams) { + tkData := handler.TokensRegistry.GetTokenData(params.AbstractTokenIdentifier) + + scCallParams := []string{ + hex.EncodeToString([]byte(tkData.MvxUniversalToken)), + handler.BobKeys.MvxAddress.Hex(), + } + + hash, txResult := handler.scCallAndCheckTx( + ctx, + handler.OwnerKeys, + handler.ESDTSystemContractAddress, + zeroStringValue, + setCallsGasLimit, + freezeFunction, + scCallParams) + + log.Info("freeze 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 { @@ -1251,6 +1150,53 @@ func (handler *MultiversxHandler) scCallAndCheckTx( return hash, txResult } +// RefundAllFromScBridgeProxy will refund transactions from the bridge proxy, if existing +func (handler *MultiversxHandler) RefundAllFromScBridgeProxy(ctx context.Context) { + refundIDs := handler.getAllRefundIDsFromScBridgeProxy(ctx) + if len(refundIDs) == 0 { + return + } + + for _, refundID := range refundIDs { + handler.refundTransactionInScBridgeProxy(ctx, refundID) + } +} + +func (handler *MultiversxHandler) getAllRefundIDsFromScBridgeProxy(ctx context.Context) []uint64 { + responseBytes := handler.ChainSimulator.ExecuteVMQuery( + ctx, + handler.ScProxyAddress, + getRefundTransactions, + make([]string, 0), + ) + + numResponseLines := len(responseBytes) + require.Equal(handler, 0, numResponseLines%2, "expected an even number on response") + + refundIDs := make([]uint64, 0, numResponseLines/2) + for i := 0; i < numResponseLines; i += 2 { + refundID := big.NewInt(0).SetBytes(responseBytes[i]) + refundIDs = append(refundIDs, refundID.Uint64()) + } + + return refundIDs +} + +func (handler *MultiversxHandler) refundTransactionInScBridgeProxy(ctx context.Context, refundID uint64) { + log.Info("sending refund transaction in SC bridge proxy", "refund ID", refundID) + handler.scCallAndCheckTx( + ctx, + handler.SCExecutorKeys, // anyone can call this, for example, the sc executor + handler.ScProxyAddress, + "0", + generalSCCallGasLimit, + executeRefundTransaction, + []string{ + hex.EncodeToString(big.NewInt(0).SetUint64(refundID).Bytes()), + }, + ) +} + func getHexBool(input bool) string { if input { return hexTrue diff --git a/integrationTests/relayers/slowTests/framework/testSetup.go b/integrationTests/relayers/slowTests/framework/testSetup.go index d0792ed0..960c8ee5 100644 --- a/integrationTests/relayers/slowTests/framework/testSetup.go +++ b/integrationTests/relayers/slowTests/framework/testSetup.go @@ -161,12 +161,6 @@ func (setup *TestSetup) IssueAndConfigureTokens(tokens ...TestTokenParams) { setup.AddToken(token.IssueTokenParams) setup.EthereumHandler.IssueAndWhitelistToken(setup.Ctx, token.IssueTokenParams) setup.MultiversxHandler.IssueAndWhitelistToken(setup.Ctx, token.IssueTokenParams) - if token.IsNativeOnMvX { - setup.transferTokensToMvxTestKey(token) // TODO: (Next PRs) this will be moved an batch creation time - } - setup.ChainSimulator.GenerateBlocks(setup.Ctx, 10) - - esdtBalanceForSafe := setup.MultiversxHandler.GetESDTChainSpecificTokenBalance(setup.Ctx, setup.MultiversxHandler.SafeAddress, token.AbstractTokenIdentifier) setup.mutBalances.Lock() setup.initMvxInitialBalancesForUniversalUnsafe(token, @@ -189,6 +183,7 @@ func (setup *TestSetup) IssueAndConfigureTokens(tokens ...TestTokenParams) { ) setup.mutBalances.Unlock() + esdtBalanceForSafe := setup.MultiversxHandler.GetESDTChainSpecificTokenBalance(setup.Ctx, setup.MultiversxHandler.SafeAddress, token.AbstractTokenIdentifier) log.Info("recorded the ESDT balance for safe contract", "token", token.AbstractTokenIdentifier, "balance", esdtBalanceForSafe.String()) } @@ -380,11 +375,17 @@ func (setup *TestSetup) createBatchOnMultiversXForToken(params TestTokenParams) token := setup.GetTokenData(params.AbstractTokenIdentifier) require.NotNil(setup, token) - // TODO: transfer only required amount for deposit to the test key + setup.transferTokensToMvxTestKey(params, setup.AliceKeys) + setup.ChainSimulator.GenerateBlocks(setup.Ctx, 10) + + setup.mutBalances.Lock() + setup.initMvxInitialBalancesForUniversalUnsafe(params, setup.AliceKeys.MvxAddress) + setup.mutBalances.Unlock() + _ = setup.createDepositOnMultiversxForToken(setup.AliceKeys, setup.BobKeys, params) } -func (setup *TestSetup) transferTokensToMvxTestKey(params TestTokenParams) { +func (setup *TestSetup) transferTokensToMvxTestKey(params TestTokenParams, holder KeysHolder) { depositValue := big.NewInt(0) for _, operation := range params.TestOperations { if operation.ValueToSendFromMvX == nil { @@ -397,7 +398,7 @@ func (setup *TestSetup) transferTokensToMvxTestKey(params TestTokenParams) { setup.MultiversxHandler.TransferToken( setup.Ctx, setup.OwnerKeys, - setup.AliceKeys, + holder, depositValue, params.IssueTokenParams, ) @@ -421,8 +422,7 @@ func (setup *TestSetup) createDepositOnMultiversxForToken(from KeysHolder, to Ke } if operation.InvalidReceiver != nil && !setup.hasCallData(operation) { - invalidReceiver := common.Address(operation.InvalidReceiver) - to = KeysHolder{EthAddress: invalidReceiver} + to = KeysHolder{EthAddress: operation.InvalidReceiver.(common.Address)} } depositValue.Add(depositValue, operation.ValueToSendFromMvX) @@ -451,10 +451,37 @@ func (setup *TestSetup) createBatchOnEthereumForToken(mvxCalleeScAddress sdkCore token := setup.GetTokenData(params.AbstractTokenIdentifier) require.NotNil(setup, token) - // TODO: transfer only required amount for deposit to the test key + setup.transferTokensToEthTestKey(params, setup.AliceKeys) + + setup.mutBalances.Lock() + setup.initEthInitialBalancesUnsafe(params, setup.AliceKeys.EthAddress) + setup.mutBalances.Unlock() + setup.createDepositOnEthereumForToken(setup.AliceKeys, setup.BobKeys, mvxCalleeScAddress, params) } +func (setup *TestSetup) transferTokensToEthTestKey(params TestTokenParams, holder KeysHolder) { + depositValue := big.NewInt(0) + for _, operation := range params.TestOperations { + if operation.ValueToTransferToMvx == nil { + continue + } + + depositValue.Add(depositValue, operation.ValueToTransferToMvx) + } + + if params.MultipleSpendings != nil { + depositValue.Mul(depositValue, params.MultipleSpendings) + } + + setup.EthereumHandler.TransferToken( + setup.Ctx, + params, + setup.DepositorKeys, + holder, + depositValue) +} + // SendFromEthereumToMultiversX will create the deposits that will be gathered in a batch on Ethereum func (setup *TestSetup) SendFromEthereumToMultiversX(from KeysHolder, to KeysHolder, mvxTestCallerAddress sdkCore.AddressHandler, tokensParams ...TestTokenParams) { for _, params := range tokensParams { @@ -486,7 +513,7 @@ func (setup *TestSetup) createDepositOnEthereumForToken(from KeysHolder, to Keys } if operation.InvalidReceiver != nil { - invalidReceiver := NewMvxAddressFromBytes(setup, operation.InvalidReceiver) + invalidReceiver := NewMvxAddressFromBytes(setup, operation.InvalidReceiver.([]byte)) if setup.hasCallData(operation) { targetSCAddress = invalidReceiver @@ -516,6 +543,10 @@ func (setup *TestSetup) TestWithdrawTotalFeesOnEthereumForTokens(tokensParams .. } for _, operation := range param.TestOperations { + if operation.IsFaultyDeposit { + continue + } + if operation.InvalidReceiver != nil { expectedRefund.Add(expectedRefund, feeInt) } @@ -526,9 +557,6 @@ func (setup *TestSetup) TestWithdrawTotalFeesOnEthereumForTokens(tokensParams .. if operation.ValueToSendFromMvX.Cmp(zeroValueBigInt) == 0 { continue } - if operation.IsFaultyDeposit { - continue - } expectedAccumulated.Add(expectedAccumulated, feeInt) } diff --git a/integrationTests/relayers/slowTests/framework/types.go b/integrationTests/relayers/slowTests/framework/types.go index 333925a4..c6310a3b 100644 --- a/integrationTests/relayers/slowTests/framework/types.go +++ b/integrationTests/relayers/slowTests/framework/types.go @@ -14,6 +14,8 @@ type IssueTokenParams struct { InitialSupplyParams AbstractTokenIdentifier string PreventWhitelist bool + IsFrozen bool + MultipleSpendings *big.Int // MultiversX NumOfDecimalsUniversal int @@ -48,7 +50,7 @@ type TokenOperations struct { MvxFaultySCCall bool MvxForceSCCall bool IsFaultyDeposit bool - InvalidReceiver []byte + InvalidReceiver interface{} } // TestTokenParams defines a token collection of operations in one or 2 batches diff --git a/integrationTests/relayers/slowTests/refundWithChainSimulator_test.go b/integrationTests/relayers/slowTests/refundWithChainSimulator_test.go index 680a7820..093c5941 100644 --- a/integrationTests/relayers/slowTests/refundWithChainSimulator_test.go +++ b/integrationTests/relayers/slowTests/refundWithChainSimulator_test.go @@ -109,6 +109,69 @@ func TestRelayersShouldExecuteTransfersWithRefund(t *testing.T) { mexToken, ) }) + t.Run("empty function with no args should refund", func(t *testing.T) { + callData := createScCallData("", 50000000) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + ApplyUSDCRefundBalances(&usdcToken) + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + ApplyMEMERefundBalances(&memeToken) + + eurocToken := GenerateTestEUROCToken() + eurocToken.TestOperations[2].MvxSCCallData = callData + eurocToken.TestOperations[2].MvxFaultySCCall = true + ApplyEUROCRefundBalances(&eurocToken) + + mexToken := GenerateTestMEXToken() + mexToken.TestOperations[2].MvxSCCallData = callData + mexToken.TestOperations[2].MvxFaultySCCall = true + ApplyMEXRefundBalances(&mexToken) + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + eurocToken, + mexToken, + ) + }) + t.Run("empty function with args should refund", func(t *testing.T) { + dummyAddress := strings.Repeat("2", 32) + callData := createScCallData("", 50000000, dummyAddress) + usdcToken := GenerateTestUSDCToken() + usdcToken.TestOperations[2].MvxSCCallData = callData + usdcToken.TestOperations[2].MvxFaultySCCall = true + ApplyUSDCRefundBalances(&usdcToken) + + memeToken := GenerateTestMEMEToken() + memeToken.TestOperations[2].MvxSCCallData = callData + memeToken.TestOperations[2].MvxFaultySCCall = true + ApplyMEMERefundBalances(&memeToken) + + eurocToken := GenerateTestEUROCToken() + eurocToken.TestOperations[2].MvxSCCallData = callData + eurocToken.TestOperations[2].MvxFaultySCCall = true + ApplyEUROCRefundBalances(&eurocToken) + + mexToken := GenerateTestMEXToken() + mexToken.TestOperations[2].MvxSCCallData = callData + mexToken.TestOperations[2].MvxFaultySCCall = true + ApplyMEXRefundBalances(&mexToken) + + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + usdcToken, + memeToken, + eurocToken, + mexToken, + ) + }) t.Run("uninitialized contract should refund", func(t *testing.T) { callData := createScCallData("claim", 50000000) uninitializedSCAddressBytes, _ := data.NewAddressFromBech32String("erd1qqqqqqqqqqqqqpgqcc69ts8409p3h77q5chsaqz57y6hugvc4fvs64k74v") @@ -466,6 +529,13 @@ func TestRelayersShouldExecuteTransfersWithRefund(t *testing.T) { mexToken, ) }) + t.Run("frozen token for receiver should refund", func(t *testing.T) { + testRelayersWithChainSimulatorAndTokensAndRefund( + t, + make(chan error), + GenerateFrozenToken(), + ) + }) } func testRelayersWithChainSimulatorAndTokensAndRefund(tb testing.TB, manualStopChan chan error, tokens ...framework.TestTokenParams) { diff --git a/integrationTests/relayers/slowTests/testFlow.go b/integrationTests/relayers/slowTests/testFlow.go index 8fc816d7..671d9517 100644 --- a/integrationTests/relayers/slowTests/testFlow.go +++ b/integrationTests/relayers/slowTests/testFlow.go @@ -53,6 +53,9 @@ func (flow *testFlow) process() (finished bool) { flow.setup.MultiversxHandler.MoveRefundBatchToSafe(flow.setup.Ctx) } + //TODO: move this logic into the SC calls executor + flow.setup.MultiversxHandler.RefundAllFromScBridgeProxy(flow.setup.Ctx) + transferDoneForSecondHalf := flow.setup.AreAllTransfersCompleted(framework.SecondHalfBridge, flow.tokens...) if !flow.secondHalfBridgeDone && transferDoneForSecondHalf { flow.setup.CheckCorrectnessOnMintBurnTokens(flow.tokens...) diff --git a/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.abi.json b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.abi.json index 7b4939af..8898e1b6 100644 --- a/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.abi.json +++ b/integrationTests/relayers/slowTests/testdata/contracts/mvx/bridge-proxy.abi.json @@ -13,18 +13,12 @@ }, "framework": { "name": "multiversx-sc", - "version": "0.52.3" + "version": "0.53.2" } }, "name": "BridgeProxyContract", "constructor": { - "inputs": [ - { - "name": "opt_multi_transfer_address", - "type": "optional
", - "multi_arg": true - } - ], + "inputs": [], "outputs": [] }, "upgradeConstructor": { @@ -61,6 +55,17 @@ ], "outputs": [] }, + { + "name": "executeRefundTransaction", + "mutability": "mutable", + "inputs": [ + { + "name": "tx_id", + "type": "u32" + } + ], + "outputs": [] + }, { "name": "getPendingTransactionById", "mutability": "readonly", @@ -88,71 +93,39 @@ ] }, { - "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", + "name": "getRefundTransactionById", + "mutability": "readonly", "inputs": [ { - "name": "opt_address", - "type": "optional", - "multi_arg": true + "name": "tx_id", + "type": "u32" } ], - "outputs": [] - }, - { - "name": "getMultiTransferAddress", - "mutability": "readonly", - "inputs": [], "outputs": [ { - "type": "Address" + "type": "EthTransaction" } ] }, { - "name": "getBridgedTokensWrapperAddress", + "name": "getRefundTransactions", "mutability": "readonly", "inputs": [], "outputs": [ { - "type": "Address" + "type": "variadic