Skip to content

Commit f176749

Browse files
committed
feat(transfer): show client status and trusted height on render page
The Transfer section listed each client by ID only, making it hard to tell which client is up-to-date or usable when several track the same chain. Add read-only core.ClientLatestHeight and core.ClientStatus accessors and surface both next to each client. Non-active clients (Expired, Frozen) are still listed but without a "send via" link, since packets can't be sent through them.
1 parent 01bc43e commit f176749

4 files changed

Lines changed: 105 additions & 5 deletions

File tree

gno.land/r/aib/ibc/apps/transfer/filetests/z0bb_render_home_with_client_filetest.gno

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func main(cur realm) {
3636
//
3737
// Trigger an IBC transfer from your wallet. Pick the client of the destination chain:
3838
//
39-
// - [send via `07-tendermint-1`](/r/aib/main$help&func=Transfer&amount=&clientID=07-tendermint-1&denom=&memo=&receiver=&timeoutTimestamp=1234571490) ([client details](/r/aib/ibc/core:clients/07-tendermint-1))
39+
// - [send via `07-tendermint-1`](/r/aib/main$help&func=Transfer&amount=&clientID=07-tendermint-1&denom=&memo=&receiver=&timeoutTimestamp=1234571490) — Active, trusted height `2/2` ([client details](/r/aib/ibc/core:clients/07-tendermint-1))
4040
//
4141
// ## Vouchers (0)
4242
//
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// PKGPATH: gno.land/r/aib/main
2+
package main
3+
4+
import (
5+
"testing"
6+
"time"
7+
8+
tmtesting "gno.land/p/aib/ibc/lightclient/tendermint/testing"
9+
"gno.land/p/aib/ibc/types"
10+
11+
"gno.land/r/aib/ibc/apps/transfer"
12+
"gno.land/r/aib/ibc/core"
13+
)
14+
15+
// renderHome with a non-active (Expired) client: confirms the Transfer section
16+
// still lists the client with its status and trusted height, but omits the
17+
// "send via" txlink since packets can't be sent through it. The client is
18+
// created Active, then block time is advanced past the trusting period to
19+
// expire it.
20+
func main(cur realm) {
21+
var (
22+
chainID = "chain-id-2"
23+
clientState = tmtesting.NewClientState(chainID, types.NewHeight(2, 2))
24+
apphash = tmtesting.Hash("apphash")
25+
trustedValset = tmtesting.GenValset()
26+
consensusState = tmtesting.GenConsensusState(time.Now(), apphash, trustedValset.Hash())
27+
)
28+
clientID := core.CreateClient(cross(cur), clientState, consensusState)
29+
core.RegisterCounterparty(cross(cur), clientID, [][]byte{[]byte("iavlStoreKey"), []byte("prefix2")}, "07-tendermint-2")
30+
31+
// Advance block time past the trusting period to expire the client.
32+
ctx := testing.GetContext()
33+
ctx.Time = time.Now().Add(clientState.TrustingPeriod)
34+
testing.SetContext(ctx)
35+
36+
println(transfer.Render(""))
37+
}
38+
39+
// Output:
40+
// # IBC transfer
41+
//
42+
// ICS-20 style transfer state and voucher token queries.
43+
//
44+
// ## Transfer
45+
//
46+
// Trigger an IBC transfer from your wallet. Pick the client of the destination chain:
47+
//
48+
// - `07-tendermint-1` — Expired, trusted height `2/2` ([client details](/r/aib/ibc/core:clients/07-tendermint-1))
49+
//
50+
// ## Vouchers (0)
51+
//
52+
// No vouchers yet.
53+
//
54+
// ## Escrow (0)
55+
//
56+
// No escrow yet.
57+
//
58+
// ## JSON endpoints
59+
//
60+
// - [`denoms`](/r/aib/ibc/apps/transfer:denoms): list known IBC denoms (`?page`, `?limit`)
61+
// - `denoms/ibc/{hash}`: get metadata for an IBC denom
62+
// - `total_escrow/{denom}`: get total escrow tracked for a base denom
63+
// - [`vouchers`](/r/aib/ibc/apps/transfer:vouchers): list voucher tokens (`?page`, `?limit`)
64+
// - `voucher/ibc/{hash}`: get voucher token metadata
65+
// - `voucher/ibc/{hash}/balance/{addr}`: get a voucher balance for an address

gno.land/r/aib/ibc/apps/transfer/render.gno

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"strings"
77
"time"
88

9+
"gno.land/p/aib/ibc/lightclient"
910
"gno.land/p/aib/jsonpage"
1011
"gno.land/p/moul/txlink"
1112
"gno.land/p/nt/mux/v0"
@@ -186,10 +187,20 @@ func renderTransferLinks() string {
186187

187188
out.WriteString("Trigger an IBC transfer from your wallet. Pick the client of the destination chain:\n\n")
188189
for _, clientID := range clientIDs {
189-
out.WriteString(ufmt.Sprintf(
190-
"- [send via `%s`](%s) ([client details](/r/aib/ibc/core:clients/%s))\n",
191-
clientID, newTransferLink(clientID, "", "").URL(), clientID,
192-
))
190+
status := core.ClientStatus(clientID)
191+
height := core.ClientLatestHeight(clientID)
192+
if status == lightclient.Active {
193+
out.WriteString(ufmt.Sprintf(
194+
"- [send via `%s`](%s) — %s, trusted height `%s` ([client details](/r/aib/ibc/core:clients/%s))\n",
195+
clientID, newTransferLink(clientID, "", "").URL(), status, height, clientID,
196+
))
197+
} else {
198+
// Non-active client: no send link, since packets can't be sent.
199+
out.WriteString(ufmt.Sprintf(
200+
"- `%s` — %s, trusted height `%s` ([client details](/r/aib/ibc/core:clients/%s))\n",
201+
clientID, status, height, clientID,
202+
))
203+
}
193204
}
194205

195206
// Per-voucher links: send back via IBC, plus local GRC20 send/approve.

gno.land/r/aib/ibc/core/client.gno

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,30 @@ func ClientIDs() []string {
5858
return ids
5959
}
6060

61+
// ClientLatestHeight returns the latest (highest) consensus height the given
62+
// client has been updated to, formatted as "<revision>/<height>". This is the
63+
// client's latest trusted height. Returns "" if the client is unknown.
64+
// Intended for read-only callers (UIs) that want to surface how up-to-date a
65+
// client is.
66+
func ClientLatestHeight(clientID string) string {
67+
c := store.getClient(clientID)
68+
if c == nil {
69+
return ""
70+
}
71+
return c.lightClient.LatestHeight().String()
72+
}
73+
74+
// ClientStatus returns the status of the given client (Active, Expired,
75+
// Frozen, ...). Returns "" if the client is unknown. Intended for read-only
76+
// callers (UIs) that want to surface whether a client is usable.
77+
func ClientStatus(clientID string) string {
78+
c := store.getClient(clientID)
79+
if c == nil {
80+
return ""
81+
}
82+
return c.lightClient.Status()
83+
}
84+
6185
// RegisterCounterparty will register the IBC v2 counterparty info for the
6286
// given clientID. It must be called by the same relayer that called
6387
// CreateClient.

0 commit comments

Comments
 (0)