Skip to content

Commit

Permalink
Merge pull request #9 from tokamak-network/OR-1734-add-usdcBridge-han…
Browse files Browse the repository at this point in the history
…dler-for-thanos-event-listener

Or 1734 add usdc bridge handler for thanos event listener
  • Loading branch information
xxeonge authored Jul 8, 2024
2 parents a2f1d51 + 2c1884e commit 12e1c8a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 34 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/release-docker-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,18 @@ jobs:

- name: Update notif_thanos Kubernetes resources
run: |
cd tokamak-infra/apps/notif_thanos/kustomize/overlays/aws/sepolia-nightly
cd tokamak-infra/apps/notif_thanos/kustomize/overlays/aws/sepolia
kustomize edit set image tokamaknetwork/thanos-event-listener:${{ needs.version.outputs.nightly-docker-tag}}
- name: Show updated Kubernetes resources
run: |
cd tokamak-infra/apps/notif_thanos/kustomize/overlays/aws/sepolia-nightly
cd tokamak-infra/apps/notif_thanos/kustomize/overlays/aws/sepolia
cat kustomization.yml
- name: Commit and push changes
uses: EndBug/add-and-commit@v9
with:
default_author: github_actions
github_token: ${{ secrets.ACCESS_TOKEN }}
message: '[AUTO] Update thanos-event-listener images tag for sepolia-nightly'
message: '[AUTO] Update thanos-event-listener images tag for sepolia'
cwd: 'tokamak-infra/'
82 changes: 53 additions & 29 deletions internal/app/thanos-notif/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ type Notifier interface {
}

type App struct {
cfg *Config
notifier Notifier
tokenInfo map[string]TokenInfo
cfg *Config
notifier Notifier
tonAddress string
tokenInfo map[string]TokenInfo
}

func (app *App) ETHDepEvent(vLog *types.Log) {
Expand All @@ -58,7 +59,7 @@ func (app *App) ETHDepEvent(vLog *types.Log) {

// Slack notify title and text
title := fmt.Sprintf("[" + app.cfg.Network + "] [ETH Deposit Initialized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nAmount: %+v ETH", vLog.TxHash, ethDep.From, ethDep.To, Amount)
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nAmount: %s ETH", vLog.TxHash, ethDep.From, ethDep.To, Amount)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -87,7 +88,7 @@ func (app *App) ETHWithEvent(vLog *types.Log) {

// Slack notify title and text
title := fmt.Sprintf("[" + app.cfg.Network + "] [ETH Withdrawal Finalized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nAmount: %+v ETH", vLog.TxHash, ethWith.From, ethWith.To, Amount)
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nAmount: %s ETH", vLog.TxHash, ethWith.From, ethWith.To, Amount)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -128,8 +129,16 @@ func (app *App) ERC20DepEvent(vLog *types.Log) {
Amount := app.formatAmount(erc20Dep.Amount, tokenDecimals)

// Slack notify title and text
title := fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Deposit Initialized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, erc20Dep.From, erc20Dep.To, erc20Dep.L1Token, erc20Dep.L2Token, Amount, tokenSymbol)
var title string

isTON := tokenAddress.Cmp(common.HexToAddress(app.tonAddress)) == 0

if isTON {
title = fmt.Sprintf("[" + app.cfg.Network + "] [TON Deposit Initialized]")
} else {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Deposit Initialized]")
}
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, erc20Dep.From, erc20Dep.To, erc20Dep.L1Token, erc20Dep.L2Token, Amount, tokenSymbol)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -170,8 +179,16 @@ func (app *App) ERC20WithEvent(vLog *types.Log) {
Amount := app.formatAmount(erc20With.Amount, tokenDecimals)

// Slack notify title and text
title := fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Withdrawal Finalized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, erc20With.From, erc20With.To, erc20With.L1Token, erc20With.L2Token, Amount, tokenSymbol)
var title string

isTON := tokenAddress.Cmp(common.HexToAddress(app.tonAddress)) == 0

