@@ -3,12 +3,16 @@ package common
33import (
44 "fmt"
55 "os"
6+ "sort"
67
78 "github.com/spf13/cobra"
89
10+ staking "github.com/oasisprotocol/oasis-core/go/staking/api"
11+ "github.com/oasisprotocol/oasis-sdk/client-sdk/go/helpers"
912 "github.com/oasisprotocol/oasis-sdk/client-sdk/go/testing"
1013 "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types"
1114
15+ buildRoflProvider "github.com/oasisprotocol/cli/build/rofl/provider"
1216 "github.com/oasisprotocol/cli/config"
1317)
1418
@@ -41,19 +45,66 @@ func CheckForceErr(err interface{}) {
4145 cobra .CheckErr (errMsg )
4246}
4347
44- // GenAccountNames generates a map of all addresses -> account name for pretty printing.
48+ // GenAccountNames generates a map of all known native addresses -> account name for pretty printing.
49+ // It includes test accounts, configured networks (paratimes/ROFL defaults), addressbook and wallet.
50+ //
51+ // Priority order (later entries overwrite earlier):
52+ // test accounts < network entries < addressbook < wallet.
4553func GenAccountNames () types.AccountNames {
4654 an := types.AccountNames {}
47- for name , acc := range config .Global ().Wallet .All {
48- an [acc .GetAddress ().String ()] = name
55+
56+ // Test accounts have lowest priority.
57+ for name , acc := range testing .TestAccounts {
58+ an [acc .Address .String ()] = fmt .Sprintf ("test:%s" , name )
59+ }
60+
61+ // Network-derived entries (paratimes, ROFL providers) have second-lowest priority.
62+ cfg := config .Global ()
63+ netNames := make ([]string , 0 , len (cfg .Networks .All ))
64+ for name := range cfg .Networks .All {
65+ netNames = append (netNames , name )
66+ }
67+ sort .Strings (netNames )
68+ for _ , netName := range netNames {
69+ net := cfg .Networks .All [netName ]
70+ if net == nil {
71+ continue
72+ }
73+
74+ // Include ParaTime runtime addresses as paratime:<name>.
75+ ptNames := make ([]string , 0 , len (net .ParaTimes .All ))
76+ for ptName := range net .ParaTimes .All {
77+ ptNames = append (ptNames , ptName )
78+ }
79+ sort .Strings (ptNames )
80+ for _ , ptName := range ptNames {
81+ pt := net .ParaTimes .All [ptName ]
82+ if pt == nil {
83+ continue
84+ }
85+
86+ rtAddr := types .NewAddressFromConsensus (staking .NewRuntimeAddress (pt .Namespace ()))
87+ an [rtAddr .String ()] = fmt .Sprintf ("paratime:%s" , ptName )
88+
89+ // Include ROFL default provider addresses as rofl:provider:<paratime>.
90+ if svc , ok := buildRoflProvider .DefaultRoflServices [pt .ID ]; ok {
91+ if svc .Provider != "" {
92+ if a , _ , err := helpers .ResolveEthOrOasisAddress (svc .Provider ); err == nil && a != nil {
93+ an [a .String ()] = fmt .Sprintf ("rofl:provider:%s" , ptName )
94+ }
95+ }
96+ }
97+ }
4998 }
5099
51- for name , acc := range config .Global ().AddressBook .All {
100+ // Addressbook entries have medium priority.
101+ for name , acc := range cfg .AddressBook .All {
52102 an [acc .GetAddress ().String ()] = name
53103 }
54104
55- for name , acc := range testing .TestAccounts {
56- an [acc .Address .String ()] = fmt .Sprintf ("test:%s" , name )
105+ // Wallet entries have highest priority.
106+ for name , acc := range cfg .Wallet .All {
107+ an [acc .GetAddress ().String ()] = name
57108 }
58109
59110 return an
@@ -64,3 +115,82 @@ func FindAccountName(address string) string {
64115 an := GenAccountNames ()
65116 return an [address ]
66117}
118+
119+ // AddressFormatContext contains precomputed maps for address formatting.
120+ type AddressFormatContext struct {
121+ // Names maps native address string to account name.
122+ Names types.AccountNames
123+ // Eth maps native address string to Ethereum hex address string, if known.
124+ Eth map [string ]string
125+ }
126+
127+ // GenAccountEthMap generates a map of native address string -> eth hex address (if known).
128+ // Priority order matches GenAccountNames: test accounts < addressbook < wallet.
129+ func GenAccountEthMap () map [string ]string {
130+ eth := make (map [string ]string )
131+
132+ for _ , acc := range testing .TestAccounts {
133+ if acc .EthAddress != nil {
134+ eth [acc .Address .String ()] = acc .EthAddress .Hex ()
135+ }
136+ }
137+
138+ for _ , acc := range config .Global ().AddressBook .All {
139+ if ethAddr := acc .GetEthAddress (); ethAddr != nil {
140+ eth [acc .GetAddress ().String ()] = ethAddr .Hex ()
141+ }
142+ }
143+
144+ for _ , acc := range config .Global ().Wallet .All {
145+ if ethAddr := acc .GetEthAddress (); ethAddr != nil {
146+ eth [acc .GetAddress ().String ()] = ethAddr .Hex ()
147+ }
148+ }
149+
150+ return eth
151+ }
152+
153+ // GenAddressFormatContext builds both name and eth address maps for formatting.
154+ func GenAddressFormatContext () AddressFormatContext {
155+ return AddressFormatContext {
156+ Names : GenAccountNames (),
157+ Eth : GenAccountEthMap (),
158+ }
159+ }
160+
161+ // PrettyAddressWith formats a string address for display using a precomputed context.
162+ // Known addresses return "name (preferred_addr)", unknown addresses return the input unchanged.
163+ func PrettyAddressWith (ctx AddressFormatContext , addr string ) string {
164+ nativeAddr , ethFromInput , err := helpers .ResolveEthOrOasisAddress (addr )
165+ if err != nil || nativeAddr == nil {
166+ return addr
167+ }
168+
169+ nativeStr := nativeAddr .String ()
170+
171+ name := ctx .Names [nativeStr ]
172+ if name == "" {
173+ return addr
174+ }
175+
176+ // Prefer eth address in parentheses when available.
177+ var parenAddr string
178+ if ethFromInput != nil {
179+ parenAddr = ethFromInput .Hex ()
180+ } else if ethHex := ctx .Eth [nativeStr ]; ethHex != "" {
181+ parenAddr = ethHex
182+ } else {
183+ parenAddr = nativeStr
184+ }
185+
186+ if name == parenAddr {
187+ return parenAddr
188+ }
189+
190+ return fmt .Sprintf ("%s (%s)" , name , parenAddr )
191+ }
192+
193+ // PrettyAddress is like PrettyAddressWith but builds a fresh context on each call.
194+ func PrettyAddress (addr string ) string {
195+ return PrettyAddressWith (GenAddressFormatContext (), addr )
196+ }
0 commit comments