diff --git a/docs/config-file/node-config-doc.html b/docs/config-file/node-config-doc.html index 67e8abca6b..7a8fab3a3d 100644 --- a/docs/config-file/node-config-doc.html +++ b/docs/config-file/node-config-doc.html @@ -14,7 +14,7 @@
"300ms"
PollMinAllowedGasPriceInterval is the interval to poll the suggested min gas price for a tx
"1m"
"300ms"
-
AccountQueue represents the maximum number of non-executable transaction slots permitted per account
GlobalQueue represents the maximum number of non-executable transaction slots for all accounts
Enabled is a flag to enable/disable the effective gas price
L1GasPriceFactor is the percentage of the L1 gas price that will be used as the L2 min gas price
ByteGasCost is the gas cost per byte that is not 0
ZeroByteGasCost is the gas cost per byte that is 0
NetProfit is the profit margin to apply to the calculated breakEvenGasPrice
BreakEvenFactor is the factor to apply to the calculated breakevenGasPrice when comparing it with the gasPriceSigned of a tx
FinalDeviationPct is the max allowed deviation percentage BreakEvenGasPrice on re-calculation
EthTransferGasPrice is the fixed gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error
EthTransferL1GasPriceFactor is the percentage of L1 gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error
L2GasPriceSuggesterFactor is the factor to apply to L1 gas price to get the suggested L2 gas price used in the
calculations when the effective gas price is disabled (testing/metrics purposes)
ForkID is the current fork ID of the chain
TxFeeCap is the global transaction fee(price * gaslimit) cap for
send-transaction variants. The unit is ether. 0 means no cap.
XLayer config
BlockedList is the blocked address list
FreeGasAddress is the default free gas address
FreeClaimGasLimit is the max gas allowed use to do a free claim
BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method
EnableFreeGasByNonce enable free gas
FreeGasExAddress is the ex address which can be free gas for the transfer receiver
FreeGasCountPerAddr is the count limit of free gas tx per address
FreeGasLimit is the max gas allowed use to do a free gas tx
Host defines the network adapter that will be used to serve the HTTP requests
Port defines the port to serve the endpoints via HTTP
ReadTimeout is the HTTP server read timeout
check net/http.server.ReadTimeout and net/http.server.ReadHeaderTimeout
"1m"
+
AccountQueue represents the maximum number of non-executable transaction slots permitted per account
GlobalQueue represents the maximum number of non-executable transaction slots for all accounts
Enabled is a flag to enable/disable the effective gas price
L1GasPriceFactor is the percentage of the L1 gas price that will be used as the L2 min gas price
ByteGasCost is the gas cost per byte that is not 0
ZeroByteGasCost is the gas cost per byte that is 0
NetProfit is the profit margin to apply to the calculated breakEvenGasPrice
BreakEvenFactor is the factor to apply to the calculated breakevenGasPrice when comparing it with the gasPriceSigned of a tx
FinalDeviationPct is the max allowed deviation percentage BreakEvenGasPrice on re-calculation
EthTransferGasPrice is the fixed gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error
EthTransferL1GasPriceFactor is the percentage of L1 gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error
L2GasPriceSuggesterFactor is the factor to apply to L1 gas price to get the suggested L2 gas price used in the
calculations when the effective gas price is disabled (testing/metrics purposes)
ForkID is the current fork ID of the chain
TxFeeCap is the global transaction fee(price * gaslimit) cap for
send-transaction variants. The unit is ether. 0 means no cap.
XLayer config
BlockedList is the blocked address list
FreeGasAddress is the default free gas address
FreeClaimGasLimit is the max gas allowed use to do a free claim
BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method
EnableFreeGasByNonce enable free gas
FreeGasExAddress is the ex address which can be free gas for the transfer receiver
FreeGasCountPerAddr is the count limit of free gas tx per address
FreeGasLimit is the max gas allowed use to do a free gas tx
EnableFreeGasList enable the special project of XLayer for free gas
FreeGasList is the special project of XLayer
Host defines the network adapter that will be used to serve the HTTP requests
Port defines the port to serve the endpoints via HTTP
ReadTimeout is the HTTP server read timeout
check net/http.server.ReadTimeout and net/http.server.ReadHeaderTimeout
"1m"
"300ms"
WriteTimeout is the HTTP server write timeout
check net/http.server.WriteTimeout
"1m"
"300ms"
diff --git a/docs/config-file/node-config-doc.md b/docs/config-file/node-config-doc.md
index 607bda00b9..8c8281c85c 100644
--- a/docs/config-file/node-config-doc.md
+++ b/docs/config-file/node-config-doc.md
@@ -715,6 +715,8 @@ SecretKey=""
| - [FreeGasExAddress](#Pool_FreeGasExAddress ) | No | array of string | No | - | FreeGasExAddress is the ex address which can be free gas for the transfer receiver |
| - [FreeGasCountPerAddr](#Pool_FreeGasCountPerAddr ) | No | integer | No | - | FreeGasCountPerAddr is the count limit of free gas tx per address |
| - [FreeGasLimit](#Pool_FreeGasLimit ) | No | integer | No | - | FreeGasLimit is the max gas allowed use to do a free gas tx |
+| - [EnableFreeGasList](#Pool_EnableFreeGasList ) | No | boolean | No | - | EnableFreeGasList enable the special project of XLayer for free gas |
+| - [FreeGasList](#Pool_FreeGasList ) | No | array of object | No | - | FreeGasList is the special project of XLayer |
### 7.1. `Pool.IntervalToRefreshBlockedAddresses`
@@ -1321,6 +1323,70 @@ FreeGasCountPerAddr=0
FreeGasLimit=0
```
+### 7.24. `Pool.EnableFreeGasList`
+
+**Type:** : `boolean`
+
+**Default:** `false`
+
+**Description:** EnableFreeGasList enable the special project of XLayer for free gas
+
+**Example setting the default value** (false):
+```
+[Pool]
+EnableFreeGasList=false
+```
+
+### 7.25. `Pool.FreeGasList`
+
+**Type:** : `array of object`
+**Description:** FreeGasList is the special project of XLayer
+
+| | Array restrictions |
+| -------------------- | ------------------ |
+| **Min items** | N/A |
+| **Max items** | N/A |
+| **Items unicity** | False |
+| **Additional items** | False |
+| **Tuple validation** | See below |
+
+| Each item of this array must be | Description |
+| -------------------------------------------- | ----------------------------------------------------------- |
+| [FreeGasList items](#Pool_FreeGasList_items) | FreeGasInfo contains the details for what tx should be free |
+
+#### 7.25.1. [Pool.FreeGasList.FreeGasList items]
+
+**Type:** : `object`
+**Description:** FreeGasInfo contains the details for what tx should be free
+
+| Property | Pattern | Type | Deprecated | Definition | Title/Description |
+| --------------------------------------------------------------- | ------- | --------------- | ---------- | ---------- | ----------------- |
+| - [Name](#Pool_FreeGasList_items_Name ) | No | string | No | - | - |
+| - [FromList](#Pool_FreeGasList_items_FromList ) | No | array of string | No | - | - |
+| - [ToList](#Pool_FreeGasList_items_ToList ) | No | array of string | No | - | - |
+| - [MethodSigs](#Pool_FreeGasList_items_MethodSigs ) | No | array of string | No | - | - |
+| - [GasPriceMultiple](#Pool_FreeGasList_items_GasPriceMultiple ) | No | number | No | - | - |
+
+##### 7.25.1.1. `Pool.FreeGasList.FreeGasList items.Name`
+
+**Type:** : `string`
+
+##### 7.25.1.2. `Pool.FreeGasList.FreeGasList items.FromList`
+
+**Type:** : `array of string`
+
+##### 7.25.1.3. `Pool.FreeGasList.FreeGasList items.ToList`
+
+**Type:** : `array of string`
+
+##### 7.25.1.4. `Pool.FreeGasList.FreeGasList items.MethodSigs`
+
+**Type:** : `array of string`
+
+##### 7.25.1.5. `Pool.FreeGasList.FreeGasList items.GasPriceMultiple`
+
+**Type:** : `number`
+
## 8. `[RPC]`
**Type:** : `object`
@@ -2080,7 +2146,7 @@ SpecialApis=[]
| ----------------------------------------------------- | ---------------------------------------------------------- |
| [SpecialApis items](#RPC_RateLimit_SpecialApis_items) | RateLimitItem defines the special rate limit for some apis |
-##### 8.25.5.1. [RPC.RateLimit.SpecialApis.SpecialApis items]
+##### 8.25.5.1. [RPC.RateLimit.SpecialApis.SpecialApis items]
**Type:** : `object`
**Description:** RateLimitItem defines the special rate limit for some apis
@@ -2315,7 +2381,7 @@ ApiKeys=[]
| ----------------------------------------------------- | --------------------------- |
| [ApiKeys items](#RPC_ApiAuthentication_ApiKeys_items) | KeyItem is the api key item |
-##### 8.29.2.1. [RPC.ApiAuthentication.ApiKeys.ApiKeys items]
+##### 8.29.2.1. [RPC.ApiAuthentication.ApiKeys.ApiKeys items]
**Type:** : `object`
**Description:** KeyItem is the api key item
@@ -4633,7 +4699,7 @@ RollupManagerBlockNumber=0
| ----------------------------------------------------- | ------------------------------------------------------------------------- |
| [Actions items](#NetworkConfig_Genesis_Actions_items) | GenesisAction represents one of the values set on the SMT during genesis. |
-##### 13.3.4.1. [NetworkConfig.Genesis.Actions.Actions items]
+##### 13.3.4.1. [NetworkConfig.Genesis.Actions.Actions items]
**Type:** : `object`
**Description:** GenesisAction represents one of the values set on the SMT during genesis.
@@ -5513,7 +5579,7 @@ ChainID=0
| ----------------------------------------------------- | ------------------------------------ |
| [ForkIDIntervals items](#State_ForkIDIntervals_items) | ForkIDInterval is a fork id interval |
-#### 20.3.1. [State.ForkIDIntervals.ForkIDIntervals items]
+#### 20.3.1. [State.ForkIDIntervals.ForkIDIntervals items]
**Type:** : `object`
**Description:** ForkIDInterval is a fork id interval
diff --git a/docs/config-file/node-config-schema.json b/docs/config-file/node-config-schema.json
index 847d43b296..9414fe86d0 100644
--- a/docs/config-file/node-config-schema.json
+++ b/docs/config-file/node-config-schema.json
@@ -505,6 +505,46 @@
"type": "integer",
"description": "FreeGasLimit is the max gas allowed use to do a free gas tx",
"default": 0
+ },
+ "EnableFreeGasList": {
+ "type": "boolean",
+ "description": "EnableFreeGasList enable the special project of XLayer for free gas",
+ "default": false
+ },
+ "FreeGasList": {
+ "items": {
+ "properties": {
+ "Name": {
+ "type": "string"
+ },
+ "FromList": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "ToList": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "MethodSigs": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "GasPriceMultiple": {
+ "type": "number"
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "description": "FreeGasInfo contains the details for what tx should be free"
+ },
+ "type": "array",
+ "description": "FreeGasList is the special project of XLayer"
}
},
"additionalProperties": false,
diff --git a/pool/apollo_xlayer.go b/pool/apollo_xlayer.go
index bad98fad87..16ea4953cd 100644
--- a/pool/apollo_xlayer.go
+++ b/pool/apollo_xlayer.go
@@ -21,6 +21,11 @@ type apolloConfig struct {
FreeGasCountPerAddr uint64
FreeGasLimit uint64
+ // for special project
+ EnableFreeGasList bool
+ FreeGasFromNameMap map[string]string // map[from]projectName
+ FreeGasList map[string]*FreeGasInfo // map[projectName]FreeGasInfo
+
BlockedList []string
sync.RWMutex
@@ -43,6 +48,21 @@ func (c *apolloConfig) enable() bool {
return c.EnableApollo
}
+func (c *apolloConfig) setFreeGasList(freeGasList []FreeGasInfo) {
+ if c == nil || !c.EnableApollo {
+ return
+ }
+ c.FreeGasFromNameMap = make(map[string]string)
+ c.FreeGasList = make(map[string]*FreeGasInfo, len(freeGasList))
+ for _, info := range freeGasList {
+ for _, from := range info.FromList {
+ c.FreeGasFromNameMap[from] = info.Name
+ }
+ infoCopy := info
+ c.FreeGasList[info.Name] = &infoCopy
+ }
+}
+
func (c *apolloConfig) setFreeGasAddresses(freeGasAddrs []string) {
if c == nil || !c.EnableApollo {
return
@@ -95,6 +115,8 @@ func UpdateConfig(apolloConfig Config) {
getApolloConfig().setFreeGasExAddresses(apolloConfig.FreeGasExAddress)
getApolloConfig().FreeGasCountPerAddr = apolloConfig.FreeGasCountPerAddr
getApolloConfig().FreeGasLimit = apolloConfig.FreeGasLimit
+ getApolloConfig().EnableFreeGasList = apolloConfig.EnableFreeGasList
+ getApolloConfig().setFreeGasList(apolloConfig.FreeGasList)
getApolloConfig().Unlock()
}
@@ -119,10 +141,10 @@ func isFreeGasAddress(localFreeGasAddrs []string, address common.Address) bool {
if getApolloConfig().enable() {
getApolloConfig().RLock()
defer getApolloConfig().RUnlock()
- return contains(getApolloConfig().FreeGasAddresses, address)
+ return Contains(getApolloConfig().FreeGasAddresses, address)
}
- return contains(localFreeGasAddrs, address)
+ return Contains(localFreeGasAddrs, address)
}
func getEnableFreeGasByNonce(enableFreeGasByNonce bool) bool {
@@ -139,10 +161,10 @@ func isFreeGasExAddress(localFreeGasExAddrs []string, address common.Address) bo
if getApolloConfig().enable() {
getApolloConfig().RLock()
defer getApolloConfig().RUnlock()
- return contains(getApolloConfig().FreeGasExAddress, address)
+ return Contains(getApolloConfig().FreeGasExAddress, address)
}
- return contains(localFreeGasExAddrs, address)
+ return Contains(localFreeGasExAddrs, address)
}
func getFreeGasCountPerAddr(localFreeGasCountPerAddr uint64) uint64 {
@@ -197,8 +219,39 @@ func isBlockedAddress(localBlockedList []string, address common.Address) bool {
if getApolloConfig().enable() {
getApolloConfig().RLock()
defer getApolloConfig().RUnlock()
- return contains(getApolloConfig().BlockedList, address)
+ return Contains(getApolloConfig().BlockedList, address)
+ }
+
+ return Contains(localBlockedList, address)
+}
+
+// GetEnableSpecialFreeGasList returns enable flag of FreeGasList
+func GetEnableSpecialFreeGasList(enableFreeGasList bool) bool {
+ if getApolloConfig().enable() {
+ getApolloConfig().RLock()
+ defer getApolloConfig().RUnlock()
+ return getApolloConfig().EnableFreeGasList
+ }
+ return enableFreeGasList
+}
+
+// GetSpecialFreeGasList returns the special project in XLayer for free gas
+func GetSpecialFreeGasList(freeGasList []FreeGasInfo) (map[string]string, map[string]*FreeGasInfo) {
+ if getApolloConfig().enable() {
+ getApolloConfig().RLock()
+ defer getApolloConfig().RUnlock()
+ return getApolloConfig().FreeGasFromNameMap, getApolloConfig().FreeGasList
+ }
+
+ freeGasFromNameMap := make(map[string]string)
+ freeGasMap := make(map[string]*FreeGasInfo, len(freeGasList))
+ for _, info := range freeGasList {
+ for _, from := range info.FromList {
+ freeGasFromNameMap[from] = info.Name
+ }
+ infoCopy := info
+ freeGasMap[info.Name] = &infoCopy
}
- return contains(localBlockedList, address)
+ return freeGasFromNameMap, freeGasMap
}
diff --git a/pool/config.go b/pool/config.go
index 283788a706..a7cd61baa1 100644
--- a/pool/config.go
+++ b/pool/config.go
@@ -71,6 +71,19 @@ type Config struct {
FreeGasCountPerAddr uint64 `mapstructure:"FreeGasCountPerAddr"`
// FreeGasLimit is the max gas allowed use to do a free gas tx
FreeGasLimit uint64 `mapstructure:"FreeGasLimit"`
+ // EnableFreeGasList enable the special project of XLayer for free gas
+ EnableFreeGasList bool `mapstructure:"EnableFreeGasList"`
+ // FreeGasList is the special project of XLayer
+ FreeGasList []FreeGasInfo `mapstructure:"FreeGasList"`
+}
+
+// FreeGasInfo contains the details for what tx should be free
+type FreeGasInfo struct {
+ Name string `mapstructure:"Name"`
+ FromList []string `mapstructure:"FromList"`
+ ToList []string `mapstructure:"ToList"`
+ MethodSigs []string `mapstructure:"MethodSigs"`
+ GasPriceMultiple float64 `mapstructure:"GasPriceMultiple"`
}
// EffectiveGasPriceCfg contains the configuration properties for the effective gas price
diff --git a/pool/pool_xlayer.go b/pool/pool_xlayer.go
index ce7b4f1f3a..e6d2e16713 100644
--- a/pool/pool_xlayer.go
+++ b/pool/pool_xlayer.go
@@ -31,7 +31,8 @@ const (
MainnetBridgeURL = "https://www.okx.com/xlayer/bridge"
)
-func contains(s []string, ele common.Address) bool {
+// Contains returns if string[] contains ele string
+func Contains(s []string, ele common.Address) bool {
for _, e := range s {
if common.HexToAddress(e) == ele {
return true
@@ -40,6 +41,16 @@ func contains(s []string, ele common.Address) bool {
return false
}
+// ContainsMethod returns if data has prefix of method sig
+func ContainsMethod(data string, methods []string) bool {
+ for _, m := range methods {
+ if strings.HasPrefix(data, m) {
+ return true
+ }
+ }
+ return false
+}
+
// StartRefreshingWhiteAddressesPeriodically will make this instance of the pool
// to check periodically(accordingly to the configuration) for updates regarding
// the white address and update the in memory blocked addresses
@@ -113,9 +124,23 @@ func (p *Pool) GetDynamicGasPrice() *big.Int {
}
func (p *Pool) checkFreeGp(ctx context.Context, poolTx Transaction, from common.Address) (bool, error) {
- if isFreeGasAddress(p.cfg.FreeGasAddress, from) && poolTx.IsClaims { // claim tx
+ // claim tx
+ if isFreeGasAddress(p.cfg.FreeGasAddress, from) && poolTx.IsClaims {
return true, nil
}
+
+ // special project
+ if GetEnableSpecialFreeGasList(p.cfg.EnableFreeGasList) {
+ fromToName, freeGpList := GetSpecialFreeGasList(p.cfg.FreeGasList)
+ info := freeGpList[fromToName[from.String()]]
+ if info != nil &&
+ Contains(info.ToList, *poolTx.To()) &&
+ ContainsMethod("0x"+common.Bytes2Hex(poolTx.Data()), info.MethodSigs) {
+ return true, nil
+ }
+ }
+
+ // new bridge address
if getEnableFreeGasByNonce(p.cfg.EnableFreeGasByNonce) && poolTx.GasPrice().Cmp(big.NewInt(0)) == 0 { // free-gas tx by count
isFreeAddr, err := p.storage.IsFreeGasAddr(ctx, from)
if err != nil {
diff --git a/sequencer/sequencer.go b/sequencer/sequencer.go
index 2ba8dfb96d..c99ef4f661 100644
--- a/sequencer/sequencer.go
+++ b/sequencer/sequencer.go
@@ -231,18 +231,19 @@ func (s *Sequencer) addTxToWorker(ctx context.Context, tx pool.Transaction) erro
return err
}
- // XLayer claim tx
- freeGp, isClaimTx := s.checkFreeGas(tx, txTracker)
+ // XLayer free gas tx
+ freeGp, isClaimTx, gp := s.checkFreeGas(tx, txTracker)
if freeGp {
defaultGp := s.pool.GetDynamicGasPrice()
- baseGp := s.worker.getBaseClaimGp(defaultGp)
- copyBaseGp := new(big.Int).Set(baseGp)
+ copyBaseGp := new(big.Int)
if isClaimTx {
txTracker.IsClaimTx = true
- txTracker.GasPrice = copyBaseGp.Mul(copyBaseGp, new(big.Int).SetUint64(uint64(getGasPriceMultiple(s.cfg.GasPriceMultiple))))
+ baseGp := s.worker.getBaseClaimGp(defaultGp)
+ copyBaseGp.Set(baseGp)
} else {
- txTracker.GasPrice = defaultGp.Mul(defaultGp, new(big.Int).SetUint64(uint64(getInitGasPriceMultiple(s.cfg.InitGasPriceMultiple))))
+ copyBaseGp.Set(defaultGp)
}
+ txTracker.GasPrice = copyBaseGp.Mul(copyBaseGp, new(big.Int).SetUint64(uint64(gp)))
}
replacedTx, dropReason := s.worker.AddTxTracker(ctx, txTracker)
diff --git a/sequencer/sequencer_xlayer.go b/sequencer/sequencer_xlayer.go
index 838c9a8e4e..3a27452f22 100644
--- a/sequencer/sequencer_xlayer.go
+++ b/sequencer/sequencer_xlayer.go
@@ -9,6 +9,7 @@ import (
"github.com/0xPolygonHermez/zkevm-node/pool"
pmetric "github.com/0xPolygonHermez/zkevm-node/sequencer/metrics"
"github.com/0xPolygonHermez/zkevm-node/state"
+ "github.com/ethereum/go-ethereum/common"
)
var countinterval = 10
@@ -36,14 +37,29 @@ func (s *Sequencer) countReadyTx() {
state.InfiniteSafeRun(s.updateReadyTxCount, "error counting ready tx", time.Second)
}
-func (s *Sequencer) checkFreeGas(tx pool.Transaction, txTracker *TxTracker) (freeGp, claimTx bool) {
+func (s *Sequencer) checkFreeGas(tx pool.Transaction, txTracker *TxTracker) (freeGp, claimTx bool, gpMul float64) {
// check if tx is init-free-gas and if it can be prior pack
freeGp = tx.GasPrice().Cmp(big.NewInt(0)) == 0
+ gpMul = getInitGasPriceMultiple(s.cfg.InitGasPriceMultiple)
// check if tx is bridge-claim
addrs := getPackBatchSpacialList(s.cfg.PackBatchSpacialList)
if addrs[txTracker.FromStr] {
claimTx = true
+ gpMul = getGasPriceMultiple(s.cfg.GasPriceMultiple)
+ return
+ }
+
+ // check if tx is from special project
+ if pool.GetEnableSpecialFreeGasList(s.poolCfg.EnableFreeGasList) {
+ fromToName, freeGpList := pool.GetSpecialFreeGasList(s.poolCfg.FreeGasList)
+ info := freeGpList[fromToName[txTracker.FromStr]]
+ if info != nil &&
+ pool.Contains(info.ToList, *tx.To()) &&
+ pool.ContainsMethod("0x"+common.Bytes2Hex(tx.Data()), info.MethodSigs) {
+ gpMul = info.GasPriceMultiple
+ return
+ }
}
return
diff --git a/test/Makefile b/test/Makefile
index d1df75cd76..01b7da898c 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,4 +1,4 @@
-DOCKERCOMPOSE := docker-compose -f docker-compose.yml
+DOCKERCOMPOSE := docker compose -f docker-compose.yml
DOCKERCOMPOSEAPPSEQ := xlayer-sequencer
DOCKERCOMPOSEAPPSEQSENDER := xlayer-sequence-sender
DOCKERCOMPOSEAPPSIGNER := xlayer-signer