if isTON {
title = fmt.Sprintf("[" + app.cfg.Network + "] [TON Withdrawal Finalized]")
} else {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Withdrawal Finalized]")
}
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, erc20With.From, erc20With.To, erc20With.L1Token, erc20With.L2Token, Amount, tokenSymbol)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -207,10 +224,10 @@ func (app *App) L2DepEvent(vLog *types.Log) {
isTON := tokenAddress.Cmp(common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")) == 0

if isETH {
tokenSymbol = " ETH"
tokenSymbol = "ETH"
tokenDecimals = 18
} else if isTON {
tokenSymbol = " TON"
tokenSymbol = "TON"
tokenDecimals = 18
} else {
tokenInfo, found := app.tokenInfo[tokenAddress.Hex()]
Expand All @@ -229,13 +246,13 @@ func (app *App) L2DepEvent(vLog *types.Log) {

if isETH {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ETH Deposit Finalized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: ETH\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, l2Dep.From, l2Dep.To, l2Dep.L2Token, Amount, tokenSymbol)
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: ETH\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, l2Dep.From, l2Dep.To, l2Dep.L2Token, Amount, tokenSymbol)
} else if isTON {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Deposit Finalized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: NativeToken\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, l2Dep.From, l2Dep.To, l2Dep.L2Token, Amount, tokenSymbol)
title = fmt.Sprintf("[" + app.cfg.Network + "] [TON Deposit Finalized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, l2Dep.From, l2Dep.To, app.tonAddress, l2Dep.L2Token, Amount, tokenSymbol)
} else {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Deposit Finalized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, l2Dep.From, l2Dep.To, l2Dep.L1Token, l2Dep.L2Token, Amount, tokenSymbol)
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, l2Dep.From, l2Dep.To, l2Dep.L1Token, l2Dep.L2Token, Amount, tokenSymbol)
}

