Transaction

151A2C48C33521…4D83C301D23E

Block 747,732 · index 0 · indexed

Summary

Hash
151A2C48C3352146D6E17A568064A96EB225C1CF6551B5CEAB294D83C301D23E
Block
747,732
Size
51718 bytes
Gas used
71,362,927 / 142,725,754
Fee
142726 ugnot
Status
success

Messages

#1AddPackagegno.land/r/akkadia/beta/admin24 arguments

Arguments · 24

  1. #1admin
  2. #2admin.gno
  3. #3package admin import ( "chain" "chain/runtime/unsafe" "gno.land/p/akkadia/beta/accesscontrol" ) const ( TransferAdminEvent = "TransferAdmin" AcceptAdminEvent = "AcceptAdmin" CancelAdminEvent = "CancelAdmin" SetExplorerURLEvent = "SetExplorerURL" ) var ( admin address pendingAdmin address explorerURL string = "https://app.beta.akkadia.land" ) func init() { caller := unsafe.OriginCaller() admin = caller } func GetAdmin() address { return admin } func GetPendingAdmin() address { return pendingAdmin } func TransferAdmin(cur realm, newAdmin address) { assertTransferAdminCaller(0, cur) assertValidAddress(newAdmin) if pendingAdmin.IsValid() { panic("pending admin transfer already exists") } pendingAdmin = newAdmin chain.Emit( TransferAdminEvent, "from", admin.String(), "to", newAdmin.String(), ) } func assertTransferAdminCaller(_ int, rlm realm) { if !admin.IsValid() { // The admin realm normally initializes admin from OriginCaller during // package deployment, so a live on-chain admin should be a valid address. // An empty admin is a bootstrap-only state observed by cross-package tests: // dependent realm tests can import admin before a real deploy signer exists, // leaving admin as address(""). In that state there is no valid user address // that can satisfy direct caller authorization, so requiring AssertIsAdmin // would make those tests unable to promote a real test admin at all. // // Keep the legacy OriginCaller check only for this invalid-admin bootstrap // state. This preserves the narrow test/bootstrap path where the current // placeholder admin is address(""), while ensuring that once a real admin is // accepted every subsequent admin transfer is direct-caller-only. In other // words, code-realm relays cannot borrow a valid admin signer's authority to // change pendingAdmin; the exception exists solely to escape the empty // imported-test initialization state. accesscontrol.AssertCurrentRealm(0, rlm) accesscontrol.AssertIsAdminOrigin(IsAdmin) return } accesscontrol.AssertIsAdmin(0, rlm, IsAdmin) } func AcceptAdmin(cur realm) { if !pendingAdmin.IsValid() { panic("invalid pending admin address") } caller := mustGetPendingAdminUserCaller(0, cur) prevAdmin := admin admin = caller pendingAdmin = address("") chain.Emit( AcceptAdminEvent, "from", prevAdmin.String(), "to", admin.String(), ) } func CancelAdminTransfer(cur realm) { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) if !pendingAdmin.IsValid() { panic("invalid pending admin address") } canceledAdmin := pendingAdmin pendingAdmin = address("") chain.Emit( CancelAdminEvent, "admin", admin.String(), "pendingAdmin", canceledAdmin.String(), ) } func IsAdmin(address address) bool { return address == admin } // SetExplorerURL sets the explorer base URL (admin only) func SetExplorerURL(cur realm, url string) { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) normalizedURL := normalizeExplorerURL(url) oldURL := explorerURL explorerURL = normalizedURL chain.Emit( SetExplorerURLEvent, "admin", admin.String(), "oldURL", oldURL, "newURL", normalizedURL, ) } // GetExplorerURL returns the explorer base URL func GetExplorerURL() string { return explorerURL }
  4. #4admin_test.gno
  5. #5package admin import ( "chain/runtime" "strings" "testing" "gno.land/p/akkadia/beta/accesscontrol" "gno.land/p/nt/uassert/v0" ) // ==================== Admin Tests ==================== func TestTransferAdmin(cur realm, t *testing.T) { tests := []struct { name string originCaller address realm runtime.Realm newAdmin address shouldPanic bool panicMsg string }{ {"admin transfers", testAdmin, testing.NewUserRealm(testAdmin), testUser, false, ""}, {"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testUser, true, "admin access required"}, {"invalid address", testAdmin, testing.NewUserRealm(testAdmin), address(""), true, "invalid address"}, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { TransferAdmin(cross(cur), tt.newAdmin) }) } else { TransferAdmin(cross(cur), tt.newAdmin) uassert.Equal(t, testAdmin.String(), GetAdmin().String()) uassert.Equal(t, tt.newAdmin.String(), GetPendingAdmin().String()) uassert.True(t, IsAdmin(testAdmin)) uassert.False(t, IsAdmin(tt.newAdmin)) } } } func TestAcceptAdmin(cur realm, t *testing.T) { tests := []struct { name string setup func() originCaller address realm runtime.Realm shouldPanic bool panicMsg string }{ { name: "pending admin accepts", setup: func() { testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) }, originCaller: testUser, realm: testing.NewUserRealm(testUser), }, { name: "unauthorized user cannot accept", setup: func() { testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) }, originCaller: unauthorizedUser, realm: testing.NewUserRealm(unauthorizedUser), shouldPanic: true, panicMsg: "pending admin access required", }, { name: "missing pending admin", originCaller: testAdmin, realm: testing.NewUserRealm(testAdmin), shouldPanic: true, panicMsg: "invalid pending admin address", }, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) if tt.setup != nil { tt.setup() } testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { AcceptAdmin(cross(cur)) }) continue } AcceptAdmin(cross(cur)) uassert.Equal(t, tt.originCaller.String(), GetAdmin().String()) uassert.Equal(t, "", GetPendingAdmin().String()) } } func TestTransferAdmin_RejectsPendingOverwrite(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) uassert.Equal(t, testUser.String(), GetPendingAdmin().String()) uassert.AbortsWithMessage(t, cur, "pending admin transfer already exists", func() { TransferAdmin(cross(cur), testUser2) }) uassert.Equal(t, testAdmin.String(), GetAdmin().String()) uassert.Equal(t, testUser.String(), GetPendingAdmin().String()) } func TestTransferAdmin_AllowsOriginOnlyForEmptyBootstrapAdmin(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) admin = address("") testing.SetOriginCaller(address("")) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_bootstrap_relay")) TransferAdmin(cross(cur), testUser) uassert.Equal(t, "", GetAdmin().String()) uassert.Equal(t, testUser.String(), GetPendingAdmin().String()) testing.SetOriginCaller(testUser) testing.SetRealm(testing.NewUserRealm(testUser)) AcceptAdmin(cross(cur)) uassert.Equal(t, testUser.String(), GetAdmin().String()) uassert.Equal(t, "", GetPendingAdmin().String()) testing.SetOriginCaller(testUser) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay_after_bootstrap")) uassert.AbortsWithMessage(t, cur, "admin access required", func() { TransferAdmin(cross(cur), testUser2) }) } func TestAdminInterrealmSecurity(cur realm, t *testing.T) { tests := []struct { name string setup func(realm) realm call func(realm, realm) shouldPanic bool panicMsg string verify func(t *testing.T) }{ { name: "accept admin rejects code realm caller", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) testing.SetOriginCaller(testUser) testing.SetRealm(testing.NewCodeRealm("gno.land/r/admin/relay")) return nil }, call: func(cur realm, stale realm) { AcceptAdmin(cross(cur)) }, panicMsg: "contract calls not allowed: function must be called directly by user", }, { name: "transfer admin rejects valid admin origin through code realm", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { TransferAdmin(cross(cur), testUser) }, panicMsg: "admin access required", }, { name: "transfer admin rejects non-admin origin through code realm", setup: func(cur realm) realm { testing.SetOriginCaller(unauthorizedUser) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { TransferAdmin(cross(cur), testUser) }, panicMsg: "admin access required", }, { name: "cancel admin transfer", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { CancelAdminTransfer(cross(cur)) }, panicMsg: "admin access required", }, { name: "set explorer url", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { SetExplorerURL(cross(cur), "https://relay.akkadia.land") }, panicMsg: "admin access required", }, { name: "set operator", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { SetOperator(cross(cur), testUser) }, panicMsg: "admin access required", }, { name: "unset operator", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testUser) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { UnsetOperator(cross(cur), testUser) }, panicMsg: "admin access required", }, { name: "set fee collector", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { SetFeeCollector(cross(cur), testUser) }, panicMsg: "admin access required", }, { name: "set protocol", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { SetProtocol(cross(cur), testProtocol) }, panicMsg: "admin access required", }, { name: "set faucet target amount", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { SetFaucetTargetAmount(cross(cur), 1000000) }, panicMsg: "admin access required", }, { name: "request faucet rejects operator origin through code realm", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), 1000000) testing.SetOriginCaller(testOperator) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_operator_faucet_relay")) return nil }, call: func(cur realm, stale realm) { RequestFaucet(cross(cur)) }, panicMsg: "contract calls not allowed: function must be called directly by user", }, { name: "request faucet for", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { RequestFaucetFor(cross(cur), testOperator) }, panicMsg: "admin access required", }, { name: "request faucets", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { RequestFaucets(cross(cur)) }, panicMsg: "admin access required", }, { name: "withdraw", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_relay")) return nil }, call: func(cur realm, stale realm) { Withdraw(cross(cur), testUser, 1) }, panicMsg: "admin access required", }, { name: "assert current realm rejects stale realm", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_stale_probe")) return cur.Previous() }, call: func(cur realm, stale realm) { accesscontrol.AssertCurrentRealm(0, stale) }, shouldPanic: true, panicMsg: "spoofed realm", }, { name: "assert admin rejects stale realm", setup: func(cur realm) realm { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_stale_probe")) return cur.Previous() }, call: func(cur realm, stale realm) { accesscontrol.AssertIsAdmin(0, stale, IsAdmin) }, shouldPanic: true, panicMsg: "spoofed realm", }, { name: "pending admin caller rejects stale realm", setup: func(cur realm) realm { pendingAdmin = testUser testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewCodeRealm("gno.land/r/akkadia/beta/admin_stale_probe")) return cur.Previous() }, call: func(cur realm, stale realm) { mustGetPendingAdminUserCaller(0, stale) }, shouldPanic: true, panicMsg: "spoofed realm", }, } for _, tt := range tests { setupCommonAdminTests(cur, t) stale := tt.setup(cur) call := func() { tt.call(cur, stale) } if tt.shouldPanic { uassert.PanicsWithMessage(t, cur, tt.panicMsg, call) continue } if tt.panicMsg != "" { uassert.AbortsWithMessage(t, cur, tt.panicMsg, call) continue } call() if tt.verify != nil { tt.verify(t) } } } func TestCancelAdminTransfer(cur realm, t *testing.T) { tests := []struct { name string setup func() originCaller address realm runtime.Realm shouldPanic bool panicMsg string }{ { name: "admin cancels pending transfer", setup: func() { testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) }, originCaller: testAdmin, realm: testing.NewUserRealm(testAdmin), }, { name: "unauthorized user cannot cancel", setup: func() { testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) }, originCaller: unauthorizedUser, realm: testing.NewUserRealm(unauthorizedUser), shouldPanic: true, panicMsg: "admin access required", }, { name: "missing pending admin", originCaller: testAdmin, realm: testing.NewUserRealm(testAdmin), shouldPanic: true, panicMsg: "invalid pending admin address", }, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) if tt.setup != nil { tt.setup() } testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { CancelAdminTransfer(cross(cur)) }) continue } CancelAdminTransfer(cross(cur)) uassert.Equal(t, testAdmin.String(), GetAdmin().String()) uassert.Equal(t, "", GetPendingAdmin().String()) } } func TestCancelAdminTransfer_PreservesAdmin(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) TransferAdmin(cross(cur), testUser) CancelAdminTransfer(cross(cur)) uassert.Equal(t, testAdmin.String(), GetAdmin().String()) uassert.True(t, IsAdmin(testAdmin)) uassert.Equal(t, "", GetPendingAdmin().String()) } // ==================== ExplorerURL Tests ==================== func TestSetExplorerURL(cur realm, t *testing.T) { tests := []struct { name string originCaller address realm runtime.Realm url string shouldPanic bool panicMsg string expectedURL string }{ {"admin sets url", testAdmin, testing.NewUserRealm(testAdmin), "https://new.explorer.com", false, "", "https://new.explorer.com"}, {"trailing slash normalized", testAdmin, testing.NewUserRealm(testAdmin), "https://new.explorer.com///", false, "", "https://new.explorer.com"}, {"admin sets empty url", testAdmin, testing.NewUserRealm(testAdmin), "", true, "explorer URL is required", ""}, {"rejects http", testAdmin, testing.NewUserRealm(testAdmin), "http://new.explorer.com", true, "explorer URL must use https", ""}, {"rejects whitespace", testAdmin, testing.NewUserRealm(testAdmin), " https://new.explorer.com", true, "invalid explorer URL", ""}, {"rejects brackets", testAdmin, testing.NewUserRealm(testAdmin), "https://new.explorer.com/[test]", true, "invalid explorer URL", ""}, {"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), "https://hacker.com", true, "admin access required", ""}, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { SetExplorerURL(cross(cur), tt.url) }) uassert.Equal(t, "https://app.beta.akkadia.land", GetExplorerURL()) } else { SetExplorerURL(cross(cur), tt.url) uassert.Equal(t, tt.expectedURL, GetExplorerURL()) } } } // ==================== Operator Tests ==================== func TestSetOperator(cur realm, t *testing.T) { tests := []struct { name string originCaller address realm runtime.Realm newAddr address shouldPanic bool panicMsg string }{ {"admin sets operator", testAdmin, testing.NewUserRealm(testAdmin), testUser, false, ""}, {"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testUser, true, "admin access required"}, {"invalid address", testAdmin, testing.NewUserRealm(testAdmin), address(""), true, "invalid address"}, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { SetOperator(cross(cur), tt.newAddr) }) } else { SetOperator(cross(cur), tt.newAddr) uassert.True(t, IsOperator(tt.newAddr)) uassert.True(t, IsOperator(testAdmin)) } } } func TestGetOperators(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) operators := GetOperators() uassert.Equal(t, len(operators), 1) uassert.Equal(t, operators[0].String(), testAdmin.String()) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testUser) operators = GetOperators() uassert.Equal(t, len(operators), 2) operators[0] = unauthorizedUser operatorsAgain := GetOperators() uassert.Equal(t, len(operatorsAgain), 2) uassert.False(t, containsOperator(operatorsAgain, unauthorizedUser)) } func TestUnsetOperator(cur realm, t *testing.T) { tests := []struct { name string originCaller address realm runtime.Realm addr address shouldPanic bool panicMsg string }{ {"admin unsets operator", testAdmin, testing.NewUserRealm(testAdmin), testUser, false, ""}, {"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testUser, true, "admin access required"}, {"invalid address", testAdmin, testing.NewUserRealm(testAdmin), address(""), true, "invalid address"}, {"not an operator", testAdmin, testing.NewUserRealm(testAdmin), unauthorizedUser, true, "address is not an operator"}, } for _, tt := range tests { setupCommonAdminTests(cur, t) // Add testUser as operator for unset tests testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testUser) testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { UnsetOperator(cross(cur), tt.addr) }) } else { UnsetOperator(cross(cur), tt.addr) uassert.False(t, IsOperator(tt.addr)) uassert.True(t, IsOperator(testAdmin)) } } } // ==================== FeeCollector Tests ==================== func TestSetFeeCollector(cur realm, t *testing.T) { tests := []struct { name string originCaller address realm runtime.Realm newAddr address shouldPanic bool panicMsg string }{ {"admin sets feeCollector", testAdmin, testing.NewUserRealm(testAdmin), testUser, false, ""}, {"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testUser, true, "admin access required"}, {"invalid address", testAdmin, testing.NewUserRealm(testAdmin), address(""), true, "invalid address"}, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { SetFeeCollector(cross(cur), tt.newAddr) }) uassert.Equal(t, adminRealmAddr().String(), GetFeeCollector().String()) } else { SetFeeCollector(cross(cur), tt.newAddr) uassert.Equal(t, tt.newAddr.String(), GetFeeCollector().String()) uassert.True(t, IsFeeCollector(tt.newAddr)) uassert.False(t, IsFeeCollector(testAdmin)) } } } // ==================== Protocol Tests ==================== func TestSetProtocol(cur realm, t *testing.T) { tests := []struct { name string originCaller address realm runtime.Realm newAddr address shouldPanic bool panicMsg string }{ {"admin sets protocol", testAdmin, testing.NewUserRealm(testAdmin), testUser, false, ""}, {"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testUser, true, "admin access required"}, {"invalid address", testAdmin, testing.NewUserRealm(testAdmin), address(""), true, "invalid address"}, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { SetProtocol(cross(cur), tt.newAddr) }) uassert.Equal(t, testAdmin.String(), GetProtocol().String()) } else { SetProtocol(cross(cur), tt.newAddr) uassert.Equal(t, tt.newAddr.String(), GetProtocol().String()) uassert.True(t, IsProtocol(tt.newAddr)) uassert.False(t, IsProtocol(testAdmin)) } } } func TestRender(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testUser) SetFeeCollector(cross(cur), testUser2) SetProtocol(cross(cur), unauthorizedUser) SetFaucetTargetAmount(cross(cur), 1000000) output := Render("") uassert.True(t, strings.Contains(output, testAdmin.String()), "render should include admin") uassert.True(t, strings.Contains(output, testUser.String()), "render should include operator") uassert.True(t, strings.Contains(output, testUser2.String()), "render should include fee collector") uassert.True(t, strings.Contains(output, unauthorizedUser.String()), "render should include protocol") uassert.True(t, strings.Contains(output, "1000000 ugnot"), "render should include faucet target") }
  6. #6fee_collector.gno
  7. #7package admin import ( "chain" "gno.land/p/akkadia/beta/accesscontrol" ) const ( SetFeeCollectorEvent = "SetFeeCollector" ) var ( feeCollectorAddr address ) func init() { feeCollectorAddr = chain.PackageAddress("gno.land/r/akkadia/beta/admin") } func GetFeeCollector() address { return feeCollectorAddr } func SetFeeCollector(cur realm, addr address) { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertValidAddress(addr) oldAddr := feeCollectorAddr feeCollectorAddr = addr chain.Emit( SetFeeCollectorEvent, "oldAddr", oldAddr.String(), "newAddr", addr.String(), ) } func IsFeeCollector(address address) bool { return address == feeCollectorAddr }
  8. #8gnomod.toml
  9. #9module = "gno.land/r/akkadia/beta/admin" gno = "0.9"
  10. #10operator.gno
  11. #11package admin import ( "chain" "gno.land/p/akkadia/beta/accesscontrol" "gno.land/p/nt/bptree/v0" ) const ( SetOperatorEvent = "SetOperator" UnsetOperatorEvent = "UnsetOperator" ) var ( operatorAddrs *bptree.BPTree = bptree.NewBPTree32() ) func SetOperator(cur realm, addr address) { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertValidAddress(addr) operatorAddrs.Set(addr.String(), true) chain.Emit( SetOperatorEvent, "address", addr.String(), ) } func GetOperators() []address { result := []address{} operatorAddrs.Iterate("", "", func(addr string, _ any) bool { result = append(result, address(addr)) return false }) return result } func UnsetOperator(cur realm, addr address) { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertValidAddress(addr) if !operatorAddrs.Has(addr.String()) { panic("address is not an operator") } operatorAddrs.Remove(addr.String()) chain.Emit( UnsetOperatorEvent, "address", addr.String(), ) } func IsOperator(addr address) bool { return operatorAddrs.Has(addr.String()) }
  12. #12operator_faucet.gno
  13. #13package admin import ( "chain" "chain/banker" "gno.land/p/akkadia/beta/accesscontrol" ) const ( SetFaucetTargetAmountEvent = "SetFaucetTargetAmount" RequestFaucetEvent = "RequestFaucet" RequestFaucetsEvent = "RequestFaucets" WithdrawEvent = "Withdraw" ) var ( faucetTargetAmount int64 ) func SetFaucetTargetAmount(cur realm, amount int64) { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertPositiveFaucetTarget(amount) oldAmount := faucetTargetAmount faucetTargetAmount = amount chain.Emit( SetFaucetTargetAmountEvent, "oldAmount", int64ToString(oldAmount), "newAmount", int64ToString(amount), ) } func GetFaucetTargetAmount() int64 { return faucetTargetAmount } func RequestFaucet(cur realm) int64 { caller := mustGetFaucetCaller(0, cur) assertPositiveFaucetTarget(faucetTargetAmount) return sendFaucetDeficit(cur, caller, true) } func RequestFaucetFor(cur realm, operator address) int64 { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertPositiveFaucetTarget(faucetTargetAmount) assertFaucetOperator(operator) return sendFaucetDeficit(cur, operator, true) } func RequestFaucets(cur realm) int64 { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertPositiveFaucetTarget(faucetTargetAmount) bnk := banker.NewBanker(banker.BankerTypeRealmSend, cur) operators := GetOperators() deficitOperators := []address{} deficits := []int64{} totalAmount := int64(0) for _, operator := range operators { deficit := getFaucetDeficit(bnk, operator) if deficit <= 0 { continue } deficitOperators = append(deficitOperators, operator) deficits = append(deficits, deficit) totalAmount += deficit } if totalAmount == 0 { return 0 } assertAdminRealmBalance(bnk, cur.Address(), totalAmount) for i, operator := range deficitOperators { sendAdminRealmUgnot(bnk, cur.Address(), operator, deficits[i]) emitRequestFaucet(operator, deficits[i]) } chain.Emit( RequestFaucetsEvent, "amount", int64ToString(totalAmount), "targetAmount", int64ToString(faucetTargetAmount), ) return totalAmount } func Withdraw(cur realm, recipient address, amount int64) int64 { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertValidAddress(recipient) assertPositiveUgnotAmount(amount) bnk := banker.NewBanker(banker.BankerTypeRealmSend, cur) realmAddr := cur.Address() assertAdminRealmBalance(bnk, realmAddr, amount) sendAdminRealmUgnot(bnk, realmAddr, recipient, amount) chain.Emit( WithdrawEvent, "recipient", recipient.String(), "amount", int64ToString(amount), ) return amount } func sendFaucetDeficit(cur realm, operator address, panicIfFunded bool) int64 { bnk := banker.NewBanker(banker.BankerTypeRealmSend, cur) amount := getFaucetDeficit(bnk, operator) if amount <= 0 { if panicIfFunded { panic("operator balance is not below faucet target") } return 0 } realmAddr := cur.Address() assertAdminRealmBalance(bnk, realmAddr, amount) sendAdminRealmUgnot(bnk, realmAddr, operator, amount) emitRequestFaucet(operator, amount) return amount } func getFaucetDeficit(bnk banker.Banker, operator address) int64 { operatorBalance := bnk.GetCoins(operator).AmountOf("ugnot") if operatorBalance >= faucetTargetAmount { return 0 } return faucetTargetAmount - operatorBalance } func assertAdminRealmBalance(bnk banker.Banker, realmAddr address, amount int64) { realmBalance := bnk.GetCoins(realmAddr).AmountOf("ugnot") if realmBalance < amount { panic("admin realm balance is insufficient") } } func sendAdminRealmUgnot(bnk banker.Banker, realmAddr address, recipient address, amount int64) { coins := chain.Coins{chain.Coin{"ugnot", amount}} bnk.SendCoins(realmAddr, recipient, coins) } func emitRequestFaucet(operator address, amount int64) { chain.Emit( RequestFaucetEvent, "operator", operator.String(), "amount", int64ToString(amount), "targetAmount", int64ToString(faucetTargetAmount), ) } func mustGetFaucetCaller(_ int, rlm realm) address { caller := accesscontrol.MustGetUserCaller(0, rlm) assertFaucetOperator(caller) return caller } func assertFaucetOperator(operator address) { assertValidAddress(operator) if !IsOperator(operator) { panic("operator access required") } }
  14. #14operator_faucet_test.gno
  15. #15package admin import ( "testing" "chain" "chain/banker" "chain/runtime" "gno.land/p/nt/uassert/v0" ) const ( testFaucetFundAmount int64 = 1000000 ) type faucetTestHelper struct{} var faucetTest = &faucetTestHelper{} func TestSetFaucetTargetAmount(cur realm, t *testing.T) { tests := []struct { name string originCaller address realm runtime.Realm amount int64 shouldPanic bool panicMsg string }{ {"admin sets target amount", testAdmin, testing.NewUserRealm(testAdmin), 1000000, false, ""}, {"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), 1000000, true, "admin access required"}, {"zero amount", testAdmin, testing.NewUserRealm(testAdmin), 0, true, "faucet target amount must be positive"}, {"negative amount", testAdmin, testing.NewUserRealm(testAdmin), -1, true, "faucet target amount must be positive"}, } for _, tt := range tests { setupCommonAdminTests(cur, t) testing.SetOriginCaller(tt.originCaller) testing.SetRealm(tt.realm) if tt.shouldPanic { uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { SetFaucetTargetAmount(cross(cur), tt.amount) }) uassert.Equal(t, int64(0), GetFaucetTargetAmount()) continue } SetFaucetTargetAmount(cross(cur), tt.amount) uassert.Equal(t, tt.amount, GetFaucetTargetAmount()) } } func TestRequestFaucetTransfersDeficit(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) adminRealm := adminRealmAddr() adminBefore := adminUgnotBalance(adminRealm) operatorBefore := adminUgnotBalance(testOperator) expectedTransfer := int64(300000) faucetTest.FundAdmin(testFaucetFundAmount) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), operatorBefore+expectedTransfer) testing.SetOriginCaller(testOperator) testing.SetRealm(testing.NewUserRealm(testOperator)) transferred := RequestFaucet(cross(cur)) uassert.Equal(t, expectedTransfer, transferred) uassert.Equal(t, operatorBefore+expectedTransfer, adminUgnotBalance(testOperator)) uassert.Equal(t, adminBefore+testFaucetFundAmount-expectedTransfer, adminUgnotBalance(adminRealm)) } func TestRequestFaucetForTransfersDeficit(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) adminRealm := adminRealmAddr() adminBefore := adminUgnotBalance(adminRealm) operatorBefore := adminUgnotBalance(testOperator) expectedTransfer := int64(300000) faucetTest.FundAdmin(testFaucetFundAmount) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), operatorBefore+expectedTransfer) transferred := RequestFaucetFor(cross(cur), testOperator) uassert.Equal(t, expectedTransfer, transferred) uassert.Equal(t, operatorBefore+expectedTransfer, adminUgnotBalance(testOperator)) uassert.Equal(t, adminBefore+testFaucetFundAmount-expectedTransfer, adminUgnotBalance(adminRealm)) } func TestRequestFaucetsTransfersDeficits(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) UnsetOperator(cross(cur), testAdmin) SetOperator(cross(cur), testOperator) SetOperator(cross(cur), testUser) SetOperator(cross(cur), testUser2) adminRealm := adminRealmAddr() adminBefore := adminUgnotBalance(adminRealm) operatorBefore := adminUgnotBalance(testOperator) userBefore := adminUgnotBalance(testUser) user2Before := adminUgnotBalance(testUser2) targetAmount := maxInt64(operatorBefore, maxInt64(userBefore, user2Before)) + 300000 if user2Before < targetAmount { faucetTest.Issue(testUser2, targetAmount-user2Before) user2Before = targetAmount } operatorDeficit := targetAmount - operatorBefore userDeficit := targetAmount - userBefore expectedTransfer := operatorDeficit + userDeficit faucetTest.FundAdmin(expectedTransfer) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetFaucetTargetAmount(cross(cur), targetAmount) transferred := RequestFaucets(cross(cur)) uassert.Equal(t, expectedTransfer, transferred) uassert.Equal(t, targetAmount, adminUgnotBalance(testOperator)) uassert.Equal(t, targetAmount, adminUgnotBalance(testUser)) uassert.Equal(t, user2Before, adminUgnotBalance(testUser2)) uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealm)) } func TestRequestFaucetsReturnsZeroWithoutDeficits(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) UnsetOperator(cross(cur), testAdmin) SetOperator(cross(cur), testOperator) SetOperator(cross(cur), testUser) operatorBefore := adminUgnotBalance(testOperator) userBefore := adminUgnotBalance(testUser) targetAmount := maxInt64(operatorBefore, userBefore) if operatorBefore < targetAmount { faucetTest.Issue(testOperator, targetAmount-operatorBefore) } if userBefore < targetAmount { faucetTest.Issue(testUser, targetAmount-userBefore) } if targetAmount <= 0 { targetAmount = 1 faucetTest.Issue(testOperator, targetAmount) faucetTest.Issue(testUser, targetAmount) } adminBefore := adminUgnotBalance(adminRealmAddr()) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetFaucetTargetAmount(cross(cur), targetAmount) transferred := RequestFaucets(cross(cur)) uassert.Equal(t, int64(0), transferred) uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealmAddr())) } func TestRequestFaucetRejectsInvalidRequest(cur realm, t *testing.T) { tests := []struct { name string setup func(cur realm) (address, runtime.Realm) panicMsg string }{ { name: "non-operator direct caller", setup: func(cur realm) (address, runtime.Realm) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetFaucetTargetAmount(cross(cur), 1000000) faucetTest.FundAdmin(testFaucetFundAmount) return unauthorizedUser, testing.NewUserRealm(unauthorizedUser) }, panicMsg: "operator access required", }, { name: "operator balance already reaches target", setup: func(cur realm) (address, runtime.Realm) { operatorBalance := adminUgnotBalance(testOperator) if operatorBalance == 0 { faucetTest.Issue(testOperator, 1) operatorBalance = 1 } testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), operatorBalance) faucetTest.FundAdmin(testFaucetFundAmount) return testOperator, testing.NewUserRealm(testOperator) }, panicMsg: "operator balance is not below faucet target", }, { name: "admin realm balance is insufficient", setup: func(cur realm) (address, runtime.Realm) { adminBalance := adminUgnotBalance(adminRealmAddr()) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1) return testOperator, testing.NewUserRealm(testOperator) }, panicMsg: "admin realm balance is insufficient", }, { name: "operator origin through code realm", setup: func(cur realm) (address, runtime.Realm) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+1) faucetTest.FundAdmin(testFaucetFundAmount) return testOperator, testing.NewCodeRealm("gno.land/r/akkadia/beta/operator_faucet_relay") }, panicMsg: "contract calls not allowed: function must be called directly by user", }, { name: "target amount is not configured", setup: func(cur realm) (address, runtime.Realm) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) faucetTest.FundAdmin(testFaucetFundAmount) return testOperator, testing.NewUserRealm(testOperator) }, panicMsg: "faucet target amount must be positive", }, } for _, tt := range tests { setupCommonAdminTests(cur, t) caller, rlm := tt.setup(cur) testing.SetOriginCaller(caller) testing.SetRealm(rlm) uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { RequestFaucet(cross(cur)) }) } } func TestRequestFaucetForRejectsInvalidRequest(cur realm, t *testing.T) { tests := []struct { name string setup func(cur realm) (address, runtime.Realm, address) panicMsg string }{ { name: "non-admin caller", setup: func(cur realm) (address, runtime.Realm, address) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), 1000000) faucetTest.FundAdmin(testFaucetFundAmount) return unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testOperator }, panicMsg: "admin access required", }, { name: "non-operator target", setup: func(cur realm) (address, runtime.Realm, address) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetFaucetTargetAmount(cross(cur), 1000000) faucetTest.FundAdmin(testFaucetFundAmount) return testAdmin, testing.NewUserRealm(testAdmin), unauthorizedUser }, panicMsg: "operator access required", }, { name: "operator balance already reaches target", setup: func(cur realm) (address, runtime.Realm, address) { operatorBalance := adminUgnotBalance(testOperator) if operatorBalance == 0 { faucetTest.Issue(testOperator, 1) operatorBalance = 1 } testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), operatorBalance) faucetTest.FundAdmin(testFaucetFundAmount) return testAdmin, testing.NewUserRealm(testAdmin), testOperator }, panicMsg: "operator balance is not below faucet target", }, { name: "admin realm balance is insufficient", setup: func(cur realm) (address, runtime.Realm, address) { adminBalance := adminUgnotBalance(adminRealmAddr()) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1) return testAdmin, testing.NewUserRealm(testAdmin), testOperator }, panicMsg: "admin realm balance is insufficient", }, { name: "target amount is not configured", setup: func(cur realm) (address, runtime.Realm, address) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) faucetTest.FundAdmin(testFaucetFundAmount) return testAdmin, testing.NewUserRealm(testAdmin), testOperator }, panicMsg: "faucet target amount must be positive", }, } for _, tt := range tests { setupCommonAdminTests(cur, t) caller, rlm, operator := tt.setup(cur) testing.SetOriginCaller(caller) testing.SetRealm(rlm) uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { RequestFaucetFor(cross(cur), operator) }) } } func TestRequestFaucetsRejectsInvalidRequest(cur realm, t *testing.T) { tests := []struct { name string setup func(cur realm) (address, runtime.Realm) panicMsg string }{ { name: "non-admin caller", setup: func(cur realm) (address, runtime.Realm) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+1) faucetTest.FundAdmin(testFaucetFundAmount) return unauthorizedUser, testing.NewUserRealm(unauthorizedUser) }, panicMsg: "admin access required", }, { name: "admin realm balance is insufficient", setup: func(cur realm) (address, runtime.Realm) { adminBalance := adminUgnotBalance(adminRealmAddr()) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1) return testAdmin, testing.NewUserRealm(testAdmin) }, panicMsg: "admin realm balance is insufficient", }, { name: "target amount is not configured", setup: func(cur realm) (address, runtime.Realm) { testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) SetOperator(cross(cur), testOperator) faucetTest.FundAdmin(testFaucetFundAmount) return testAdmin, testing.NewUserRealm(testAdmin) }, panicMsg: "faucet target amount must be positive", }, } for _, tt := range tests { setupCommonAdminTests(cur, t) caller, rlm := tt.setup(cur) testing.SetOriginCaller(caller) testing.SetRealm(rlm) uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { RequestFaucets(cross(cur)) }) } } func TestWithdrawTransfersAdminRealmBalance(cur realm, t *testing.T) { setupCommonAdminTests(cur, t) adminRealm := adminRealmAddr() adminBefore := adminUgnotBalance(adminRealm) recipientBefore := adminUgnotBalance(testUser) withdrawAmount := int64(200000) faucetTest.FundAdmin(withdrawAmount) testing.SetOriginCaller(testAdmin) testing.SetRealm(testing.NewUserRealm(testAdmin)) transferred := Withdraw(cross(cur), testUser, withdrawAmount) uassert.Equal(t, withdrawAmount, transferred) uassert.Equal(t, recipientBefore+withdrawAmount, adminUgnotBalance(testUser)) uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealm)) } type withdrawRejectContext struct { caller address recipient address amount int64 adminBefore int64 recipientBefore int64 checkRecipient bool } func TestWithdrawRejectsInvalidRequest(cur realm, t *testing.T) { tests := []struct { name string setup func() withdrawRejectContext panicMsg string }{ { name: "non-admin caller", setup: func() withdrawRejectContext { faucetTest.FundAdmin(1) return withdrawRejectContext{ caller: unauthorizedUser, recipient: testUser, amount: 1, } }, panicMsg: "admin access required", }, { name: "invalid recipient", setup: func() withdrawRejectContext { faucetTest.FundAdmin(1) return withdrawRejectContext{ caller: testAdmin, recipient: address(""), amount: 1, } }, panicMsg: "invalid address", }, { name: "zero amount", setup: func() withdrawRejectContext { faucetTest.FundAdmin(1) return withdrawRejectContext{ caller: testAdmin, recipient: testUser, amount: 0, } }, panicMsg: "amount must be greater than 0", }, { name: "negative amount", setup: func() withdrawRejectContext { faucetTest.FundAdmin(1) return withdrawRejectContext{ caller: testAdmin, recipient: testUser, amount: -1, } }, panicMsg: "amount must be greater than 0", }, { name: "admin realm balance is insufficient", setup: func() withdrawRejectContext { return withdrawRejectContext{ caller: testAdmin, recipient: testUser, amount: adminUgnotBalance(adminRealmAddr()) + 1, } }, panicMsg: "admin realm balance is insufficient", }, } for _, tt := range tests { setupCommonAdminTests(cur, t) ctx := tt.setup() ctx.adminBefore = adminUgnotBalance(adminRealmAddr()) ctx.checkRecipient = ctx.recipient.IsValid() if ctx.checkRecipient { ctx.recipientBefore = adminUgnotBalance(ctx.recipient) } testing.SetOriginCaller(ctx.caller) testing.SetRealm(testing.NewUserRealm(ctx.caller)) uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() { Withdraw(cross(cur), ctx.recipient, ctx.amount) }) uassert.Equal(t, ctx.adminBefore, adminUgnotBalance(adminRealmAddr())) if ctx.checkRecipient { uassert.Equal(t, ctx.recipientBefore, adminUgnotBalance(ctx.recipient)) } } } func adminUgnotBalance(addr address) int64 { bnk := banker.NewReadonlyBanker() return bnk.GetCoins(addr).AmountOf("ugnot") } func (h *faucetTestHelper) FundAdmin(amount int64) { h.Issue(adminRealmAddr(), amount) } func (h *faucetTestHelper) Issue(addr address, amount int64) { if amount <= 0 { return } testing.IssueCoins(addr, chain.Coins{{"ugnot", amount}}) } func maxInt64(a int64, b int64) int64 { if a > b { return a } return b }
  16. #16protocol.gno
  17. #17package admin import ( "chain" "chain/runtime/unsafe" "gno.land/p/akkadia/beta/accesscontrol" ) const ( SetProtocolEvent = "SetProtocol" ) var ( protocolAddr address ) func init() { caller := unsafe.OriginCaller() protocolAddr = caller } func GetProtocol() address { return protocolAddr } func SetProtocol(cur realm, addr address) { accesscontrol.AssertIsAdmin(0, cur, IsAdmin) assertValidAddress(addr) oldAddr := protocolAddr protocolAddr = addr chain.Emit( SetProtocolEvent, "oldAddr", oldAddr.String(), "newAddr", addr.String(), ) } func IsProtocol(address address) bool { return address == protocolAddr }
  18. #18render.gno
  19. #19package admin func Render(path string) string { return renderHome() } func renderHome() string { operators := GetOperators() operatorList := "" for _, addr := range operators { operatorList += "* " + addr.String() + "\n" } if operatorList == "" { operatorList = "(none)\n" } output := `# AKKADIA on Gno ## Introduction AKKADIA is a blockchain-based game like Minecraft. this contract is managing the admin of the AKKADIA game. ## Admin * **Address**: ` + admin.String() + ` ## Operators ` + operatorList + ` ## Fee Collector * **Address**: ` + feeCollectorAddr.String() + ` ## Protocol * **Address**: ` + protocolAddr.String() + ` ## Operator Faucet * **Target Amount**: ` + int64ToString(faucetTargetAmount) + ` ugnot ` return output }
  20. #20setup_test.gno
  21. #21package admin import ( "testing" "chain" "gno.land/p/nt/bptree/v0" "gno.land/p/nt/testutils/v0" ) var ( testAdmin = testutils.TestAddress("testAdmin") testOperator = testutils.TestAddress("testOperator") testUser = testutils.TestAddress("testUser") testUser2 = testutils.TestAddress("testUser2") unauthorizedUser = testutils.TestAddress("unauthorizedUser") testProtocol = testutils.TestAddress("testProtocol") ) // setupCommonAdminTests initializes common admin test environment. func setupCommonAdminTests(cur realm, t *testing.T) { t.Helper() admin = testAdmin pendingAdmin = address("") operatorAddrs = bptree.NewBPTree32() operatorAddrs.Set(testAdmin.String(), true) feeCollectorAddr = adminRealmAddr() protocolAddr = testAdmin faucetTargetAmount = 0 explorerURL = "https://app.beta.akkadia.land" } func adminRealmAddr() address { return chain.PackageAddress("gno.land/r/akkadia/beta/admin") } func containsOperator(operators []address, addr address) bool { for _, operator := range operators { if operator == addr { return true } } return false }
  22. #22utils.gno
  23. #23package admin import ( "strconv" "gno.land/p/akkadia/beta/accesscontrol" ) func int64ToString(value int64) string { return strconv.FormatInt(value, 10) } func mustGetPendingAdminUserCaller(_ int, rlm realm) address { caller := accesscontrol.MustGetUserCaller(0, rlm) if caller != pendingAdmin { panic("pending admin access required") } return caller }
  24. #24validation.gno

Result log

msg:0,success:true,log:,events:[]