diff --git a/.gnoversion b/.gnoversion index ddef93042d..5aa07b31e4 100644 --- a/.gnoversion +++ b/.gnoversion @@ -1 +1 @@ -9786fa366f922f04e1251ec6f1df6423b4fd2bf4 +d69b5529e938f89b05b449fcd0a0bdcf7bcdc09e diff --git a/gno/r/launchpad_grc20/airdrop_grc20.gno b/gno/r/launchpad_grc20/airdrop_grc20.gno index be3ea4ca8d..ea7cace9b9 100644 --- a/gno/r/launchpad_grc20/airdrop_grc20.gno +++ b/gno/r/launchpad_grc20/airdrop_grc20.gno @@ -128,8 +128,8 @@ func (a *Airdrop) isOnGoing() bool { func (a *Airdrop) ToJSON() *json.Node { return json.ObjectNode("", map[string]*json.Node{ "id": json.StringNode("", ufmt.Sprintf("%d", uint64(a.id))), - "tokenName": json.StringNode("", a.token.banker.GetName()), - "tokenSymbol": json.StringNode("", a.token.banker.GetSymbol()), + "tokenName": json.StringNode("", a.token.GetName()), + "tokenSymbol": json.StringNode("", a.token.GetSymbol()), "amountPerAddr": json.StringNode("", strconv.FormatUint(a.amountPerAddr, 10)), "startTimestamp": json.StringNode("", strconv.FormatInt(a.startTimestamp, 10)), "endTimestamp": json.StringNode("", strconv.FormatInt(a.endTimestamp, 10)), diff --git a/gno/r/launchpad_grc20/render.gno b/gno/r/launchpad_grc20/render.gno index 3a6dddc969..19cc0cdce3 100644 --- a/gno/r/launchpad_grc20/render.gno +++ b/gno/r/launchpad_grc20/render.gno @@ -52,11 +52,11 @@ func renderTokenPage(res *mux.ResponseWriter, req *mux.Request) { res.Write("## Last tokens created\n") for _, token := range lastTokensCreated { - res.Write(ufmt.Sprintf("### Name: %s - Symbol: %s\n", token.banker.GetName(), token.banker.GetSymbol())) - res.Write(ufmt.Sprintf("#### Total Supply: %d %s\n", token.banker.TotalSupply(), token.banker.GetSymbol())) - res.Write(ufmt.Sprintf("#### Decimals: %d\n", token.banker.GetDecimals())) + res.Write(ufmt.Sprintf("### Name: %s - Symbol: %s\n", token.GetName(), token.GetSymbol())) + res.Write(ufmt.Sprintf("#### Total Supply: %d %s\n", token.TotalSupply(), token.GetSymbol())) + res.Write(ufmt.Sprintf("#### Decimals: %d\n", token.GetDecimals())) res.Write(ufmt.Sprintf("#### Admin: %s\n\n", token.admin.Owner().String())) - res.Write(ufmt.Sprintf("> Link: [:token/%s](launchpad_grc20:token/%s)\n\n", token.banker.GetName(), token.banker.GetName())) + res.Write(ufmt.Sprintf("> Link: [:token/%s](launchpad_grc20:token/%s)\n\n", token.GetName(), token.GetName())) } } renderFooter(res, "") @@ -68,11 +68,11 @@ func renderTokenDetailPage(res *mux.ResponseWriter, req *mux.Request) { res.Write("# 🪙 Token Details 🪙\n") - res.Write(ufmt.Sprintf("### Name: %s - Symbol: %s\n", token.banker.GetName(), token.banker.GetSymbol())) - res.Write(ufmt.Sprintf("#### Total Supply: %d %s\n", token.banker.TotalSupply(), token.banker.GetSymbol())) - res.Write(ufmt.Sprintf("#### Decimals: %d\n", token.banker.GetDecimals())) + res.Write(ufmt.Sprintf("### Name: %s - Symbol: %s\n", token.GetName(), token.GetSymbol())) + res.Write(ufmt.Sprintf("#### Total Supply: %d %s\n", token.TotalSupply(), token.GetSymbol())) + res.Write(ufmt.Sprintf("#### Decimals: %d\n", token.GetDecimals())) res.Write(ufmt.Sprintf("#### Admin: %s\n\n", token.admin.Owner().String())) - res.Write(ufmt.Sprintf("#### Total Supply Cap (0 = unlimited): %d %s\n\n", token.totalSupplyCap, token.banker.GetSymbol())) + res.Write(ufmt.Sprintf("#### Total Supply Cap (0 = unlimited): %d %s\n\n", token.totalSupplyCap, token.GetSymbol())) if token.allowMint { res.Write("#### Mintable: true\n\n") @@ -107,7 +107,7 @@ func renderTokenDetailPage(res *mux.ResponseWriter, req *mux.Request) { sale := mustGetSale(uint64(id)) res.Write(ufmt.Sprintf("### Sale #%d\n", uint64(id))) res.Write(ufmt.Sprintf("#### Price per token: %d $GNOT\n", sale.pricePerToken)) - res.Write(ufmt.Sprintf("#### Limit per address: %d $%s\n", sale.limitPerAddr, token.banker.GetSymbol())) + res.Write(ufmt.Sprintf("#### Limit per address: %d $%s\n", sale.limitPerAddr, token.GetSymbol())) res.Write(ufmt.Sprintf("#### Min goal: %d $GNOT\n", sale.minGoal)) res.Write(ufmt.Sprintf("#### Max goal: %d $GNOT\n", sale.maxGoal)) res.Write(ufmt.Sprintf("#### Already sold: %d $GNOT\n\n", sale.alreadySold)) @@ -120,10 +120,10 @@ func renderTokenBalancePage(res *mux.ResponseWriter, req *mux.Request) { tokenName := req.GetVar("name") address := req.GetVar("address") token := mustGetToken(tokenName) - balance := token.banker.BalanceOf(std.Address(address)) + balance := token.BalanceOf(std.Address(address)) res.Write("# 🪙 Token Balance 🪙\n") - res.Write(ufmt.Sprintf("### 📍 Address: %s\n ### 🏦 Balance: %d %s\n", address, balance, token.banker.GetSymbol())) + res.Write(ufmt.Sprintf("### 📍 Address: %s\n ### 🏦 Balance: %d %s\n", address, balance, token.GetSymbol())) renderFooter(res, "../../../") } @@ -144,7 +144,7 @@ func renderAirdropPage(res *mux.ResponseWriter, req *mux.Request) { } airdrop := mustGetAirdrop(uint64(i)) res.Write(ufmt.Sprintf("### Airdrop #%d\n", i)) - res.Write(ufmt.Sprintf("#### Token: %s\n", airdrop.token.banker.GetName())) + res.Write(ufmt.Sprintf("#### Token: %s\n", airdrop.token.GetName())) if airdrop.isOnGoing() { res.Write("#### Status: Ongoing\n") } else { @@ -175,7 +175,7 @@ func renderAirdropDetailPage(res *mux.ResponseWriter, req *mux.Request) { res.Write(ufmt.Sprintf("# 🎁 Airdrop #%d Details 🎁\n", airdropID)) - res.Write(ufmt.Sprintf("### Token: %s\n", airdrop.token.banker.GetName())) + res.Write(ufmt.Sprintf("### Token: %s\n", airdrop.token.GetName())) if airdrop.isOnGoing() { res.Write("### Status: Ongoing\n") } else { @@ -234,7 +234,7 @@ func renderSalePage(res *mux.ResponseWriter, req *mux.Request) { } sale := mustGetSale(uint64(i)) res.Write(ufmt.Sprintf("### Sale #%d\n", i)) - res.Write(ufmt.Sprintf("#### Token: %s\n", sale.token.banker.GetName())) + res.Write(ufmt.Sprintf("#### Token: %s\n", sale.token.GetName())) if sale.isOnGoing() { res.Write("#### Status: Ongoing\n") } else { @@ -252,7 +252,7 @@ func renderSalePage(res *mux.ResponseWriter, req *mux.Request) { res.Write("#### Sale is public\n") } res.Write(ufmt.Sprintf("#### Price per token: %d $GNOT\n", sale.pricePerToken)) - res.Write(ufmt.Sprintf("#### Limit per address: %d $%s\n", sale.limitPerAddr, sale.token.banker.GetSymbol())) + res.Write(ufmt.Sprintf("#### Limit per address: %d $%s\n", sale.limitPerAddr, sale.token.GetSymbol())) res.Write(ufmt.Sprintf("#### Min goal: %d $GNOT\n", sale.minGoal)) res.Write(ufmt.Sprintf("#### Max goal: %d $GNOT\n", sale.maxGoal)) res.Write(ufmt.Sprintf("#### Already sold: %d $GNOT\n\n", sale.alreadySold)) @@ -272,7 +272,7 @@ func renderSaleDetailPage(res *mux.ResponseWriter, req *mux.Request) { res.Write(ufmt.Sprintf("# 🛒 Sale #%d Details 🛒\n", saleID)) - res.Write(ufmt.Sprintf("### Token: %s\n", sale.token.banker.GetName())) + res.Write(ufmt.Sprintf("### Token: %s\n", sale.token.GetName())) if sale.isOnGoing() { res.Write("### Status: Ongoing\n") } else { @@ -290,7 +290,7 @@ func renderSaleDetailPage(res *mux.ResponseWriter, req *mux.Request) { res.Write("### Sale is public\n") } res.Write(ufmt.Sprintf("### Price per token: %d $GNOT\n", sale.pricePerToken)) - res.Write(ufmt.Sprintf("### Limit per address: %d $%s\n", sale.limitPerAddr, sale.token.banker.GetSymbol())) + res.Write(ufmt.Sprintf("### Limit per address: %d $%s\n", sale.limitPerAddr, sale.token.GetSymbol())) res.Write(ufmt.Sprintf("### Min goal: %d $GNOT\n", sale.minGoal)) res.Write(ufmt.Sprintf("### Max goal: %d $GNOT\n", sale.maxGoal)) res.Write(ufmt.Sprintf("### Already sold: %d $GNOT\n\n", sale.alreadySold)) @@ -312,7 +312,7 @@ func renderSaleBalancePage(res *mux.ResponseWriter, req *mux.Request) { res.Write("# 🛒 Sale Balance 🛒\n") res.Write(ufmt.Sprintf("### 🛒 Sale ID: %d\n", saleID)) - res.Write(ufmt.Sprintf("### 📍 Address: %s\n ### 🏦 Balance (Tokens from this sale only): %d %s\n", address, balance, sale.token.banker.GetSymbol())) + res.Write(ufmt.Sprintf("### 📍 Address: %s\n ### 🏦 Balance (Tokens from this sale only): %d %s\n", address, balance, sale.token.GetSymbol())) res.Write("> ⚠️ *The tokens will be transfered or refunded after the sale ends depending if the sale reached the min goal or not* ⚠️\n") renderFooter(res, "../../../") diff --git a/gno/r/launchpad_grc20/sale_grc20.gno b/gno/r/launchpad_grc20/sale_grc20.gno index e8bead8124..0ba34c154a 100644 --- a/gno/r/launchpad_grc20/sale_grc20.gno +++ b/gno/r/launchpad_grc20/sale_grc20.gno @@ -270,7 +270,7 @@ func (s *Sale) payAllBuyers() { func (s *Sale) ToJSON() *json.Node { return json.ObjectNode("", map[string]*json.Node{ "id": json.StringNode("", ufmt.Sprintf("%d", uint64(s.id))), - "tokenName": json.StringNode("", s.token.banker.GetName()), + "tokenName": json.StringNode("", s.token.GetName()), "pricePerToken": json.StringNode("", strconv.FormatUint(s.pricePerToken, 10)), "limitPerAddr": json.StringNode("", strconv.FormatUint(s.limitPerAddr, 10)), "minGoal": json.StringNode("", strconv.FormatUint(s.minGoal, 10)), diff --git a/gno/r/launchpad_grc20/token_factory_grc20.gno b/gno/r/launchpad_grc20/token_factory_grc20.gno index 5f6e279cf2..3233b94b4e 100644 --- a/gno/r/launchpad_grc20/token_factory_grc20.gno +++ b/gno/r/launchpad_grc20/token_factory_grc20.gno @@ -14,7 +14,8 @@ import ( const LENGTH_LAST_TOKENS_CACHE = 10 type Token struct { - banker *grc20.Banker + banker *grc20.PrivateLedger + token *grc20.Token admin *ownable.Ownable image string totalSupplyCap uint64 @@ -24,7 +25,7 @@ type Token struct { SalesIDs []seqid.ID } -var _ grc20.Token = (*Token)(nil) +var _ grc20.Teller = (*Token)(nil) var ( tokens *avl.Tree // name -> token @@ -54,7 +55,7 @@ func NewToken(name, symbol, image string, decimals uint, initialSupply, totalSup panic("decimals must be 18 or less") } - banker := grc20.NewBanker(name, symbol, decimals) + token, banker := grc20.NewToken(name, symbol, decimals) fee := initialSupply * 25 / 1000 netSupply := initialSupply - fee @@ -66,6 +67,7 @@ func NewToken(name, symbol, image string, decimals uint, initialSupply, totalSup } inst := Token{ + token: token, banker: banker, admin: ownable.NewWithAddress(admin), image: image, @@ -141,33 +143,32 @@ func TransferFrom(name string, from, to std.Address, amount uint64) { checkErr(token.TransferFrom(from, to, amount)) } -func (token Token) Token() grc20.Token { return token.banker.Token() } -func (token Token) GetName() string { return token.banker.GetName() } -func (token Token) GetSymbol() string { return token.banker.GetSymbol() } -func (token Token) GetDecimals() uint { return token.banker.GetDecimals() } -func (token Token) TotalSupply() uint64 { return token.Token().TotalSupply() } -func (token Token) BalanceOf(owner std.Address) uint64 { return token.Token().BalanceOf(owner) } +func (token Token) GetName() string { return token.token.GetName() } +func (token Token) GetSymbol() string { return token.token.GetSymbol() } +func (token Token) GetDecimals() uint { return token.token.GetDecimals() } +func (token Token) TotalSupply() uint64 { return token.token.TotalSupply() } +func (token Token) BalanceOf(owner std.Address) uint64 { return token.token.BalanceOf(owner) } func (token Token) Transfer(to std.Address, amount uint64) error { - return token.Token().Transfer(to, amount) + return token.token.CallerTeller().Transfer(to, amount) } func (token Token) Allowance(owner, spender std.Address) uint64 { - return token.Token().Allowance(owner, spender) + return token.token.Allowance(owner, spender) } func (token Token) Approve(spender std.Address, amount uint64) error { - return token.Token().Approve(spender, amount) + return token.token.CallerTeller().Approve(spender, amount) } func (token Token) TransferFrom(from, to std.Address, amount uint64) error { - return token.Token().TransferFrom(from, to, amount) + return token.token.CallerTeller().TransferFrom(from, to, amount) } func (token Token) ToJSON() *json.Node { return json.ObjectNode("", map[string]*json.Node{ - "name": json.StringNode("", token.banker.GetName()), - "symbol": json.StringNode("", token.banker.GetSymbol()), - "decimals": json.StringNode("", strconv.FormatUint(uint64(token.banker.GetDecimals()), 10)), + "name": json.StringNode("", token.GetName()), + "symbol": json.StringNode("", token.GetSymbol()), + "decimals": json.StringNode("", strconv.FormatUint(uint64(token.GetDecimals()), 10)), "admin": json.StringNode("", token.admin.Owner().String()), "image": json.StringNode("", token.image), "totalSupply": json.StringNode("", strconv.FormatInt(int64(token.TotalSupply()), 10)), diff --git a/gno/r/tori/gno.mod b/gno/r/tori/gno.mod index cc72eab861..213ef54615 100644 --- a/gno/r/tori/gno.mod +++ b/gno/r/tori/gno.mod @@ -1,11 +1 @@ module gno.land/r/teritori/tori - -require ( - gno.land/p/demo/grc/grc20 v0.0.0-latest - gno.land/p/demo/json v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest - gno.land/p/demo/users v0.0.0-latest - gno.land/p/teritori/dao_interfaces v0.0.0-latest - gno.land/p/teritori/jsonutil v0.0.0-latest - gno.land/r/demo/users v0.0.0-latest -) diff --git a/gno/r/tori/messages.gno b/gno/r/tori/messages.gno index a583af5517..30adafc73a 100644 --- a/gno/r/tori/messages.gno +++ b/gno/r/tori/messages.gno @@ -1,6 +1,7 @@ package tori import ( + "std" "strconv" "strings" @@ -10,17 +11,19 @@ import ( "gno.land/p/teritori/jsonutil" ) +// TODO: move this file in a generic package to administrate grc20s via daos + type ExecutableMessageMintTori struct { dao_interfaces.ExecutableMessage - Recipient users.AddressOrName + Recipient std.Address Amount uint64 } var _ dao_interfaces.ExecutableMessage = &ExecutableMessageMintTori{} func (msg ExecutableMessageMintTori) Type() string { - return "gno.land/r/teritori/tori.Mint" + return "gno.land/r/teritori/tori.MintTori" } func (msg *ExecutableMessageMintTori) String() string { @@ -37,13 +40,13 @@ func (msg *ExecutableMessageMintTori) String() string { func (msg *ExecutableMessageMintTori) FromJSON(ast *json.Node) { obj := ast.MustObject() - msg.Recipient = jsonutil.MustAddressOrName(obj["recipient"]) + msg.Recipient = jsonutil.MustAddress(obj["recipient"]) msg.Amount = jsonutil.MustUint64(obj["amount"]) } func (msg *ExecutableMessageMintTori) ToJSON() *json.Node { return json.ObjectNode("", map[string]*json.Node{ - "recipient": jsonutil.AddressOrNameNode(msg.Recipient), + "recipient": jsonutil.AddressNode(msg.Recipient), "amount": jsonutil.Uint64Node(msg.Amount), }) } @@ -60,7 +63,7 @@ func NewMintToriHandler() *MintToriHandler { func (h *MintToriHandler) Execute(imsg dao_interfaces.ExecutableMessage) { msg := imsg.(*ExecutableMessageMintTori) - Mint(msg.Recipient, msg.Amount) + Mint(users.AddressOrName(msg.Recipient), msg.Amount) } func (h MintToriHandler) Type() string { @@ -81,7 +84,7 @@ type ExecutableMessageBurnTori struct { var _ dao_interfaces.ExecutableMessage = &ExecutableMessageBurnTori{} func (msg ExecutableMessageBurnTori) Type() string { - return "gno.land/r/teritori/tori.Burn" + return "gno.land/r/teritori/tori.BurnTori" } func (msg *ExecutableMessageBurnTori) String() string { @@ -135,7 +138,7 @@ func (h *BurnToriHandler) Instantiate() dao_interfaces.ExecutableMessage { type ExecutableMessageChangeAdmin struct { dao_interfaces.ExecutableMessage - NewAdmin users.AddressOrName + NewAdmin std.Address } var _ dao_interfaces.ExecutableMessage = &ExecutableMessageChangeAdmin{} @@ -154,12 +157,12 @@ func (msg *ExecutableMessageChangeAdmin) String() string { func (msg *ExecutableMessageChangeAdmin) FromJSON(ast *json.Node) { obj := ast.MustObject() - msg.NewAdmin = jsonutil.MustAddressOrName(obj["newAdmin"]) + msg.NewAdmin = jsonutil.MustAddress(obj["newAdmin"]) } func (msg *ExecutableMessageChangeAdmin) ToJSON() *json.Node { return json.ObjectNode("", map[string]*json.Node{ - "newAdmin": jsonutil.AddressOrNameNode(msg.NewAdmin), + "newAdmin": jsonutil.AddressNode(msg.NewAdmin), }) } @@ -175,7 +178,7 @@ func NewChangeAdminHandler() *ChangeAdminHandler { func (h *ChangeAdminHandler) Execute(imsg dao_interfaces.ExecutableMessage) { msg := imsg.(*ExecutableMessageChangeAdmin) - ChangeAdmin(msg.NewAdmin) + owner.TransferOwnership(msg.NewAdmin) } func (h ChangeAdminHandler) Type() string { diff --git a/gno/r/tori/tori.gno b/gno/r/tori/tori.gno index 1572f5e09a..bffc8b4846 100644 --- a/gno/r/tori/tori.gno +++ b/gno/r/tori/tori.gno @@ -1,3 +1,4 @@ +// tori is a copy of foo20 that can be administred by a dao package tori import ( @@ -5,99 +6,87 @@ import ( "strings" "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ownable" "gno.land/p/demo/ufmt" - "gno.land/p/demo/users" - rusers "gno.land/r/demo/users" + pusers "gno.land/p/demo/users" + "gno.land/r/demo/grc20reg" + "gno.land/r/demo/users" ) var ( - tori *grc20.Banker - userTori grc20.Token - admin std.Address = std.DerivePkgAddr("gno.land/r/teritori/dao_realm") + Token, privateLedger = grc20.NewToken("Tori", "TORI", 4) + UserTeller = Token.CallerTeller() + owner = ownable.NewWithAddress(std.DerivePkgAddr("gno.land/r/teritori/dao_realm")) ) func init() { - tori = grc20.NewBanker("Tori", "TORI", 6) - userTori = tori.Token() + privateLedger.Mint(owner.Owner(), 1_000_000*10_000) + getter := func() *grc20.Token { return Token } + grc20reg.Register(getter, "") } -// method proxies as public functions. -// - -// getters. - func TotalSupply() uint64 { - return tori.TotalSupply() + return UserTeller.TotalSupply() } -func BalanceOf(owner users.AddressOrName) uint64 { - return tori.BalanceOf(rusers.Resolve(owner)) +func BalanceOf(owner pusers.AddressOrName) uint64 { + ownerAddr := users.Resolve(owner) + return UserTeller.BalanceOf(ownerAddr) } -func Allowance(owner, spender users.AddressOrName) uint64 { - return tori.Allowance(rusers.Resolve(owner), rusers.Resolve(spender)) +func Allowance(owner, spender pusers.AddressOrName) uint64 { + ownerAddr := users.Resolve(owner) + spenderAddr := users.Resolve(spender) + return UserTeller.Allowance(ownerAddr, spenderAddr) } -// setters. - -func Transfer(to users.AddressOrName, amount uint64) { - caller := std.PrevRealm().Addr() - tori.Transfer(caller, rusers.Resolve(to), amount) +func Transfer(to pusers.AddressOrName, amount uint64) { + toAddr := users.Resolve(to) + checkErr(UserTeller.Transfer(toAddr, amount)) } -func Approve(spender users.AddressOrName, amount uint64) { - caller := std.PrevRealm().Addr() - tori.Approve(caller, rusers.Resolve(spender), amount) +func Approve(spender pusers.AddressOrName, amount uint64) { + spenderAddr := users.Resolve(spender) + checkErr(UserTeller.Approve(spenderAddr, amount)) } -func TransferFrom(from, to users.AddressOrName, amount uint64) { - caller := std.PrevRealm().Addr() - tori.TransferFrom(caller, rusers.Resolve(from), rusers.Resolve(to), amount) +func TransferFrom(from, to pusers.AddressOrName, amount uint64) { + fromAddr := users.Resolve(from) + toAddr := users.Resolve(to) + checkErr(UserTeller.TransferFrom(fromAddr, toAddr, amount)) } -// administration. - -func ChangeAdmin(newAdmin users.AddressOrName) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) - admin = rusers.Resolve(newAdmin) +func Mint(to pusers.AddressOrName, amount uint64) { + owner.AssertCallerIsOwner() + toAddr := users.Resolve(to) + checkErr(privateLedger.Mint(toAddr, amount)) } -func Mint(address users.AddressOrName, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) - tori.Mint(rusers.Resolve(address), amount) +func Burn(from pusers.AddressOrName, amount uint64) { + owner.AssertCallerIsOwner() + fromAddr := users.Resolve(from) + checkErr(privateLedger.Burn(fromAddr, amount)) } -func Burn(address users.AddressOrName, amount uint64) { - caller := std.PrevRealm().Addr() - assertIsAdmin(caller) - tori.Burn(rusers.Resolve(address), amount) -} - -// render. -// - func Render(path string) string { parts := strings.Split(path, "/") c := len(parts) switch { case path == "": - return tori.RenderHome() - + return Token.RenderHome() case c == 2 && parts[0] == "balance": - owner := users.AddressOrName(parts[1]) - balance := tori.BalanceOf(rusers.Resolve(owner)) + owner := pusers.AddressOrName(parts[1]) + ownerAddr := users.Resolve(owner) + balance := UserTeller.BalanceOf(ownerAddr) return ufmt.Sprintf("%d\n", balance) - default: return "404\n" } } -func assertIsAdmin(address std.Address) { - if address != admin { - panic("restricted access") +func checkErr(err error) { + if err != nil { + panic(err) } }