app.notifier.Notify(title, text)
Expand Down Expand Up @@ -267,15 +284,15 @@ func (app *App) L2WithEvent(vLog *types.Log) {
var tokenSymbol string
var tokenDecimals int

tokenAddress := l2With.L1Token
tokenAddress := l2With.L2Token
isETH := tokenAddress.Cmp(common.HexToAddress("0x4200000000000000000000000000000000000486")) == 0
isTON := tokenAddress.Cmp(common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000")) == 0

if isETH {
tokenSymbol = "ETH"
tokenDecimals = 18
} else if isTON {
tokenSymbol = " TON"
tokenSymbol = "TON"
tokenDecimals = 18
} else {
tokenInfo, found := app.tokenInfo[tokenAddress.Hex()]
Expand All @@ -294,13 +311,13 @@ func (app *App) L2WithEvent(vLog *types.Log) {

if isETH {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ETH Withdrawal Initialized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: ETH\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, l2With.From, l2With.To, l2With.L2Token, Amount, tokenSymbol)
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: ETH\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, l2With.From, l2With.To, l2With.L2Token, Amount, tokenSymbol)
} else if isTON {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Withdrawal Initialized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: NativeToken\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, l2With.From, l2With.To, l2With.L2Token, Amount, tokenSymbol)
title = fmt.Sprintf("[" + app.cfg.Network + "] [TON Withdrawal Initialized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, l2With.From, l2With.To, app.tonAddress, l2With.L2Token, Amount, tokenSymbol)
} else {
title = fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Withdrawal Initialized]")
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v%s", vLog.TxHash, l2With.From, l2With.To, l2With.L1Token, l2With.L2Token, Amount, tokenSymbol)
text = fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s %s", vLog.TxHash, l2With.From, l2With.To, l2With.L1Token, l2With.L2Token, Amount, tokenSymbol)
}

app.notifier.Notify(title, text)
Expand Down Expand Up @@ -331,8 +348,8 @@ func (app *App) L1UsdcDepEvent(vLog *types.Log) {
Amount := app.formatAmount(l1UsdcDep.Amount, 6)

// Slack notify title and text
title := fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Deposit Initialized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v USDC", vLog.TxHash, l1UsdcDep.From, l1UsdcDep.To, l1UsdcDep.L1Token, l1UsdcDep.L2Token, Amount)
title := fmt.Sprintf("[" + app.cfg.Network + "] [USDC Deposit Initialized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s USDC", vLog.TxHash, l1UsdcDep.From, l1UsdcDep.To, l1UsdcDep.L1Token, l1UsdcDep.L2Token, Amount)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -362,8 +379,8 @@ func (app *App) L1UsdcWithEvent(vLog *types.Log) {
Amount := app.formatAmount(l1UsdcWith.Amount, 6)

// Slack notify title and text
title := fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Withdrawal Finalized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v USDC", vLog.TxHash, l1UsdcWith.From, l1UsdcWith.To, l1UsdcWith.L1Token, l1UsdcWith.L2Token, Amount)
title := fmt.Sprintf("[" + app.cfg.Network + "] [USDC Withdrawal Finalized]")
text := fmt.Sprintf("Tx: "+app.cfg.L1ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s USDC", vLog.TxHash, l1UsdcWith.From, l1UsdcWith.To, l1UsdcWith.L1Token, l1UsdcWith.L2Token, Amount)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -392,8 +409,8 @@ func (app *App) L2UsdcDepEvent(vLog *types.Log) {

Amount := app.formatAmount(l2UsdcDep.Amount, 6)

title := fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Deposit Finalized]")
text := fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v USDC", vLog.TxHash, l2UsdcDep.From, l2UsdcDep.To, l2UsdcDep.L1Token, l2UsdcDep.L2Token, Amount)
title := fmt.Sprintf("[" + app.cfg.Network + "] [USDC Deposit Finalized]")
text := fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L1ExplorerUrl+"/address/%s\nTo: "+app.cfg.L2ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s USDC", vLog.TxHash, l2UsdcDep.From, l2UsdcDep.To, l2UsdcDep.L1Token, l2UsdcDep.L2Token, Amount)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -422,8 +439,8 @@ func (app *App) L2UsdcWithEvent(vLog *types.Log) {

Amount := app.formatAmount(l2UsdcWith.Amount, 6)

title := fmt.Sprintf("[" + app.cfg.Network + "] [ERC-20 Withdrawal Initialized]")
text := fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %+v USDC", vLog.TxHash, l2UsdcWith.From, l2UsdcWith.To, l2UsdcWith.L1Token, l2UsdcWith.L2Token, Amount)
title := fmt.Sprintf("[" + app.cfg.Network + "] [USDC Withdrawal Initialized]")
text := fmt.Sprintf("Tx: "+app.cfg.L2ExplorerUrl+"/tx/%s\nFrom: "+app.cfg.L2ExplorerUrl+"/address/%s\nTo: "+app.cfg.L1ExplorerUrl+"/address/%s\nL1Token: "+app.cfg.L1ExplorerUrl+"/token/%s\nL2Token: "+app.cfg.L2ExplorerUrl+"/token/%s\nAmount: %s USDC", vLog.TxHash, l2UsdcWith.From, l2UsdcWith.To, l2UsdcWith.L1Token, l2UsdcWith.L2Token, Amount)

app.notifier.Notify(title, text)
}
Expand Down Expand Up @@ -486,6 +503,13 @@ func (app *App) updateTokenInfo() error {
return err
}

for k, v := range tokenInfoMap {
if v.Symbol == "TON" {
app.tonAddress = k
break
}
}

app.tokenInfo = tokenInfoMap

return nil
Expand Down
14 changes: 12 additions & 2 deletions internal/app/thanos-notif/tokenInfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (data *Data) tokenInfoMap() (map[string]TokenInfo, error) {
tokenInfoMap[tokenAddress] = tokenInfo
tokenInfoMap[tokenAddressLower] = tokenInfo

fmt.Printf("Token Address: %s, Symbol: %+v, Decimals: %d\n", tokenAddress, symbol, decimals)
fmt.Printf("Token Address: %s, Symbol: %s, Decimals: %d\n", tokenAddress, symbol, decimals)
}

return tokenInfoMap, nil
Expand Down Expand Up @@ -128,7 +128,17 @@ func decodeHexString(hexStr string) (string, error) {
return "", err
}

decodedString := strings.TrimRight(string(resultBytes), "\x00")
decodedString := string(resultBytes)

decodedString = strings.Map(func(r rune) rune {
if r < 32 || r == 127 { // ASCII
return -1
}
return r
}, decodedString)

decodedString = strings.TrimSpace(decodedString)

return decodedString, nil
}

Expand Down

0 comments on commit 12e1c8a

Please sign in to comment.