diff --git a/docs/resources/effective-gno.md b/docs/resources/effective-gno.md index 203a26939a8..aa6f0e99600 100644 --- a/docs/resources/effective-gno.md +++ b/docs/resources/effective-gno.md @@ -141,8 +141,8 @@ In Gno, `init()` primarily serves two purposes: ```go import "gno.land/r/some/registry" -func init() { - registry.Register(cross, "myID", myCallback) +func init(cur realm) { + registry.Register(cross(cur), "myID", myCallback) } func myCallback(a, b string) { /* ... */ } @@ -809,7 +809,7 @@ have been consumed by the intermediary. Two attacker shapes bypass the check: 1. **Intermediate code realm.** User calls `r/attacker/wrapper.DoIt()` with `-send 1000000ugnot`. The wrapper keeps the coins (via its own banker) and - then calls `BuyThing(cross, ...)` on your realm. Your realm sees + then calls `BuyThing(cross(cur), ...)` on your realm. Your realm sees `OriginSend() = 1000000ugnot`, the `IsUser()` check passes because... actually it doesn't — `IsUser()` rejects pure code realms. Which leads to: @@ -818,7 +818,7 @@ have been consumed by the intermediary. Two attacker shapes bypass the check: That script runs in an ephemeral code realm at path `gno.land/e/{attacker}/run`. Inside main, the script consumes the origin-send envelope (via its own `BankerTypeOriginSend`) or simply does whatever it - wants with the coins, then calls `BuyThing(cross, ...)`. Your realm sees + wants with the coins, then calls `BuyThing(cross(cur), ...)`. Your realm sees `OriginSend() = 1000000ugnot` in the envelope and `IsUser() = true` because **`IsUser()` accepts both `IsUserCall()` (pure EOA) AND `IsUserRun()` (user-run ephemeral realm)**. The check passes but no coins reached your realm. @@ -897,7 +897,7 @@ var ( ) func init(cur realm) { - Token, privateLedger = grc20.NewToken(0, cur, "Foo Token", "FOO", 4) + Token, privateLedger = grc20.NewToken("Foo Token", "FOO", 4, "token", cur) UserTeller = Token.CallerTeller() } diff --git a/docs/users/interact-with-gnokey.md b/docs/users/interact-with-gnokey.md index 9027aa84153..dcbc3767f89 100644 --- a/docs/users/interact-with-gnokey.md +++ b/docs/users/interact-with-gnokey.md @@ -419,7 +419,7 @@ Now, we should have the following folder structure: ``` In the `script.gno` file, first define the package to be `main`. Then we can import -the Counter realm and define a `main()` function with no return values that will +the Counter realm and define a `main(cur realm)` function with no return values that will be automatically detected and run. In it, we can call the `Increment()` function. ```go @@ -427,8 +427,8 @@ package main import "gno.land/r/demo/counter" -func main() { - println(counter.Increment(cross)) +func main(cur realm) { + println(counter.Increment(cross(cur))) } ``` @@ -1261,7 +1261,7 @@ var ( ) func init(cur realm) { - Token, adm = grc20.NewToken(0, cur, "wrapped GNOT", "wugnot", 0) + Token, adm = grc20.NewToken("wrapped GNOT", "wugnot", 0, "token", cur) } const ( diff --git a/examples/gno.land/p/demo/tokens/grc20/filetests/token_identity_filetest.gno b/examples/gno.land/p/demo/tokens/grc20/filetests/token_identity_filetest.gno new file mode 100644 index 00000000000..a76e6303d71 --- /dev/null +++ b/examples/gno.land/p/demo/tokens/grc20/filetests/token_identity_filetest.gno @@ -0,0 +1,127 @@ +// PKGPATH: gno.land/r/demo/grc20identity + +package grc20identity + +import ( + "chain" + + "gno.land/p/demo/tokens/grc20" + "gno.land/p/nt/seqid/v0" +) + +var nextTokenID seqid.ID + +func main(cur realm) { + first, firstLedger := newToken(cur) + second, secondLedger := newToken(cur) + + if first.ID() != "gno.land/r/demo/grc20identity.DUP.0000001" { + panic("unexpected first token ID: " + first.ID()) + } + if second.ID() != "gno.land/r/demo/grc20identity.DUP.0000002" { + panic("unexpected second token ID: " + second.ID()) + } + + holder := chain.PackageAddress("holder") + spender := chain.PackageAddress("spender") + firstLedger.Mint(holder, 1) + firstLedger.Approve(holder, spender, 1) + secondLedger.Mint(holder, 2) + secondLedger.Approve(holder, spender, 2) +} + +func newToken(cur realm) (*grc20.Token, *grc20.PrivateLedger) { + return grc20.NewToken("Same", "DUP", 6, nextTokenID.Next(), cur) +} + +// Events: +// [ +// { +// "type": "Transfer", +// "attrs": [ +// { +// "key": "token", +// "value": "gno.land/r/demo/grc20identity.DUP.0000001" +// }, +// { +// "key": "from", +// "value": "" +// }, +// { +// "key": "to", +// "value": "g18rwjsdf0kcy673ta0q4ujwvsncz4k2a8gcv94t" +// }, +// { +// "key": "value", +// "value": "1" +// } +// ], +// "pkg_path": "gno.land/p/demo/tokens/grc20" +// }, +// { +// "type": "Approval", +// "attrs": [ +// { +// "key": "token", +// "value": "gno.land/r/demo/grc20identity.DUP.0000001" +// }, +// { +// "key": "owner", +// "value": "g18rwjsdf0kcy673ta0q4ujwvsncz4k2a8gcv94t" +// }, +// { +// "key": "spender", +// "value": "g148tp8xkvvk3l73lmxywpdlsxgdujjlclx7wfyg" +// }, +// { +// "key": "value", +// "value": "1" +// } +// ], +// "pkg_path": "gno.land/p/demo/tokens/grc20" +// }, +// { +// "type": "Transfer", +// "attrs": [ +// { +// "key": "token", +// "value": "gno.land/r/demo/grc20identity.DUP.0000002" +// }, +// { +// "key": "from", +// "value": "" +// }, +// { +// "key": "to", +// "value": "g18rwjsdf0kcy673ta0q4ujwvsncz4k2a8gcv94t" +// }, +// { +// "key": "value", +// "value": "2" +// } +// ], +// "pkg_path": "gno.land/p/demo/tokens/grc20" +// }, +// { +// "type": "Approval", +// "attrs": [ +// { +// "key": "token", +// "value": "gno.land/r/demo/grc20identity.DUP.0000002" +// }, +// { +// "key": "owner", +// "value": "g18rwjsdf0kcy673ta0q4ujwvsncz4k2a8gcv94t" +// }, +// { +// "key": "spender", +// "value": "g148tp8xkvvk3l73lmxywpdlsxgdujjlclx7wfyg" +// }, +// { +// "key": "value", +// "value": "2" +// } +// ], +// "pkg_path": "gno.land/p/demo/tokens/grc20" +// } +// ] diff --git a/examples/gno.land/p/demo/tokens/grc20/tellers_test.gno b/examples/gno.land/p/demo/tokens/grc20/tellers_test.gno index 9518ac59a9f..4cb2467fc62 100644 --- a/examples/gno.land/p/demo/tokens/grc20/tellers_test.gno +++ b/examples/gno.land/p/demo/tokens/grc20/tellers_test.gno @@ -10,7 +10,7 @@ import ( ) func TestCallerTellerImpl(cur realm, t *testing.T) { - tok, _ := newTestToken(0, cur, "Dummy", "DUMMY", 4) + tok, _ := newTestToken("Dummy", "DUMMY", 4, 0, cur) teller := tok.CallerTeller() urequire.False(t, tok == nil) var _ Teller = teller @@ -24,7 +24,7 @@ type evilWrap struct { } func TestIsCanonicalTeller(cur realm, t *testing.T) { - tok, _ := newTestToken(0, cur, "Dummy", "DUMMY", 4) + tok, _ := newTestToken("Dummy", "DUMMY", 4, 0, cur) legit := tok.CallerTeller() uassert.True(t, IsCanonicalTeller(legit), @@ -42,7 +42,7 @@ func TestTeller(cur realm, t *testing.T) { carl = testutils.TestAddress("carl") ) - token, ledger := newTestToken(0, cur, "Dummy", "DUMMY", 6) + token, ledger := newTestToken("Dummy", "DUMMY", 6, 0, cur) checkBalances := func(aliceEB, bobEB, carlEB int64) { t.Helper() @@ -104,7 +104,7 @@ func TestCallerTeller(cur realm, t *testing.T) { bob := testutils.TestAddress("bob") carl := testutils.TestAddress("carl") - token, ledger := newTestToken(0, cur, "Dummy", "DUMMY", 6) + token, ledger := newTestToken("Dummy", "DUMMY", 6, 0, cur) teller := token.CallerTeller() checkBalances := func(aliceEB, bobEB, carlEB int64) { diff --git a/examples/gno.land/p/demo/tokens/grc20/token.gno b/examples/gno.land/p/demo/tokens/grc20/token.gno index ee271c4f89b..e83cd9f5352 100644 --- a/examples/gno.land/p/demo/tokens/grc20/token.gno +++ b/examples/gno.land/p/demo/tokens/grc20/token.gno @@ -6,25 +6,31 @@ import ( "math/overflow" "strconv" + "gno.land/p/nt/seqid/v0" "gno.land/p/nt/ufmt/v0" ) // NewToken creates a Token whose origRealm is bound to the calling realm. // rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()), // and rlm.PkgPath() — the calling realm itself — becomes the Token's -// origRealm. Token.ID() returns origRealm + "." + symbol. +// origRealm. Token.ID() returns origRealm + "." + symbol + "." + id. // // Because IsCurrent runtime-validates that rlm came from the live // crossing frame, origRealm is unforgeable: an external realm cannot // fabricate a Token claiming to belong to a different package. // -// Typical call from a realm's init(cur realm) or other crossing function: +// Realms that create multiple tokens should allocate id from one persistent +// seqid.ID, shared by every creation path, to avoid conflicting identifiers: // -// Token, ledger := grc20.NewToken(0, cur, "Foo", "FOO", 4) +// var nextTokenID seqid.ID +// Token, ledger := grc20.NewToken("Foo", "FOO", 4, nextTokenID.Next(), cur) // -// If the Token should be discoverable, follow up with grc20reg.Register. -// The registry key is Token.ID(). -func NewToken(_ int, rlm realm, name, symbol string, decimals int) (*Token, *PrivateLedger) { +// A realm that creates only a single token can pass 0 directly, since no +// other token of that realm can collide with it. +// +// If the Token should be discoverable, follow up with +// grc20reg.Register(cross(cur), Token, slug). The registry key is Token.ID(). +func NewToken(name, symbol string, decimals int, id seqid.ID, rlm realm) (*Token, *PrivateLedger) { if !rlm.IsCurrent() { panic(ErrSpoofedRealm) } @@ -43,11 +49,11 @@ func NewToken(_ int, rlm realm, name, symbol string, decimals int) (*Token, *Pri } ledger := &PrivateLedger{} token := &Token{ - name: name, - symbol: symbol, - decimals: decimals, - origRealm: pkgPath, - ledger: ledger, + id: pkgPath + "." + symbol + "." + id.String(), + name: name, + symbol: symbol, + decimals: decimals, + ledger: ledger, } ledger.token = token return token, ledger @@ -69,15 +75,13 @@ func validName(name string) bool { return true } -// validSymbol reports whether symbol is a valid token symbol: non-empty, -// within MaxSymbolLen, and consists only of [A-Za-z0-9_-]. Matches -// grc20reg.validateSlug so the symbol round-trips cleanly through -// Token.ID() and any registry binding. -func validSymbol(symbol string) bool { - if symbol == "" || len(symbol) > MaxSymbolLen { +// validSymbol reports whether s is valid slug-compatible metadata: non-empty, +// within MaxSymbolLen, and consists only of [A-Za-z0-9_-]. +func validSymbol(s string) bool { + if s == "" || len(s) > MaxSymbolLen { return false } - for _, c := range symbol { + for _, c := range s { if !isAlnum(c) && c != '_' && c != '-' { return false } @@ -105,9 +109,9 @@ func (tok Token) TotalSupply() int64 { return tok.ledger.totalSupply } func (tok Token) KnownAccounts() int { return tok.ledger.balances.Size() } // ID returns the Identifier of the token. -// It is composed of the original realm and the provided symbol. +// It is composed of the original realm, the symbol, and the provided id. func (tok *Token) ID() string { - return tok.origRealm + "." + tok.symbol + return tok.id } // HasAddr checks if the specified address is a known account in the bank. diff --git a/examples/gno.land/p/demo/tokens/grc20/token_test.gno b/examples/gno.land/p/demo/tokens/grc20/token_test.gno index 03c8ba34a40..d11aab58a18 100644 --- a/examples/gno.land/p/demo/tokens/grc20/token_test.gno +++ b/examples/gno.land/p/demo/tokens/grc20/token_test.gno @@ -6,6 +6,7 @@ import ( "strings" "testing" + "gno.land/p/nt/seqid/v0" "gno.land/p/nt/testutils/v0" "gno.land/p/nt/uassert/v0" "gno.land/p/nt/ufmt/v0" @@ -15,30 +16,43 @@ import ( // newTestToken constructs a Token. The IIFE same-realm cross // promotes the test's EOA-origin cur into a fresh /p/grc20 // CodeRealm cur so NewToken's IsCurrent + non-empty rlm.PkgPath() -// checks pass. cross(rlm) is the explicit-rlm form of bare `cross`. -func newTestToken(_ int, rlm realm, name, symbol string, decimals int) (tok *Token, adm *PrivateLedger) { +// checks pass. +func newTestToken(name, symbol string, decimals int, id seqid.ID, rlm realm) (tok *Token, adm *PrivateLedger) { func(cur realm) { - tok, adm = NewToken(0, cur, name, symbol, decimals) + tok, adm = NewToken(name, symbol, decimals, id, cur) }(cross(rlm)) return } func TestTestImpl(cur realm, t *testing.T) { - bank, _ := newTestToken(0, cur, "Dummy", "DUMMY", 4) + bank, _ := newTestToken("Dummy", "DUMMY", 4, 0, cur) urequire.False(t, bank == nil, "dummy should not be nil") } +func TestNewTokenAllowsDuplicateSymbolInSameRealm(cur realm, t *testing.T) { + holder := testutils.TestAddress("holder") + + first, firstLedger := newTestToken("Same", "DUP", 6, 1, cur) + second, secondLedger := newTestToken("Same", "DUP", 6, 2, cur) + + urequire.True(t, first.ID() != second.ID(), "duplicate symbols should not force duplicate IDs") + + urequire.NoError(t, firstLedger.Mint(holder, 11)) + urequire.NoError(t, secondLedger.Mint(holder, 22)) + urequire.Equal(t, int64(11), first.BalanceOf(holder)) + urequire.Equal(t, int64(22), second.BalanceOf(holder)) +} + func TestNewTokenValidation(cur realm, t *testing.T) { - // Each case wraps NewToken in a crossing closure (same as newTestToken) - // to satisfy IsCurrent + non-empty pkgPath. The validation under test - // is the name/symbol/decimals checks that run after. + // Wrap NewToken to satisfy IsCurrent and pkgPath requirements, + // then validate name, symbol, and decimals. mustPanic := func(name, sym string, dec int, want error, label string) { t.Helper() // newTestToken wraps NewToken in cross(...), so the panic from // NewToken's validators crosses a realm boundary — use revive() // (defer-recover doesn't see cross-realm panics). r := revive(func() { - newTestToken(0, cur, name, sym, dec) + newTestToken(name, sym, dec, 0, cur) }) if r == nil { t.Errorf("%s: expected panic, got none", label) @@ -72,11 +86,11 @@ func TestNewTokenValidation(cur realm, t *testing.T) { mustPanic("Name", "OK", MaxDecimals+1, ErrInvalidDecimals, "decimals over cap") // Boundary positives — should NOT panic. - tok, _ := newTestToken(0, cur, strings.Repeat("a", MaxNameLen), strings.Repeat("A", MaxSymbolLen), MaxDecimals) + tok, _ := newTestToken(strings.Repeat("a", MaxNameLen), strings.Repeat("A", MaxSymbolLen), MaxDecimals, 0, cur) urequire.True(t, tok != nil, "boundary name+symbol+decimals should succeed") // UTF-8 name with non-ASCII is allowed. - tok2, _ := newTestToken(0, cur, "Доллар", "RUB", 2) + tok2, _ := newTestToken("Доллар", "RUB", 2, 0, cur) urequire.True(t, tok2 != nil, "UTF-8 name should be allowed") } @@ -87,7 +101,7 @@ func TestToken(cur realm, t *testing.T) { carl = testutils.TestAddress("carl") ) - bank, adm := newTestToken(0, cur, "Dummy", "DUMMY", 6) + bank, adm := newTestToken("Dummy", "DUMMY", 6, 0, cur) checkBalances := func(aliceEB, bobEB, carlEB int64) { t.Helper() @@ -147,7 +161,7 @@ func TestToken(cur realm, t *testing.T) { func TestMintOverflow(cur realm, t *testing.T) { alice := testutils.TestAddress("alice") bob := testutils.TestAddress("bob") - tok, adm := newTestToken(0, cur, "Dummy", "DUMMY", 6) + tok, adm := newTestToken("Dummy", "DUMMY", 6, 0, cur) safeValue := int64(1 << 62) urequire.NoError(t, adm.Mint(alice, safeValue)) @@ -166,7 +180,7 @@ func TestTransferFromAtomicity(cur realm, t *testing.T) { recipient = testutils.TestAddress("to") ) - token, admin := newTestToken(0, cur, "Test", "TEST", 6) + token, admin := newTestToken("Test", "TEST", 6, 0, cur) // owner has 100 tokens, spender has 50 allowance initialBalance := int64(100) @@ -201,7 +215,7 @@ func TestTransferFromAtomicity(cur realm, t *testing.T) { func TestMintUntilOverflow(cur realm, t *testing.T) { alice := testutils.TestAddress("alice") bob := testutils.TestAddress("bob") - tok, adm := newTestToken(0, cur, "Dummy", "DUMMY", 6) + tok, adm := newTestToken("Dummy", "DUMMY", 6, 0, cur) tests := []struct { name string diff --git a/examples/gno.land/p/demo/tokens/grc20/types.gno b/examples/gno.land/p/demo/tokens/grc20/types.gno index 6eb77c43f27..0c94b612c3b 100644 --- a/examples/gno.land/p/demo/tokens/grc20/types.gno +++ b/examples/gno.land/p/demo/tokens/grc20/types.gno @@ -73,22 +73,22 @@ type Teller interface { TransferFrom(_ int, rlm realm, from, to address, amount int64) error } -// Token represents a fungible token with a name, symbol, and a certain number -// of decimal places. It maintains a ledger for tracking balances and allowances -// of addresses. +// Token represents a fungible token with an ID, name, symbol, and a certain +// number of decimal places. It maintains a ledger for tracking balances and +// allowances of addresses. // // The Token struct provides methods for retrieving token metadata, such as the // name, symbol, and decimals, as well as methods for interacting with the // ledger, including checking balances and allowances. type Token struct { + // Identifier precomputed in NewToken to make ID() a cheap field read. + id string // Name of the token (e.g., "Dummy Token"). name string // Symbol of the token (e.g., "DUMMY"). symbol string // Number of decimal places used for the token's precision. decimals int - // Original realm of the token (e.g., "gno.land/r/demo/foo20"). - origRealm string // Pointer to the PrivateLedger that manages balances and allowances. ledger *PrivateLedger } @@ -128,12 +128,11 @@ var ( ) // Construction limits. Symbol is restricted to the same charset as -// grc20reg.validateSlug because Token.ID() = origRealm + "." + symbol is -// emitted in events and used as the canonical registry key; banning `.` -// `/` and whitespace here prevents downstream parsers from being fooled -// by ambiguous IDs (e.g. an attacker emitting tokens whose ID parses as -// belonging to a victim realm). Name is for display only and allows -// any valid UTF-8 except control characters. +// grc20reg.validateSlug because it is included in Token.ID(), which is +// emitted in events and frequently used as a registry slug; banning `.` `/` +// and whitespace here prevents downstream parsers from being fooled by +// ambiguous IDs. Name is for display only and allows any valid UTF-8 except +// control characters. const ( MaxNameLen = 64 MaxSymbolLen = 11 diff --git a/examples/gno.land/p/nt/treasury/v0/filetests/banker_grc20_filetest.gno b/examples/gno.land/p/nt/treasury/v0/filetests/banker_grc20_filetest.gno index 6583bc4627b..adda1045492 100644 --- a/examples/gno.land/p/nt/treasury/v0/filetests/banker_grc20_filetest.gno +++ b/examples/gno.land/p/nt/treasury/v0/filetests/banker_grc20_filetest.gno @@ -7,15 +7,18 @@ import ( "strings" "gno.land/p/demo/tokens/grc20" + "gno.land/p/nt/seqid/v0" "gno.land/p/nt/treasury/v0" ) const amount = int64(1000) +var nextTokenID seqid.ID + func createToken(_ int, rlm realm, name string, toMint address) *grc20.Token { // Create the token. symbol := strings.ToUpper(name) - token, ledger := grc20.NewToken(0, rlm, name, symbol, 0) + token, ledger := grc20.NewToken(name, symbol, 0, nextTokenID.Next(), rlm) // Mint the requested amount. ledger.Mint(toMint, amount) diff --git a/examples/gno.land/p/nt/treasury/v0/filetests/treasury_filetest.gno b/examples/gno.land/p/nt/treasury/v0/filetests/treasury_filetest.gno index 322e13655a7..15e4ae42942 100644 --- a/examples/gno.land/p/nt/treasury/v0/filetests/treasury_filetest.gno +++ b/examples/gno.land/p/nt/treasury/v0/filetests/treasury_filetest.gno @@ -41,7 +41,7 @@ func main(cur realm) { // Define a token and the associated lister. const amount = int64(1000) - token, ledger := grc20.NewToken(0, cur, "TestToken", "TEST", 0) + token, ledger := grc20.NewToken("TestToken", "TEST", 0, 0, cur) ledger.Mint(ownerAddr, amount) grc20Lister := func() map[string]*grc20.Token { diff --git a/examples/gno.land/r/demo/defi/foo20/foo20.gno b/examples/gno.land/r/demo/defi/foo20/foo20.gno index 6553898819a..e86cc2f34bf 100644 --- a/examples/gno.land/r/demo/defi/foo20/foo20.gno +++ b/examples/gno.land/r/demo/defi/foo20/foo20.gno @@ -19,7 +19,8 @@ var ( ) func init(cur realm) { - Token, privateLedger = grc20.NewToken(0, cur, "Foo", "FOO", 4) + // foo20 only ever creates this one token, so id 0 can't collide. + Token, privateLedger = grc20.NewToken("Foo", "FOO", 4, 0, cur) userTeller = Token.CallerTeller() privateLedger.Mint(Ownable.Owner(), 1_000_000*10_000) // @privateLedgeristrator (1M) grc20reg.Register(cross(cur), Token, "") diff --git a/examples/gno.land/r/demo/defi/grc20factory/grc20factory.gno b/examples/gno.land/r/demo/defi/grc20factory/grc20factory.gno index 759d665c8be..42148422cb9 100644 --- a/examples/gno.land/r/demo/defi/grc20factory/grc20factory.gno +++ b/examples/gno.land/r/demo/defi/grc20factory/grc20factory.gno @@ -8,13 +8,15 @@ import ( "gno.land/p/nt/avl/v0/rotree" "gno.land/p/nt/mux/v0" "gno.land/p/nt/ownable/v0" + "gno.land/p/nt/seqid/v0" "gno.land/p/nt/ufmt/v0" "gno.land/r/demo/defi/grc20reg" ) var ( - instances avl.Tree // symbol -> *instance - pager = p.NewPager(rotree.Wrap(&instances, nil), 20, false) + instances avl.Tree // symbol -> *instance + nextTokenID seqid.ID + pager = p.NewPager(rotree.Wrap(&instances, nil), 20, false) ) type instance struct { @@ -35,7 +37,7 @@ func NewWithAdmin(cur realm, name, symbol string, decimals int, initialMint, fau panic("token already exists") } - token, ledger := grc20.NewToken(0, cur, name, symbol, decimals) + token, ledger := grc20.NewToken(name, symbol, decimals, nextTokenID.Next(), cur) if initialMint > 0 { ledger.Mint(admin, initialMint) } diff --git a/examples/gno.land/r/demo/defi/grc20factory/grc20factory_test.gno b/examples/gno.land/r/demo/defi/grc20factory/grc20factory_test.gno index d1a8a224be4..725b4cb26d2 100644 --- a/examples/gno.land/r/demo/defi/grc20factory/grc20factory_test.gno +++ b/examples/gno.land/r/demo/defi/grc20factory/grc20factory_test.gno @@ -37,6 +37,8 @@ func TestReadOnlyPublicMethods(cur realm, t *testing.T) { testing.SetOriginCaller(admin) NewWithAdmin(cross(cur), "Foo", "FOO", 3, 1_111_111_000, 5_555, admin) NewWithAdmin(cross(cur), "Bar", "BAR", 3, 2_222_000, 6_666, admin) + uassert.Equal(t, Bank("FOO").ID(), "gno.land/r/demo/defi/grc20factory.FOO.0000001") + uassert.Equal(t, Bank("BAR").ID(), "gno.land/r/demo/defi/grc20factory.BAR.0000002") checkBalances("step1", 1_111_111_000, 1_111_111_000, 0, 0, 0) // admin mints to bob. diff --git a/examples/gno.land/r/demo/defi/grc20reg/grc20reg.gno b/examples/gno.land/r/demo/defi/grc20reg/grc20reg.gno index eb9e5eb68bf..235f455bac6 100644 --- a/examples/gno.land/r/demo/defi/grc20reg/grc20reg.gno +++ b/examples/gno.land/r/demo/defi/grc20reg/grc20reg.gno @@ -2,6 +2,7 @@ package grc20reg import ( "chain" + "strings" "gno.land/p/demo/tokens/grc20" "gno.land/p/moul/md" @@ -13,14 +14,22 @@ import ( var registry = avl.NewTree() // rlmPath.symbol -> *Token -// Construction lives in grc20.NewToken — it now takes (_ int, rlm realm) +// Construction lives in grc20.NewToken — it takes rlm realm last // and binds origRealm from rlm.PkgPath() under an IsCurrent assertion. -// Register stores tokens under their canonical Token.ID(): +// The registry key is the canonical fqname rlmPath.symbol (one token per +// realm+symbol), independent of Token.ID()'s trailing sequence id, so +// callers can look a token up from the (realm, symbol) pair they already +// know: // -// Token, ledger := grc20.NewToken(0, cur, name, symbol, decimals) -// grc20reg.Register(cross, Token, "") +// Token, ledger := grc20.NewToken(name, symbol, decimals, id, cur) +// key := grc20reg.Register(cross(cur), Token, "") -func Register(cur realm, token *grc20.Token, slug string) { +// Register records token under its rlmPath.symbol key and returns that key. +// Token.ID() carries a trailing sequence id (rlmPath.symbol.) that keeps +// token identities/events unique, but the registry deliberately keys by +// rlmPath.symbol so lookups don't need to know the id, and so a realm cannot +// register two tokens under the same symbol (overwrite/alias guard). +func Register(cur realm, token *grc20.Token, slug string) string { if token == nil { panic("grc20reg: nil token") } @@ -29,7 +38,9 @@ func Register(cur realm, token *grc20.Token, slug string) { } rlmPath := cur.Previous().PkgPath() key := fqname.Construct(rlmPath, token.GetSymbol()) - if key != token.ID() { + // Token.ID() == key + "." + ; verify the token originates from the + // registering realm and symbol. + if !strings.HasPrefix(token.ID(), key+".") { panic("grc20reg: token must be registered from its own realm") } if registry.Has(key) { @@ -43,6 +54,7 @@ func Register(cur realm, token *grc20.Token, slug string) { "slug", slug, "symbol", token.GetSymbol(), ) + return key } func Get(key string) *grc20.Token { @@ -70,8 +82,8 @@ func Render(path string) string { registry.Iterate("", "", func(key string, tokenI any) bool { count++ token := tokenI.(*grc20.Token) - rlmPath, symbol := fqname.Parse(key) - rlmLink := fqname.RenderLink(rlmPath, symbol) + rlmPath, tokenID := fqname.Parse(key) + rlmLink := fqname.RenderLink(rlmPath, tokenID) infoLink := "/r/demo/grc20reg:" + key s += "- " + md.Bold(md.EscapeText(token.GetName())) + " - " + rlmLink + " - " + md.Link("info", infoLink) + "\n" return false @@ -83,8 +95,8 @@ func Render(path string) string { default: // specific token key := path token := MustGet(key) - rlmPath, symbol := fqname.Parse(key) - rlmLink := fqname.RenderLink(rlmPath, symbol) + rlmPath, tokenID := fqname.Parse(key) + rlmLink := fqname.RenderLink(rlmPath, tokenID) s := ufmt.Sprintf("# %s\n", md.EscapeText(token.GetName())) s += "- symbol: " + md.Bold(md.EscapeText(token.GetSymbol())) + "\n" s += ufmt.Sprintf("- realm: %s\n", rlmLink) diff --git a/examples/gno.land/r/demo/defi/grc20reg/grc20reg_test.gno b/examples/gno.land/r/demo/defi/grc20reg/grc20reg_test.gno index 9c896d48567..e57111abc64 100644 --- a/examples/gno.land/r/demo/defi/grc20reg/grc20reg_test.gno +++ b/examples/gno.land/r/demo/defi/grc20reg/grc20reg_test.gno @@ -10,12 +10,10 @@ import ( func TestRegistry(cur realm, t *testing.T) { testing.SetRealm(testing.NewCodeRealm("gno.land/r/demo/foo")) - realmAddr := cur.PkgPath() - token, ledger := grc20.NewToken(0, cur, "TestToken", "TST", 4) + token, ledger := grc20.NewToken("TestToken", "TST", 4, 0, cur) ledger.Mint(cur.Address(), 1234567) // register - Register(cross(cur), token, "mySlug") - key := realmAddr + ".TST" + key := Register(cross(cur), token, "mySlug") regToken := Get(key) urequire.True(t, regToken != nil, "expected to find a token") // fixme: use urequire.NotNil urequire.Equal(t, regToken.GetSymbol(), "TST") @@ -39,18 +37,27 @@ func TestRegistry(cur realm, t *testing.T) { ` got = Render(key) urequire.Equal(t, expected, got) + + // The registry keys by rlmPath.symbol, so a second token with the same + // symbol in the same realm is rejected even though its Token.ID() differs + // (distinct trailing sequence id). See TestRegisterRejectsOverwrite. + second, _ := grc20.NewToken("Second", "TST", 4, 1, cur) + urequire.NotEqual(t, token.ID(), second.ID()) // ids are decoupled from symbol + urequire.AbortsContains(t, cur, "token already registered", func() { + Register(cross(cur), second, "") + }) } func TestRegisterRejectsOverwrite(cur realm, t *testing.T) { testing.SetRealm(testing.NewCodeRealm("gno.land/r/demo/grc20reg_overwrite")) - token, ledger := grc20.NewToken(0, cur, "Bar", "BAR", 4) + token, ledger := grc20.NewToken("Bar", "BAR", 4, 0, cur) ledger.Mint(cur.Address(), 11) - Register(cross(cur), token, "") + key := Register(cross(cur), token, "") - urequire.Equal(t, "BAR", Get(token.ID()).GetSymbol()) - urequire.Equal(t, int64(11), Get(token.ID()).BalanceOf(cur.Address())) + urequire.Equal(t, "BAR", Get(key).GetSymbol()) + urequire.Equal(t, int64(11), Get(key).BalanceOf(cur.Address())) - replacement, _ := grc20.NewToken(0, cur, "Replacement", "BAR", 6) + replacement, _ := grc20.NewToken("Replacement", "BAR", 6, 0, cur) urequire.AbortsContains(t, cur, "token already registered", func() { Register(cross(cur), replacement, "") }) @@ -58,7 +65,7 @@ func TestRegisterRejectsOverwrite(cur realm, t *testing.T) { func TestRegisterRejectsAliasedTokenPaths(cur realm, t *testing.T) { testing.SetRealm(testing.NewCodeRealm("gno.land/r/demo/grc20reg_alias")) - token, _ := grc20.NewToken(0, cur, "Aliased Token", "ALIAS", 4) + token, _ := grc20.NewToken("Aliased Token", "ALIAS", 4, 0, cur) Register(cross(cur), token, "first") urequire.AbortsContains(t, cur, "token already registered", func() { @@ -68,7 +75,7 @@ func TestRegisterRejectsAliasedTokenPaths(cur realm, t *testing.T) { func TestRegisterRejectsTokenFromDifferentRealm(cur realm, t *testing.T) { testing.SetRealm(testing.NewCodeRealm("gno.land/r/demo/grc20reg_id_source")) - token, _ := grc20.NewToken(0, cur, "Mismatch Token", "MISMATCH", 4) + token, _ := grc20.NewToken("Mismatch Token", "MISMATCH", 4, 0, cur) testing.SetRealm(testing.NewCodeRealm("gno.land/r/demo/grc20reg_id_target")) urequire.AbortsContains(t, cur, "token must be registered from its own realm", func() { diff --git a/examples/gno.land/r/gnoland/wugnot/wugnot.gno b/examples/gno.land/r/gnoland/wugnot/wugnot.gno index 6f79accff0b..2cb4cb8a825 100644 --- a/examples/gno.land/r/gnoland/wugnot/wugnot.gno +++ b/examples/gno.land/r/gnoland/wugnot/wugnot.gno @@ -23,7 +23,8 @@ const ( ) func init(cur realm) { - Token, adm = grc20.NewToken(0, cur, "wrapped GNOT", "wugnot", 0) + // wugnot only ever creates this one token, so id 0 can't collide. + Token, adm = grc20.NewToken("wrapped GNOT", "wugnot", 0, 0, cur) grc20reg.Register(cross(cur), Token, "") } diff --git a/examples/gno.land/r/gov/dao/v3/treasury/test/treasury_test.gno b/examples/gno.land/r/gov/dao/v3/treasury/test/treasury_test.gno index 1288a747d1a..ab538d8d92c 100644 --- a/examples/gno.land/r/gov/dao/v3/treasury/test/treasury_test.gno +++ b/examples/gno.land/r/gov/dao/v3/treasury/test/treasury_test.gno @@ -10,6 +10,7 @@ import ( "gno.land/p/demo/tokens/grc20" "gno.land/p/nt/fqname/v0" + "gno.land/p/nt/seqid/v0" "gno.land/p/nt/testutils/v0" trs_pkg "gno.land/p/nt/treasury/v0" "gno.land/p/nt/uassert/v0" @@ -25,6 +26,7 @@ import ( // These tests pass `func()` callbacks (no crossing inside the callback), // so rlm is ignored — a nil realm here is safe. var cur realm +var nextTokenID seqid.ID var ( user1Addr = testutils.TestAddress("g1user1") user2Addr = testutils.TestAddress("g1user2") @@ -84,7 +86,7 @@ func registerGRC20Tokens(_ int, rlm realm, t *testing.T, tokenNames []string, to for _, name := range tokenNames { // Create the token. symbol := strings.ToUpper(name) - token, ledger := grc20.NewToken(0, rlm, name, symbol, 0) + token, ledger := grc20.NewToken(name, symbol, 0, nextTokenID.Next(), rlm) // Register the token. grc20reg.Register(cross(rlm), token, symbol) diff --git a/examples/gno.land/r/tests/vm/test20/test20.gno b/examples/gno.land/r/tests/vm/test20/test20.gno index d1e2b3b7032..7c47bd6390b 100644 --- a/examples/gno.land/r/tests/vm/test20/test20.gno +++ b/examples/gno.land/r/tests/vm/test20/test20.gno @@ -19,6 +19,7 @@ var ( ) func init(cur realm) { - Token, PrivateLedger = grc20.NewToken(0, cur, "Test20", "TST", 4) + // test20 only ever creates this one token, so id 0 can't collide. + Token, PrivateLedger = grc20.NewToken("Test20", "TST", 4, 0, cur) grc20reg.Register(cross(cur), Token, "") } diff --git a/examples/quarantined/gno.land/r/demo/defi/bar20/bar20.gno b/examples/quarantined/gno.land/r/demo/defi/bar20/bar20.gno index 3439e5101fe..aa5802f2bfa 100644 --- a/examples/quarantined/gno.land/r/demo/defi/bar20/bar20.gno +++ b/examples/quarantined/gno.land/r/demo/defi/bar20/bar20.gno @@ -24,7 +24,8 @@ var ( ) func init(cur realm) { - Token, adm = grc20.NewToken(0, cur, "Bar", "BAR", 4) + // bar20 only ever creates this one token, so id 0 can't collide. + Token, adm = grc20.NewToken("Bar", "BAR", 4, 0, cur) UserTeller = Token.CallerTeller() grc20reg.Register(cross(cur), Token, "") } diff --git a/examples/quarantined/gno.land/r/jjoptimist/eventix/eventix_test.gno b/examples/quarantined/gno.land/r/jjoptimist/eventix/eventix_test.gno index d3c47b958ea..65ce71b8206 100644 --- a/examples/quarantined/gno.land/r/jjoptimist/eventix/eventix_test.gno +++ b/examples/quarantined/gno.land/r/jjoptimist/eventix/eventix_test.gno @@ -97,7 +97,7 @@ func TestBuyTicketWithGRC20(cur realm, t *testing.T) { var token *grc20.Token var ledger *grc20.PrivateLedger func(cur realm) { - token, ledger = grc20.NewToken(0, cur, "Test Token", "TEST", 6) + token, ledger = grc20.NewToken("Test Token", "TEST", 6, 0, cur) }(cross(cur)) testing.SetRealm(testing.NewUserRealm(alice)) diff --git a/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/render.gno b/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/render.gno index 062d7ca244d..a51b3675dc8 100644 --- a/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/render.gno +++ b/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/render.gno @@ -59,13 +59,16 @@ import ( "gno.land/p/demo/tokens/grc20" "gno.land/p/demo/tokens/grc721" "gno.land/p/demo/tokens/grc1155" + "gno.land/p/nt/seqid/v0" ) +var nextTokenID seqid.ID + // grc20.NewToken and grc721.NewBasicNFT must be constructed from a realm // context (init or a crossing function) because they assert cur.IsCurrent(). func init(cur realm) { // GRC20 token - myToken, myLedger := grc20.NewToken(0, cur, "My Token", "MTK", 6) + myToken, myLedger := grc20.NewToken("My Token", "MTK", 6, nextTokenID.Next(), cur) myTokenPath := tokenhub.RegisterToken(myToken, "my_token") // GRC721 NFT diff --git a/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno b/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno index 0a2fd879df0..edea1ca77f5 100644 --- a/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno +++ b/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub.gno @@ -29,10 +29,11 @@ func init(cur realm) { } // RegisterToken registers a token in grc20reg. -// Returns the registry key that can be used to retrieve the token. +// Returns the registry key (rlmPath.symbol) that can be used to retrieve the +// token; this is grc20reg's canonical key, not the token's full ID (which +// carries a trailing sequence id). func RegisterToken(cur realm, token *grc20.Token, slug string) string { - grc20reg.Register(cross(cur), token, slug) - return token.ID() + return grc20reg.Register(cross(cur), token, slug) } // RegisterNFT is a function that registers an NFT in an avl.Tree diff --git a/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno b/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno index 001bcd10124..0b5ea6b164f 100644 --- a/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno +++ b/examples/quarantined/gno.land/r/matijamarjanovic/tokenhub/tokenhub_test.gno @@ -9,14 +9,14 @@ import ( "gno.land/p/demo/tokens/grc20" "gno.land/p/demo/tokens/grc721" "gno.land/p/nt/avl/v0" + "gno.land/p/nt/seqid/v0" "gno.land/p/nt/uassert/v0" "gno.land/p/nt/urequire/v0" ) -const ( - tokenhubPkgPath = "gno.land/r/matijamarjanovic/tokenhub" - testRealmPkgPath = "gno.land/r/matijamarjanovic/testrealm" -) +const testRealmPkgPath = "gno.land/r/matijamarjanovic/testrealm" + +var nextGRC20ID seqid.ID // newTestNFT constructs an NFT through a same-realm cross so NewBasicNFT's // IsCurrent + non-empty PkgPath checks pass. @@ -31,12 +31,14 @@ func newTestNFT(_ int, rlm realm, name, symbol string) *grc721.BasicNFT { func TestTokenRegistration(cur realm, t *testing.T) { resetState(t) - token, _ := grc20.NewToken(0, cur, "Test Token", "TEST", 6) + token, _ := grc20.NewToken("Test Token", "TEST", 6, nextGRC20ID.Next(), cur) key := RegisterToken(cross(cur), token, "test_token") - uassert.Equal(t, tokenhubPkgPath+".TEST", key) + // The registry key is the token's rlmPath.symbol; Token.ID() extends it + // with a trailing sequence id. + uassert.True(t, strings.HasPrefix(token.ID(), key+"."), "registry key should be the token's rlmPath.symbol prefix") - retrievedToken := GetToken(tokenhubPkgPath + ".TEST") + retrievedToken := GetToken(key) urequire.True(t, retrievedToken != nil, "Should retrieve registered token") uassert.Equal(t, "Test Token", retrievedToken.GetName(), "Token name should match") @@ -83,14 +85,14 @@ func TestGRC1155Registration(cur realm, t *testing.T) { func TestBalanceRetrieval(cur realm, t *testing.T) { resetState(t) - token, ledger := grc20.NewToken(0, cur, "Balance Token", "BAL", 6) - RegisterToken(cross(cur), token, "balance_token") + token, ledger := grc20.NewToken("Balance Token", "BAL", 6, nextGRC20ID.Next(), cur) + key := RegisterToken(cross(cur), token, "balance_token") holder := unsafe.CurrentRealm().Address() ledger.Mint(holder, 1000) balances := GetUserTokenBalances(holder.String()) uassert.True(t, strings.Contains(balances, - "Token:"+tokenhubPkgPath+".BAL:1000"), "Should show correct GRC20 balance") + "Token:"+key+":1000"), "Should show correct GRC20 balance") testing.SetRealm(testing.NewCodeRealm(testRealmPkgPath)) @@ -110,7 +112,7 @@ func TestBalanceRetrieval(cur realm, t *testing.T) { nonZeroBalances := GetUserTokenBalancesNonZero(holder.String()) uassert.True(t, strings.Contains(nonZeroBalances, - "Token:"+tokenhubPkgPath+".BAL:1000"), "Should show non-zero GRC20 balance") + "Token:"+key+":1000"), "Should show non-zero GRC20 balance") } func TestErrorCases(cur realm, t *testing.T) { @@ -133,11 +135,11 @@ func TestErrorCases(cur realm, t *testing.T) { func TestTokenListingFunctions(cur realm, t *testing.T) { resetState(t) - grc20Token, _ := grc20.NewToken(0, cur, "Listing Token", "LST", 6) - RegisterToken(cross(cur), grc20Token, "listing_token") + grc20Token, _ := grc20.NewToken("Listing Token", "LST", 6, nextGRC20ID.Next(), cur) + grc20Key := RegisterToken(cross(cur), grc20Token, "listing_token") grc20List := GetAllTokens() - uassert.True(t, strings.Contains(grc20List, "Token:"+tokenhubPkgPath+".LST"), + uassert.True(t, strings.Contains(grc20List, "Token:"+grc20Key), "GetAllGRC20Tokens should list registered token") testing.SetRealm(testing.NewCodeRealm(testRealmPkgPath)) @@ -160,7 +162,7 @@ func TestTokenListingFunctions(cur realm, t *testing.T) { completeList := GetAllRegistered() uassert.True(t, strings.Contains(completeList, "NFT:"+testRealmPkgPath+".listing_nft.1"), "GetAllTokens should list NFTs") - uassert.True(t, strings.Contains(completeList, "Token:"+tokenhubPkgPath+".LST"), + uassert.True(t, strings.Contains(completeList, "Token:"+grc20Key), "GetAllTokens should list GRC20 tokens") uassert.True(t, strings.Contains(completeList, "MultiToken:"+testRealmPkgPath+".listing_mt"), "GetAllTokens should list multi-tokens") @@ -169,10 +171,10 @@ func TestTokenListingFunctions(cur realm, t *testing.T) { func TestMustGetFunctions(cur realm, t *testing.T) { resetState(t) - token, _ := grc20.NewToken(0, cur, "Must Token", "MUST", 6) - RegisterToken(cross(cur), token, "must_token") + token, _ := grc20.NewToken("Must Token", "MUST", 6, nextGRC20ID.Next(), cur) + key := RegisterToken(cross(cur), token, "must_token") - retrievedToken := MustGetToken(tokenhubPkgPath + ".MUST") + retrievedToken := MustGetToken(key) uassert.Equal(t, "Must Token", retrievedToken.GetName(), "Token name should match") // Under the unified declaring-realm borrow, calling MustGetToken diff --git a/gno.land/pkg/integration/testdata/govdao_proposal_change_law.txtar b/gno.land/pkg/integration/testdata/govdao_proposal_change_law.txtar index a8ccda8091c..81565fed67b 100644 --- a/gno.land/pkg/integration/testdata/govdao_proposal_change_law.txtar +++ b/gno.land/pkg/integration/testdata/govdao_proposal_change_law.txtar @@ -13,7 +13,7 @@ gnoland start gnokey query vm/qrender --data 'gno.land/r/gov/dao:' # add the proposal -gnokey maketx run -gas-fee 3700001ugnot -gas-wanted 41_000_000 -chainid=tendermint_test member $WORK/proposer/create_proposal.gno +gnokey maketx run -gas-fee 3700001ugnot -gas-wanted 44_000_000 -chainid=tendermint_test member $WORK/proposer/create_proposal.gno stdout OK! # call gov/dao render to check the proposal was created diff --git a/gno.land/pkg/integration/testdata/govdao_proposal_treasury_tokens_update.txtar b/gno.land/pkg/integration/testdata/govdao_proposal_treasury_tokens_update.txtar index 076d22adf23..adad0d910a0 100644 --- a/gno.land/pkg/integration/testdata/govdao_proposal_treasury_tokens_update.txtar +++ b/gno.land/pkg/integration/testdata/govdao_proposal_treasury_tokens_update.txtar @@ -17,7 +17,7 @@ gnoland start gnokey query vm/qrender --data 'gno.land/r/gov/dao:' # register the tokens and fund the treasury address -gnokey maketx run -gas-fee 2500001ugnot -gas-wanted 25_000_000 -chainid=tendermint_test member $WORK/tokens/register_tokens.gno +gnokey maketx run -gas-fee 2500001ugnot -gas-wanted 28_000_000 -chainid=tendermint_test member $WORK/tokens/register_tokens.gno # verify no balances are found even though the treasury address got tokens gnokey query vm/qrender --data 'gno.land/r/gov/dao/v3/treasury:GRC20' diff --git a/gno.land/pkg/integration/testdata/grc20_registry_emit.txtar b/gno.land/pkg/integration/testdata/grc20_registry_emit.txtar index 4230938f87a..6f6a4dae080 100644 --- a/gno.land/pkg/integration/testdata/grc20_registry_emit.txtar +++ b/gno.land/pkg/integration/testdata/grc20_registry_emit.txtar @@ -16,7 +16,7 @@ stdout OK! stdout 'GAS WANTED: [0-9]+' stdout 'GAS USED: [0-9]+' stdout 'HEIGHT: [0-9]+' -stdout 'EVENTS: \[{"type":"Transfer","attrs":\[{"key":"token","value":"gno.land/r/demo/defi/foo20.FOO"},{"key":"from","value":"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"},{"key":"to","value":"g1c8cf99clyyjr2kh9awxnfyt36cvmr703tsmazp"},{"key":"value","value":"1000000"}\],"pkg_path":"gno.land/p/demo/tokens/grc20"},{\"bytes_delta\":2029,\"fee_delta\":{\"denom\":\"ugnot\",\"amount\":202900},\"pkg_path\":\"gno.land/r/demo/defi/foo20\"}\]' +stdout 'EVENTS: \[{"type":"Transfer","attrs":\[{"key":"token","value":"gno.land/r/demo/defi/foo20.FOO.0000000"},{"key":"from","value":"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"},{"key":"to","value":"g1c8cf99clyyjr2kh9awxnfyt36cvmr703tsmazp"},{"key":"value","value":"1000000"}\],"pkg_path":"gno.land/p/demo/tokens/grc20"},{\"bytes_delta\":2029,\"fee_delta\":{\"denom\":\"ugnot\",\"amount\":202900},\"pkg_path\":\"gno.land/r/demo/defi/foo20\"}\]' stdout 'TX HASH: ' -- gnomod.toml -- diff --git a/gno.land/pkg/integration/testdata/storage_deposit_price_change.txtar b/gno.land/pkg/integration/testdata/storage_deposit_price_change.txtar index 4894e148ae1..4d8b75829da 100644 --- a/gno.land/pkg/integration/testdata/storage_deposit_price_change.txtar +++ b/gno.land/pkg/integration/testdata/storage_deposit_price_change.txtar @@ -69,7 +69,7 @@ stdout 'storage: 3374, deposit: 337400' ## Verify user received a refund (balance should have increased despite gas fees) gnokey query auth/accounts/$test1_user_addr -stdout '"coins": "9999854' +stdout '"coins": "9999853' -- loader/load_govdao.gno -- package loader diff --git a/gno.land/pkg/integration/testdata/user_journey.txtar b/gno.land/pkg/integration/testdata/user_journey.txtar index df3e17afb4e..b8f0faf64f5 100644 --- a/gno.land/pkg/integration/testdata/user_journey.txtar +++ b/gno.land/pkg/integration/testdata/user_journey.txtar @@ -99,7 +99,7 @@ stdout 'OK!' env DISPERSE_REALM_ADDR=g1yryw6qs8h9anvguu4dfdc0u7zh4gvv8vqf59sj # user1 creates a new token with 5 decimals and 10 drip ammount for the faucet -gnokey maketx call -pkgpath gno.land/r/demo/defi/grc20factory -func New -args MyAwesomeToken -args MAT -args 5 -args 0 -args 10 -gas-fee 1100001ugnot -gas-wanted 11_000_000 -chainid=tendermint_test user1 +gnokey maketx call -pkgpath gno.land/r/demo/defi/grc20factory -func New -args MyAwesomeToken -args MAT -args 5 -args 0 -args 10 -gas-fee 1100001ugnot -gas-wanted 14_000_000 -chainid=tendermint_test user1 stdout 'OK!' # user1 mints 1000 MAT to himself diff --git a/gno.land/pkg/integration/testdata/wugnot.txtar b/gno.land/pkg/integration/testdata/wugnot.txtar index 0c5aac4a3f0..72e3845a854 100644 --- a/gno.land/pkg/integration/testdata/wugnot.txtar +++ b/gno.land/pkg/integration/testdata/wugnot.txtar @@ -72,7 +72,7 @@ stdout 'Known accounts..: 1' # user3 is no longer known, as they withdrew all th # ============================================ # Deploy the malicious intermediary realm -gnokey maketx addpkg -pkgdir $WORK/mitm -pkgpath gno.land/r/test/mitm -gas-fee 1000001ugnot -gas-wanted 10_000_000 -chainid=tendermint_test user1 +gnokey maketx addpkg -pkgdir $WORK/mitm -pkgpath gno.land/r/test/mitm -gas-fee 1200001ugnot -gas-wanted 12_000_000 -chainid=tendermint_test user1 stdout 'OK!' # user1 tries to deposit through the malicious intermediary - this should FAIL