Transaction

1B98D7A2DB6EDC…D516CD1AAC5C

Block 747,735 · index 0 · indexed

Summary

Hash
1B98D7A2DB6EDC8A5C07E052CE3CE38014486D567CA11692F44DD516CD1AAC5C
Block
747,735
Size
27935 bytes
Gas used
41,057,070 / 82,114,080
Fee
82115 ugnot
Status
success

Messages

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

Arguments · 23

  1. #1abt
  2. #2abt.gno
  3. #3package abt import ( "chain" "chain/runtime/unsafe" "strconv" "gno.land/p/akkadia/beta/accesscontrol" "gno.land/p/nt/grc20/v0" "gno.land/r/akkadia/beta/admin" ) const FrozenChangedEvent = "FrozenChanged" var ( // Even a Token can create mutable tellers; keep all capabilities private. token *grc20.Token ledger *grc20.PrivateLedger frozen = true ) func init(cur realm) { token, ledger = grc20.NewToken("Akkadia Beta Token", "abt", 6, 0, cur) } // Mint remains available while frozen. Each successful call issues new tokens. func Mint(cur realm, to address, amount int64) { accesscontrol.MustGetAdminOrOperatorCaller(0, cur, admin.IsAdmin, admin.IsOperator) assertNoCoins(0, cur) if !to.IsValid() { panic("invalid abt recipient") } if amount <= 0 { panic("abt amount must be positive") } if err := ledger.Mint(to, amount); err != nil { panic("abt mint failed: " + err.Error()) } } // SetFrozen preserves existing allowances, which become usable after unfreezing. func SetFrozen(cur realm, value bool) { caller := accesscontrol.MustGetAdminCaller(0, cur, admin.IsAdmin) assertNoCoins(0, cur) if frozen == value { return } previous := frozen frozen = value chain.Emit(FrozenChangedEvent, "executor", caller.String(), "previous", strconv.FormatBool(previous), "frozen", strconv.FormatBool(frozen), ) } func Transfer(cur realm, to address, amount int64) { assertCanTransfer(0, cur) if err := ledger.CallerTeller().Transfer(0, cur, to, amount); err != nil { panic("abt transfer failed: " + err.Error()) } } // Approve also rejects allowance revocation while frozen. func Approve(cur realm, spender address, amount int64) { assertCanTransfer(0, cur) if err := ledger.CallerTeller().Approve(0, cur, spender, amount); err != nil { panic("abt approval failed: " + err.Error()) } } func TransferFrom(cur realm, from, to address, amount int64) { assertCanTransfer(0, cur) // The library spends allowance before rejecting self-transfers. Reject // here so even a caller that revives the abort cannot consume allowance. if from == to { panic("abt transfer failed: cannot send transfer to self") } if err := ledger.CallerTeller().TransferFrom(0, cur, from, to, amount); err != nil { panic("abt transfer failed: " + err.Error()) } } func IsFrozen() bool { return frozen } func GetName() string { return token.GetName() } func GetSymbol() string { return token.GetSymbol() } func GetDecimals() int { return token.GetDecimals() } func TotalSupply() int64 { return token.TotalSupply() } func BalanceOf(owner address) int64 { return token.BalanceOf(owner) } func Allowance(owner, spender address) int64 { return token.Allowance(owner, spender) } func assertCanTransfer(_ int, rlm realm) { accesscontrol.AssertCurrentRealm(0, rlm) assertNoCoins(0, rlm) if frozen { panic("abt is frozen") } } func assertNoCoins(_ int, rlm realm) { // OriginSend belongs to the origin call, not a subsequent realm relay. if rlm.Previous().IsUser() && len(unsafe.OriginSend()) != 0 { panic("attached coins not allowed") } }
  4. #4abt_test.gno
  5. #5package abt import ( "math" "testing" "gno.land/p/nt/uassert/v0" "gno.land/r/akkadia/beta/admin" ) func TestInitialState(t *testing.T) { uassert.Equal(t, "Akkadia Beta Token", GetName()) uassert.Equal(t, "abt", GetSymbol()) uassert.Equal(t, 6, GetDecimals()) uassert.Equal(t, "gno.land/r/akkadia/beta/abt.abt.0000000", token.ID()) uassert.True(t, IsFrozen()) uassert.Equal(t, int64(0), TotalSupply()) uassert.Equal(t, int64(0), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(0), Allowance(abtTest.Owner, abtTest.Other)) } func TestMintWhileFrozen(cur realm, t *testing.T) { setupABT(cross(cur), t) for _, caller := range []address{abtTest.Admin, abtTest.Operator} { testing.SetOriginCaller(caller) testing.SetRealm(testing.NewUserRealm(caller)) Mint(cross(cur), abtTest.Owner, 100) Mint(cross(cur), abtTest.Other, 10) } uassert.True(t, IsFrozen()) uassert.Equal(t, int64(220), TotalSupply()) uassert.Equal(t, int64(200), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(20), BalanceOf(abtTest.Other)) uassert.Equal(t, int64(0), Allowance(abtTest.Owner, abtTest.Other)) } func TestMintInvalidInput(cur realm, t *testing.T) { cases := []struct { name string to address amount int64 panicMsg string }{ {"empty address", address(""), 1, "invalid abt recipient"}, {"invalid address", address("invalid"), 1, "invalid abt recipient"}, {"zero", abtTest.Owner, 0, "abt amount must be positive"}, {"negative", abtTest.Owner, -1, "abt amount must be positive"}, {"minimum int64", abtTest.Owner, math.MinInt64, "abt amount must be positive"}, } for _, tc := range cases { t.Run(tc.name, func(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), abtTest.Owner, 7) uassert.AbortsWithMessage(t, cur, tc.panicMsg, func() { Mint(cross(cur), tc.to, tc.amount) }) uassert.Equal(t, int64(7), TotalSupply()) uassert.Equal(t, int64(7), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(0), BalanceOf(abtTest.Other)) }) } } func TestMintOverflow(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), abtTest.Owner, math.MaxInt64) for _, to := range []address{abtTest.Owner, abtTest.Other} { uassert.AbortsWithMessage(t, cur, "abt mint failed: mint overflow", func() { Mint(cross(cur), to, 1) }) } uassert.Equal(t, int64(math.MaxInt64), TotalSupply()) uassert.Equal(t, int64(math.MaxInt64), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(0), BalanceOf(abtTest.Other)) } func TestFreezeLifecycle(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), abtTest.Owner, 100) SetFrozen(cross(cur), true) SetFrozen(cross(cur), false) SetFrozen(cross(cur), false) Mint(cross(cur), abtTest.Owner, 5) testing.SetOriginCaller(abtTest.Owner) testing.SetRealm(testing.NewUserRealm(abtTest.Owner)) Transfer(cross(cur), abtTest.Other, 10) Approve(cross(cur), abtTest.Other, 40) testing.SetOriginCaller(abtTest.Other) testing.SetRealm(testing.NewUserRealm(abtTest.Other)) TransferFrom(cross(cur), abtTest.Owner, abtTest.Admin, 15) uassert.Equal(t, int64(80), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(10), BalanceOf(abtTest.Other)) uassert.Equal(t, int64(15), BalanceOf(abtTest.Admin)) uassert.Equal(t, int64(25), Allowance(abtTest.Owner, abtTest.Other)) testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) SetFrozen(cross(cur), true) testing.SetOriginCaller(abtTest.Owner) testing.SetRealm(testing.NewUserRealm(abtTest.Owner)) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { Approve(cross(cur), abtTest.Other, 0) }) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { Transfer(cross(cur), abtTest.Other, 1) }) testing.SetOriginCaller(abtTest.Other) testing.SetRealm(testing.NewUserRealm(abtTest.Other)) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { TransferFrom(cross(cur), abtTest.Owner, abtTest.Admin, 25) }) uassert.Equal(t, int64(25), Allowance(abtTest.Owner, abtTest.Other)) uassert.Equal(t, int64(80), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(105), TotalSupply()) testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) SetFrozen(cross(cur), false) testing.SetOriginCaller(abtTest.Other) testing.SetRealm(testing.NewUserRealm(abtTest.Other)) TransferFrom(cross(cur), abtTest.Owner, abtTest.Admin, 25) uassert.Equal(t, int64(0), Allowance(abtTest.Owner, abtTest.Other)) uassert.Equal(t, int64(55), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(40), BalanceOf(abtTest.Admin)) uassert.Equal(t, int64(105), TotalSupply()) testing.SetOriginCaller(abtTest.Owner) testing.SetRealm(testing.NewUserRealm(abtTest.Owner)) Approve(cross(cur), abtTest.Other, 5) Approve(cross(cur), abtTest.Other, 7) uassert.Equal(t, int64(7), Allowance(abtTest.Owner, abtTest.Other)) Approve(cross(cur), abtTest.Other, 0) uassert.Equal(t, int64(0), Allowance(abtTest.Owner, abtTest.Other)) } func TestMintRoleChanges(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) admin.UnsetOperator(cross(cur), abtTest.Operator) testing.SetOriginCaller(abtTest.Operator) testing.SetRealm(testing.NewUserRealm(abtTest.Operator)) uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() { Mint(cross(cur), abtTest.Owner, 1) }) testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) admin.TransferAdmin(cross(cur), abtTest.Other) testing.SetOriginCaller(abtTest.Other) testing.SetRealm(testing.NewUserRealm(abtTest.Other)) admin.AcceptAdmin(cross(cur)) Mint(cross(cur), abtTest.Owner, 3) SetFrozen(cross(cur), false) testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() { Mint(cross(cur), abtTest.Owner, 1) }) uassert.AbortsWithMessage(t, cur, "admin access required", func() { SetFrozen(cross(cur), true) }) uassert.Equal(t, int64(3), TotalSupply()) uassert.False(t, IsFrozen()) }
  6. #6events_filetest.gno
  7. #7// PKGPATH: gno.land/r/akkadia/beta/abt_events package abt_events import ( "testing" "gno.land/p/nt/testutils/v0" "gno.land/r/akkadia/beta/abt" "gno.land/r/akkadia/beta/admin" ) func main(cur realm) { caller := testutils.TestAddress("abtEventAdmin") testing.SetOriginCaller(admin.GetAdmin()) testing.SetRealm(testing.NewUserRealm(admin.GetAdmin())) admin.TransferAdmin(cross(cur), caller) testing.SetOriginCaller(caller) testing.SetRealm(testing.NewUserRealm(caller)) admin.AcceptAdmin(cross(cur)) abt.Mint(cross(cur), caller, 10) abt.SetFrozen(cross(cur), true) // No event for the initial value. abt.SetFrozen(cross(cur), false) abt.SetFrozen(cross(cur), false) // No duplicate event. abt.SetFrozen(cross(cur), true) println(abt.TotalSupply(), abt.IsFrozen()) } // Output: // 10 true // Events: // [ // { // "type": "TransferAdmin", // "attrs": [ // { // "key": "from", // "value": "" // }, // { // "key": "to", // "value": "g1v938g3tkv4h8gstyd45kuh6lta047h6lx2s576" // } // ], // "pkg_path": "gno.land/r/akkadia/beta/admin" // }, // { // "type": "AcceptAdmin", // "attrs": [ // { // "key": "from", // "value": "" // }, // { // "key": "to", // "value": "g1v938g3tkv4h8gstyd45kuh6lta047h6lx2s576" // } // ], // "pkg_path": "gno.land/r/akkadia/beta/admin" // }, // { // "type": "Transfer", // "attrs": [ // { // "key": "token", // "value": "gno.land/r/akkadia/beta/abt.abt.0000000" // }, // { // "key": "from", // "value": "" // }, // { // "key": "to", // "value": "g1v938g3tkv4h8gstyd45kuh6lta047h6lx2s576" // }, // { // "key": "value", // "value": "10" // } // ], // "pkg_path": "gno.land/p/nt/grc20/v0" // }, // { // "type": "FrozenChanged", // "attrs": [ // { // "key": "executor", // "value": "g1v938g3tkv4h8gstyd45kuh6lta047h6lx2s576" // }, // { // "key": "previous", // "value": "true" // }, // { // "key": "frozen", // "value": "false" // } // ], // "pkg_path": "gno.land/r/akkadia/beta/abt" // }, // { // "type": "FrozenChanged", // "attrs": [ // { // "key": "executor", // "value": "g1v938g3tkv4h8gstyd45kuh6lta047h6lx2s576" // }, // { // "key": "previous", // "value": "false" // }, // { // "key": "frozen", // "value": "true" // } // ], // "pkg_path": "gno.land/r/akkadia/beta/abt" // } // ]
  8. #8external_test.gno
  9. #9package abt_test import ( "chain" "chain/runtime" "testing" "gno.land/p/nt/testutils/v0" "gno.land/p/nt/uassert/v0" "gno.land/r/akkadia/beta/abt" "gno.land/r/akkadia/beta/admin" ) func TestExternalWrapperLifecycle(cur realm, t *testing.T) { caller := testutils.TestAddress("abtExternalAdmin") owner := testutils.TestAddress("abtExternalOwner") other := testutils.TestAddress("abtExternalOther") testing.SetOriginCaller(admin.GetAdmin()) testing.SetRealm(testing.NewUserRealm(admin.GetAdmin())) admin.TransferAdmin(cross(cur), caller) testing.SetOriginCaller(caller) testing.SetRealm(testing.NewUserRealm(caller)) admin.AcceptAdmin(cross(cur)) testing.SetOriginSend(chain.Coins{}) testing.SetRealm(runtime.NewRealm(caller, "gno.land/e/"+caller.String()+"/run")) abt.SetFrozen(cross(cur), true) supplyBefore := abt.TotalSupply() abt.Mint(cross(cur), owner, 20) uassert.Equal(t, "Akkadia Beta Token", abt.GetName()) uassert.Equal(t, "abt", abt.GetSymbol()) uassert.Equal(t, 6, abt.GetDecimals()) uassert.Equal(t, supplyBefore+20, abt.TotalSupply()) uassert.True(t, abt.IsFrozen()) abt.SetFrozen(cross(cur), false) testing.SetOriginCaller(owner) testing.SetRealm(testing.NewUserRealm(owner)) abt.Transfer(cross(cur), other, 2) abt.Approve(cross(cur), other, 5) testing.SetOriginCaller(other) testing.SetRealm(testing.NewUserRealm(other)) abt.TransferFrom(cross(cur), owner, other, 3) uassert.Equal(t, int64(15), abt.BalanceOf(owner)) uassert.Equal(t, int64(5), abt.BalanceOf(other)) uassert.Equal(t, int64(2), abt.Allowance(owner, other)) uassert.Equal(t, "15", abt.Render("balance/"+owner.String())) // A later failing transfer must preserve allowance even if its abort is caught. uassert.AbortsWithMessage(t, cur, "abt transfer failed: cannot send transfer to self", func() { abt.TransferFrom(cross(cur), owner, owner, 1) }) uassert.Equal(t, int64(2), abt.Allowance(owner, other)) testing.SetOriginCaller(caller) testing.SetRealm(testing.NewUserRealm(caller)) abt.SetFrozen(cross(cur), true) testing.SetOriginCaller(other) testing.SetRealm(testing.NewUserRealm(other)) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { abt.TransferFrom(cross(cur), owner, other, 2) }) uassert.Equal(t, int64(2), abt.Allowance(owner, other)) uassert.Equal(t, supplyBefore+20, abt.TotalSupply()) }
  10. #10gnomod.toml
  11. #11module = "gno.land/r/akkadia/beta/abt" gno = "0.9"
  12. #12render.gno
  13. #13package abt import ( "strconv" "strings" ) func Render(path string) string { if path == "" { return token.RenderHome() + "* **Frozen**: " + strconv.FormatBool(frozen) + "\n" } if strings.HasPrefix(path, "balance/") { owner := address(strings.TrimPrefix(path, "balance/")) if owner.IsValid() { return strconv.FormatInt(BalanceOf(owner), 10) } } return "404" }
  14. #14render_test.gno
  15. #15package abt import ( "strings" "testing" "gno.land/p/nt/uassert/v0" ) func TestRender(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), abtTest.Owner, 123) for _, text := range []string{"Akkadia Beta Token", "$abt", "**Decimals**: 6", "**Total supply**: 123", "**Frozen**: true"} { uassert.True(t, strings.Contains(Render(""), text)) } uassert.Equal(t, "123", Render("balance/"+abtTest.Owner.String())) uassert.Equal(t, "0", Render("balance/"+abtTest.Other.String())) for _, path := range []string{"unknown", "balance/", "balance/invalid", "balance/" + abtTest.Owner.String() + "/extra", "balance/<script>"} { uassert.Equal(t, "404", Render(path)) } uassert.Equal(t, int64(123), TotalSupply()) uassert.Equal(t, int64(123), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(0), Allowance(abtTest.Owner, abtTest.Other)) uassert.True(t, IsFrozen()) SetFrozen(cross(cur), false) uassert.True(t, strings.Contains(Render(""), "**Frozen**: false")) }
  16. #16security_test.gno
  17. #17package abt import ( "chain" "chain/runtime" "testing" "gno.land/p/nt/uassert/v0" "gno.land/r/akkadia/beta/admin" ) func TestUnauthorizedMintAndFreeze(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetOriginCaller(abtTest.Owner) testing.SetRealm(testing.NewUserRealm(abtTest.Owner)) uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() { Mint(cross(cur), abtTest.Owner, 1) }) for _, caller := range []address{abtTest.Owner, abtTest.Operator} { testing.SetOriginCaller(caller) testing.SetRealm(testing.NewUserRealm(caller)) for _, value := range []bool{true, false} { uassert.AbortsWithMessage(t, cur, "admin access required", func() { SetFrozen(cross(cur), value) }) } } for _, signer := range []address{abtTest.Admin, abtTest.Operator} { testing.SetOriginCaller(signer) testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/abt_relay")) uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() { Mint(cross(cur), abtTest.Owner, 1) }) uassert.AbortsWithMessage(t, cur, "admin access required", func() { SetFrozen(cross(cur), false) }) } uassert.True(t, IsFrozen()) uassert.Equal(t, int64(0), TotalSupply()) uassert.Equal(t, int64(0), BalanceOf(abtTest.Owner)) } func TestFrozenRejectsAllCallers(cur realm, t *testing.T) { setupABT(cross(cur), t) for _, caller := range []address{abtTest.Admin, abtTest.Operator, abtTest.Owner} { testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), caller, 10) testing.SetOriginCaller(caller) testing.SetRealm(testing.NewUserRealm(caller)) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { Transfer(cross(cur), abtTest.Other, 1) }) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { Approve(cross(cur), abtTest.Other, 1) }) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { Approve(cross(cur), abtTest.Other, 0) }) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { TransferFrom(cross(cur), caller, abtTest.Other, 1) }) uassert.Equal(t, int64(10), BalanceOf(caller)) uassert.Equal(t, int64(0), Allowance(caller, abtTest.Other)) } uassert.Equal(t, int64(30), TotalSupply()) uassert.Equal(t, int64(0), BalanceOf(abtTest.Other)) } func TestAttachedCoinsRejected(cur realm, t *testing.T) { cases := []struct { name string call func(_ int, rlm realm) }{ {"mint", func(_ int, rlm realm) { Mint(cross(rlm), abtTest.Owner, 1) }}, {"freeze", func(_ int, rlm realm) { SetFrozen(cross(rlm), true) }}, {"freeze no-op", func(_ int, rlm realm) { SetFrozen(cross(rlm), false) }}, {"transfer", func(_ int, rlm realm) { Transfer(cross(rlm), abtTest.Other, 1) }}, {"approve", func(_ int, rlm realm) { Approve(cross(rlm), abtTest.Other, 1) }}, {"transferFrom", func(_ int, rlm realm) { TransferFrom(cross(rlm), abtTest.Owner, abtTest.Other, 1) }}, } for _, tc := range cases { t.Run(tc.name, func(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), abtTest.Admin, 10) SetFrozen(cross(cur), false) for _, caller := range []runtime.Realm{ testing.NewUserRealm(abtTest.Admin), runtime.NewRealm(abtTest.Admin, "gno.land/e/"+abtTest.Admin.String()+"/run"), } { testing.SetRealm(caller) testing.SetOriginSend(chain.Coins{{"ugnot", 1}}) uassert.AbortsWithMessage(t, cur, "attached coins not allowed", func() { tc.call(0, cur) }) uassert.False(t, IsFrozen()) uassert.Equal(t, int64(10), TotalSupply()) uassert.Equal(t, int64(10), BalanceOf(abtTest.Admin)) uassert.Equal(t, int64(0), BalanceOf(abtTest.Other)) uassert.Equal(t, int64(0), Allowance(abtTest.Admin, abtTest.Other)) } }) } } func TestRelayUsesOwnBalanceAndAllowance(cur realm, t *testing.T) { setupABT(cross(cur), t) relay := testing.NewCodeRealm("gno.land/r/example/abt_relay") testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), abtTest.Owner, 100) Mint(cross(cur), relay.Address(), 20) SetFrozen(cross(cur), false) testing.SetOriginCaller(abtTest.Owner) testing.SetRealm(testing.NewUserRealm(abtTest.Owner)) Approve(cross(cur), relay.Address(), 7) Approve(cross(cur), abtTest.Owner, 30) // These coins belong to the relay's origin call, not to ABT. testing.SetOriginSend(chain.Coins{{"ugnot", 1}}) testing.SetRealm(relay) Transfer(cross(cur), abtTest.Other, 5) Approve(cross(cur), abtTest.Other, 3) TransferFrom(cross(cur), abtTest.Owner, abtTest.Other, 7) uassert.AbortsWithMessage(t, cur, "abt transfer failed: insufficient allowance", func() { TransferFrom(cross(cur), abtTest.Owner, abtTest.Other, 1) }) uassert.Equal(t, int64(93), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(15), BalanceOf(relay.Address())) uassert.Equal(t, int64(12), BalanceOf(abtTest.Other)) uassert.Equal(t, int64(3), Allowance(relay.Address(), abtTest.Other)) uassert.Equal(t, int64(0), Allowance(abtTest.Owner, abtTest.Other)) uassert.Equal(t, int64(0), Allowance(abtTest.Owner, relay.Address())) uassert.Equal(t, int64(30), Allowance(abtTest.Owner, abtTest.Owner)) uassert.Equal(t, int64(120), TotalSupply()) testing.SetOriginSend(chain.Coins{}) testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) SetFrozen(cross(cur), true) testing.SetRealm(relay) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { Transfer(cross(cur), abtTest.Other, 1) }) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { Approve(cross(cur), abtTest.Other, 0) }) uassert.AbortsWithMessage(t, cur, "abt is frozen", func() { TransferFrom(cross(cur), abtTest.Owner, abtTest.Other, 1) }) } func TestOperatorRealmCanMintWithOwnAuthority(cur realm, t *testing.T) { setupABT(cross(cur), t) relay := testing.NewCodeRealm("gno.land/r/example/abt_operator") testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) admin.SetOperator(cross(cur), relay.Address()) testing.SetOriginCaller(abtTest.Owner) testing.SetOriginSend(chain.Coins{{"ugnot", 1}}) testing.SetRealm(relay) Mint(cross(cur), abtTest.Owner, 2) uassert.AbortsWithMessage(t, cur, "admin access required", func() { SetFrozen(cross(cur), false) }) uassert.Equal(t, int64(2), TotalSupply()) uassert.True(t, IsFrozen()) } func TestSpoofedRealmRejected(cur realm, t *testing.T) { setupABT(cross(cur), t) stale := captureABTRealm(cross(cur)) uassert.PanicsWithMessage(t, cur, "spoofed realm", func() { assertCanTransfer(0, stale) }) uassert.True(t, IsFrozen()) uassert.Equal(t, int64(0), TotalSupply()) }
  18. #18setup_test.gno
  19. #19package abt import ( "chain" "testing" "gno.land/p/nt/grc20/v0" "gno.land/p/nt/testutils/v0" "gno.land/r/akkadia/beta/admin" ) var abtTest = struct { Admin, Operator, Owner, Other address }{ Admin: testutils.TestAddress("abtAdmin"), Operator: testutils.TestAddress("abtOperator"), Owner: testutils.TestAddress("abtOwner"), Other: testutils.TestAddress("abtOther"), } // Call with cross(cur) so token construction uses a live ABT realm. func setupABT(cur realm, t *testing.T) { t.Helper() token, ledger = grc20.NewToken("Akkadia Beta Token", "abt", 6, 0, cur) frozen = true testing.SetOriginSend(chain.Coins{}) originalAdmin := admin.GetAdmin() if originalAdmin != abtTest.Admin { testing.SetOriginCaller(originalAdmin) testing.SetRealm(testing.NewUserRealm(originalAdmin)) admin.TransferAdmin(cross(cur), abtTest.Admin) testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) admin.AcceptAdmin(cross(cur)) } testing.SetOriginCaller(abtTest.Admin) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) for _, operator := range admin.GetOperators() { admin.UnsetOperator(cross(cur), operator) } admin.SetOperator(cross(cur), abtTest.Operator) } func captureABTRealm(cur realm) realm { return cur }
  20. #20transfer_test.gno
  21. #21package abt import ( "testing" "gno.land/p/nt/uassert/v0" ) func TestTransferFailuresPreserveState(cur realm, t *testing.T) { cases := []struct { name string caller address call func(_ int, rlm realm) panicMsg string }{ {"transfer address", abtTest.Owner, func(_ int, rlm realm) { Transfer(cross(rlm), address("bad"), 1) }, "abt transfer failed: invalid address"}, {"transfer negative", abtTest.Owner, func(_ int, rlm realm) { Transfer(cross(rlm), abtTest.Other, -1) }, "abt transfer failed: invalid amount"}, {"transfer balance", abtTest.Owner, func(_ int, rlm realm) { Transfer(cross(rlm), abtTest.Other, 11) }, "abt transfer failed: insufficient balance"}, {"transfer self", abtTest.Owner, func(_ int, rlm realm) { Transfer(cross(rlm), abtTest.Owner, 1) }, "abt transfer failed: cannot send transfer to self"}, {"approve address", abtTest.Owner, func(_ int, rlm realm) { Approve(cross(rlm), address(""), 1) }, "abt approval failed: invalid address"}, {"approve negative", abtTest.Owner, func(_ int, rlm realm) { Approve(cross(rlm), abtTest.Other, -1) }, "abt approval failed: invalid amount"}, {"transferFrom owner", abtTest.Other, func(_ int, rlm realm) { TransferFrom(cross(rlm), address(""), abtTest.Admin, 1) }, "abt transfer failed: invalid address"}, {"transferFrom recipient", abtTest.Other, func(_ int, rlm realm) { TransferFrom(cross(rlm), abtTest.Owner, address("bad"), 1) }, "abt transfer failed: invalid address"}, {"transferFrom negative", abtTest.Other, func(_ int, rlm realm) { TransferFrom(cross(rlm), abtTest.Owner, abtTest.Admin, -1) }, "abt transfer failed: invalid amount"}, {"transferFrom allowance", abtTest.Other, func(_ int, rlm realm) { TransferFrom(cross(rlm), abtTest.Owner, abtTest.Admin, 6) }, "abt transfer failed: insufficient allowance"}, {"transferFrom balance", abtTest.Other, func(_ int, rlm realm) { TransferFrom(cross(rlm), abtTest.Owner, abtTest.Admin, 11) }, "abt transfer failed: insufficient balance"}, {"transferFrom self", abtTest.Other, func(_ int, rlm realm) { TransferFrom(cross(rlm), abtTest.Owner, abtTest.Owner, 1) }, "abt transfer failed: cannot send transfer to self"}, } for _, tc := range cases { t.Run(tc.name, func(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) Mint(cross(cur), abtTest.Owner, 10) SetFrozen(cross(cur), false) testing.SetOriginCaller(abtTest.Owner) testing.SetRealm(testing.NewUserRealm(abtTest.Owner)) Approve(cross(cur), abtTest.Other, 5) testing.SetOriginCaller(tc.caller) testing.SetRealm(testing.NewUserRealm(tc.caller)) uassert.AbortsWithMessage(t, cur, tc.panicMsg, func() { tc.call(0, cur) }) uassert.Equal(t, int64(10), TotalSupply()) uassert.Equal(t, int64(10), BalanceOf(abtTest.Owner)) uassert.Equal(t, int64(0), BalanceOf(abtTest.Other)) uassert.Equal(t, int64(0), BalanceOf(abtTest.Admin)) uassert.Equal(t, int64(5), Allowance(abtTest.Owner, abtTest.Other)) }) } } func TestZeroTransfers(cur realm, t *testing.T) { setupABT(cross(cur), t) testing.SetRealm(testing.NewUserRealm(abtTest.Admin)) SetFrozen(cross(cur), false) testing.SetOriginCaller(abtTest.Owner) testing.SetRealm(testing.NewUserRealm(abtTest.Owner)) Transfer(cross(cur), abtTest.Other, 0) TransferFrom(cross(cur), abtTest.Other, abtTest.Admin, 0) uassert.Equal(t, int64(0), TotalSupply()) uassert.Equal(t, int64(0), BalanceOf(abtTest.Other)) uassert.Equal(t, int64(0), Allowance(abtTest.Other, abtTest.Owner)) }
  22. #22/gno.MemPackageType
  23. #23 MPUserAll

Result log

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