diff --git a/test4.gno.land/README.md b/test4.gno.land/README.md index 7180d044..535c3b00 100644 --- a/test4.gno.land/README.md +++ b/test4.gno.land/README.md @@ -2,7 +2,7 @@ ## TXs ``` -20167 +20175 ``` ## addpkgs @@ -11,18 +11,18 @@ ## top realm calls ``` - 4503 "gno.land/r/demo/wugnot" + 4511 "gno.land/r/demo/wugnot" 1142 "gno.land/r/gnoswap/gns" - 970 "gno.land/r/gnoswap/v2/gns" + 977 "gno.land/r/gnoswap/v2/gns" 764 "gno.land/r/gnoswap/router" 755 "gno.land/r/gnoswap/position" - 747 "gno.land/r/gnoswap/v2/router" - 668 "gno.land/r/gnoswap/v2/position" + 751 "gno.land/r/gnoswap/v2/router" + 669 "gno.land/r/gnoswap/v2/position" 654 "gno.land/r/gnoswap/gnft" 637 "gno.land/r/onbloc/usdc" 625 "gno.land/r/onbloc/foo" 485 "gno.land/r/gnoswap/v2/staker" - 437 "gno.land/r/gnoswap/v2/gnft" + 438 "gno.land/r/gnoswap/v2/gnft" 355 "gno.land/r/onbloc/bar" 320 "gno.land/r/gnoswap/staker" 262 "gno.land/r/onbloc/baz" @@ -76,6 +76,7 @@ 6 "gno.land/r/varmeta/demo/v402/domain/registrar" 6 "gno.land/r/villaquiranm/grc20_launchpad" 5 "gno.land/r/demo/art/millipede" + 5 "gno.land/r/varmeta/demo/v6/domain/registrar" 5 "gno.land/r/varmeta/demo1/domain/registrar" 4 "gno.land/r/demo/domain/registrar" 4 "gno.land/r/demo/user" @@ -90,7 +91,6 @@ 3 "gno.land/r/g1w6886hdj2tet0seyw6kn8fl92sx06prgd9w9j8/games/v1/diceroller" 3 "gno.land/r/gnoswap/v2/community_pool" 3 "gno.land/r/varmeta/demo/v406/domain/registrar" - 3 "gno.land/r/varmeta/demo/v6/domain/registrar" 3 "gno.land/r/varmeta/test/gettime" 2 "gno.land/r/g125em6arxsnj49vx35f0n0z34putv5ty3376fg5/checkerr" 2 "gno.land/r/g13f63ua8uhmuf9mgc0x8zfz04yrsaqh7j78vcgq/monkey" diff --git a/test4.gno.land/backup_2486724-2518430.jsonl b/test4.gno.land/backup_2486724-2518430.jsonl new file mode 100755 index 00000000..be714089 --- /dev/null +++ b/test4.gno.land/backup_2486724-2518430.jsonl @@ -0,0 +1,8 @@ +{"tx":{"msg":[{"@type":"/bank.MsgSend","from_address":"g1njagaeg7e398hze39ygfgvc4gwsh6lkz7dwnuz","to_address":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","amount":"15000000ugnot"}],"fee":{"gas_wanted":"100000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"As2VmUYjIs+Q+esyTyrgv8nQpAtImqdkUUKCpJYzNQ4a"},"signature":"1rDCRYdVmZpkBfh8nWv0ZwWzHOikJEr2gPuPuoTq/i0ufWcm6dZehMjEclLiqg1SQJpHb9ddQCcrj26UaeBziw=="}],"memo":""},"blockNum":"2497114"} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"1000000ugnot","pkg_path":"gno.land/r/gnoswap/v2/router","func":"SwapRoute","args":["gnot","gno.land/r/gnoswap/v2/gns","1000000","EXACT_IN","gno.land/r/demo/wugnot:gno.land/r/onbloc/baz:3000*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:3000*POOL*gno.land/r/onbloc/bar:gno.land/r/gnoswap/v2/gns:3000","100","4836321"]}],"fee":{"gas_wanted":"100000000","gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"ApnYLcvsto0hCsEHaBsULXtYMWbNcjXZcLMXbSNoGLGC"},"signature":"sC6YLJ/yBuTrOO2siJXbthiRlTak66OQrdfe09EyERhfGsMQbFUYYmv07urVVghRBf9C01zpvqZbMJNCn9oBdg=="}],"memo":""},"blockNum":"2497144"} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/gnoswap/v2/router","func":"SwapRoute","args":["gno.land/r/gnoswap/v2/gns","gnot","4860000","EXACT_IN","gno.land/r/gnoswap/v2/gns:gno.land/r/demo/wugnot:3000","100","4766485"]}],"fee":{"gas_wanted":"100000000","gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"ApnYLcvsto0hCsEHaBsULXtYMWbNcjXZcLMXbSNoGLGC"},"signature":"dDJGB5VaLkimkKHIiZ6+6z9qzkzzSM4M3fxns1gD5gwiCVT+e7FZ6nVE5q8RcXL6bQPgao+SLs13XdfcVqgTRw=="}],"memo":""},"blockNum":"2497158"} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"1000000ugnot","pkg_path":"gno.land/r/gnoswap/v2/router","func":"SwapRoute","args":["gnot","gno.land/r/gnoswap/v2/gns","1000000","EXACT_IN","gno.land/r/demo/wugnot:gno.land/r/onbloc/baz:3000*POOL*gno.land/r/onbloc/baz:gno.land/r/onbloc/bar:3000*POOL*gno.land/r/onbloc/bar:gno.land/r/gnoswap/v2/gns:3000","100","4835912"]}],"fee":{"gas_wanted":"100000000","gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"ApnYLcvsto0hCsEHaBsULXtYMWbNcjXZcLMXbSNoGLGC"},"signature":"w+n/p/BdFuGCqcGM2o5BoH+h3qbXN5oeA4nw9iG08NtT/F1EKv7G70fLtEdjDe3sOLveV+n8o2Hr0eJD3qXv6Q=="}],"memo":""},"blockNum":"2497407"} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","1000000"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","1026744"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g1vsm68lq9cpn7x507s6gh59anmx86kxfhzyszu2","1000000"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"1000000ugnot","pkg_path":"gno.land/r/gnoswap/v2/position","func":"Mint","args":["gnot","gno.land/r/gnoswap/v2/gns","3000","-6960","7020","1000000","1026744","995000","1021610","7282571140","g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql"]},{"@type":"/vm.m_call","caller":"g1gqu09hspeygv83vyl2utl67s3m3jntw46vuvql","send":"","pkg_path":"gno.land/r/gnoswap/v2/gnft","func":"SetTokenURILast","args":null}],"fee":{"gas_wanted":"100000000","gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"ApnYLcvsto0hCsEHaBsULXtYMWbNcjXZcLMXbSNoGLGC"},"signature":"dGwxqSQ7VXI7a+vq+hCSj3EzT8IrEYm+U5VqKTQuorYPRJxRFmp9gA49dUw/WOMF9HBNU0pyO4l9649XbCNFhw=="}],"memo":""},"blockNum":"2497431"} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g14ks20xj4csuyn0jucwdt96rgr6cenht30avkyr","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g126swhfaq2vyvvjywevhgw7lv9hg8qan93dasu8","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g14ks20xj4csuyn0jucwdt96rgr6cenht30avkyr","send":"","pkg_path":"gno.land/r/gnoswap/v2/gns","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g14ks20xj4csuyn0jucwdt96rgr6cenht30avkyr","send":"","pkg_path":"gno.land/r/demo/wugnot","func":"Approve","args":["g1cnz5gm2l09pm2k6rknjjar9a2w53fdhk4yjzy5","18446744073709551615"]},{"@type":"/vm.m_call","caller":"g14ks20xj4csuyn0jucwdt96rgr6cenht30avkyr","send":"","pkg_path":"gno.land/r/gnoswap/v2/router","func":"SwapRoute","args":["gno.land/r/gnoswap/v2/gns","gnot","100000000","EXACT_IN","gno.land/r/gnoswap/v2/gns:gno.land/r/demo/wugnot:3000","100","98075577"]}],"fee":{"gas_wanted":"100000000","gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A5AUjFIdvgvvYPCszkBSj4LyyQVI/uczvuxzbg5IRDB3"},"signature":"6B1V5YRYUbFRDMypYYKKMP2MQAC1TkQX5XQ5OxH4ySB5Q8LOT8ykWP6SKML8OjlM5GxEcRstktrEOVRDasOynA=="}],"memo":""},"blockNum":"2504384"} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9","send":"100ugnot","pkg_path":"gno.land/r/varmeta/demo/v6/domain/registrar","func":"CommitHash","args":["thinhnxtest.gno","ec2738feb2bbb0bc783eb4667903391416372ba6ed8b8dddbebbdb37e5102473"]}],"fee":{"gas_wanted":"10000000","gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AqD4AqUY/VAPgrwGhq7e9Px4Pj81GRDZYwRvm+A69599"},"signature":"NCxnRs5v1AZPWSXXihNYwykmO/ruvTjBNxyIQyJLcRoxJ/bSxRTg4KevQstj1xjm77Pee1xoAvmy6jDIBQuxkQ=="}],"memo":""},"blockNum":"2504921"} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9","send":"100ugnot","pkg_path":"gno.land/r/varmeta/demo/v6/domain/registrar","func":"CommitPrice","args":["thinhnxtest.gno","10","test"]}],"fee":{"gas_wanted":"10000000","gas_fee":"1ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AqD4AqUY/VAPgrwGhq7e9Px4Pj81GRDZYwRvm+A69599"},"signature":"JMVWmbwVXjzcKGdCKb9ZUnPO1f9Cmj7PUp3/If++m3AaxVpABBUSqEzs0AM0eFpN0kDapd4kqLzkKL0rrQDlnw=="}],"memo":""},"blockNum":"2504945"} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_metadata.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_metadata.gno new file mode 100644 index 00000000..388902c4 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_metadata.gno @@ -0,0 +1,38 @@ +package domain + +import ( + "time" +) + +// Trait represents a key-value pair with an optional display type for metadata attributes +type Trait struct { + DisplayType string // Optional display type (e.g., "date", "number", etc.) + TraitType string // Type of the trait (e.g., "age", "height", etc.) + Value string // Value of the trait +} + +// Metadata represents the metadata associated with a domain +type Metadata struct { + Avatar string // URL or identifier for an avatar image + RegistrationTime time.Time // The time when the domain was registered + ExpirationTime time.Time // The time when the domain will be expire + Attributes []Trait // Additional attributes of the domain + Description string // A description of the domain + ContactInfo string // Contact information for the domain owner + RenewalFee string // The fee required to renew the domain, represented as a string +} + +// NewMetadata creates a new Metadata instance +func NewMetadata(avatar, description, contactInfo, renewalFee string, + registrationTime, expirationTime time.Time, attributes []Trait, +) Metadata { + return Metadata{ + Avatar: avatar, + RegistrationTime: registrationTime, + ExpirationTime: expirationTime, + RenewalFee: renewalFee, + Attributes: attributes, + Description: description, + ContactInfo: contactInfo, + } +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_registry.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_registry.gno new file mode 100644 index 00000000..77a2df9b --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_registry.gno @@ -0,0 +1,236 @@ +package domain + +import ( + "std" + "time" + + "gno.land/p/demo/avl" + "gno.land/p/varmeta/demo/v6/grc/grc721" +) + +// domainRegistry represents a registry for domain names with metadata +type domainRegistry struct { + domains grc721.IGRC721 // Interface for basic NFT functionality + metadata *avl.Tree // AVL tree for storing domain metadata + expDate time.Time +} + +// DomainRegistry defines the methods for managing domain names and metadata +type DomainRegistry interface { + BalanceOf(owner std.Address) (uint64, error) + OwnerOf(domainName string) (std.Address, error) + SafeTransferFrom(from, to std.Address, domainName string) error + TransferFrom(from, to std.Address, domainName string) error + Approve(approved std.Address, domainName string) error + SetApprovalForAll(operator std.Address, approved bool) error + GetApproved(domainName string) (std.Address, error) + IsApprovedForAll(owner, operator std.Address) bool + Mint(to std.Address, domainName string) error + + RegisterDomain(owner std.Address, domainName string, metadata Metadata, dur time.Duration) error + SetDomainData(domainName string, metadata Metadata) error + GetDomainData(domainName string, field MetadataField) (Metadata, error) + GetDomainFields(domainName string, fields []MetadataField) (Metadata, error) + RenewDomain(domainName string, additionalDuration time.Duration) error + GetExpirationDate(domainName string) time.Time + SetExpirationDate(domainName string, expDate time.Time) bool +} + +// NewDomainRegistry creates a new domain registry with metadata extensions +func NewDomainRegistry(name, symbol string) *domainRegistry { + registry := grc721.NewBasicNFT(name, symbol) + + return &domainRegistry{ + domains: registry, + metadata: avl.NewTree(), + } +} + +// RegisterDomain registers a new domain with the given metadata +func (d *domainRegistry) RegisterDomain(owner std.Address, domainName string, metadata Metadata, dur time.Duration) error { + err := d.domains.Mint(owner, grc721.TokenID(domainName)) + if err != nil { + return err + } + d.expDate = time.Now().Add(dur) + d.metadata.Set(domainName, metadata) + + return nil +} + +// RenewDomain extends the expiration time of a domain name +func (d *domainRegistry) RenewDomain(domainName string, additionalDuration time.Duration) error { + _, found := d.metadata.Get(domainName) + if !found { + return ErrInvalidDomainName + } + + owner, err := d.domains.OwnerOf(grc721.TokenID(domainName)) + if err != nil { + return err + } + + caller := std.PrevRealm().Addr() + if caller != owner { + return ErrUnauthorized + } + + // set new expiration date + d.expDate = d.expDate.Add(additionalDuration) + return nil +} + +// SetDomainData sets the metadata for a given domain name +func (d *domainRegistry) SetDomainData(domainName string, metadata Metadata) error { + owner, err := d.domains.OwnerOf(grc721.TokenID(domainName)) + if err != nil { + return err + } + + caller := std.PrevRealm().Addr() + if caller != owner { + return ErrUnauthorized + } + + d.metadata.Set(domainName, metadata) + return nil +} + +// GetDomainFields retrieves multiple fields of metadata for a given domain +func (d *domainRegistry) GetDomainFields(domainName string, fields []MetadataField) (Metadata, error) { + data, found := d.metadata.Get(domainName) + if !found { + return Metadata{}, ErrInvalidDomainName + } + + metadata := data.(Metadata) + + if len(fields) == 0 { + return metadata, nil + } + + var result Metadata + for _, field := range fields { + switch field { + case FieldAvatar: + result.Avatar = metadata.Avatar + case FieldRegistrationTime: + result.RegistrationTime = metadata.RegistrationTime + case FieldExpirationTime: + result.ExpirationTime = metadata.ExpirationTime + case FieldRenewalFee: + result.RenewalFee = metadata.RenewalFee + case FieldAttributes: + result.Attributes = metadata.Attributes + case FieldDescription: + result.Description = metadata.Description + case FieldContactInfo: + result.ContactInfo = metadata.ContactInfo + default: + return Metadata{}, ErrInvalidMetadataField + } + } + + return result, nil +} + +// GetDomainData retrieves metadata for a given domain +func (d *domainRegistry) GetDomainData(domainName string, field MetadataField) (Metadata, error) { + data, found := d.metadata.Get(domainName) + if !found { + return Metadata{}, ErrInvalidDomainName + } + + metadata := data.(Metadata) + + switch field { + case FieldAvatar: + return Metadata{ + Avatar: metadata.Avatar, + }, nil + case FieldRegistrationTime: + return Metadata{ + RegistrationTime: metadata.RegistrationTime, + }, nil + case FieldExpirationTime: + return Metadata{ + ExpirationTime: metadata.ExpirationTime, + }, nil + case FieldRenewalFee: + return Metadata{ + RenewalFee: metadata.RenewalFee, + }, nil + case FieldAttributes: + return Metadata{ + Attributes: metadata.Attributes, + }, nil + case FieldDescription: + return Metadata{ + Description: metadata.Description, + }, nil + case FieldContactInfo: + return Metadata{ + ContactInfo: metadata.ContactInfo, + }, nil + default: + return Metadata{}, ErrInvalidMetadataField + } +} + +// BalanceOf returns the number of domains owned by a given address +func (d *domainRegistry) BalanceOf(owner std.Address) (uint64, error) { + return d.domains.BalanceOf(owner) +} + +// OwnerOf returns the owner of a given domain name +func (d *domainRegistry) OwnerOf(domainName string) (std.Address, error) { + return d.domains.OwnerOf(grc721.TokenID(domainName)) +} + +// SafeTransferFrom safely transfers a domain from one address to another +func (d *domainRegistry) SafeTransferFrom(from, to std.Address, domainName string) error { + return d.domains.SafeTransferFrom(from, to, grc721.TokenID(domainName)) +} + +// TransferFrom transfers a domain from one address to another +func (d *domainRegistry) TransferFrom(from, to std.Address, domainName string) error { + return d.domains.TransferFrom(from, to, grc721.TokenID(domainName)) +} + +// Approve grants approval to another address to manage a specific domain +func (d *domainRegistry) Approve(approved std.Address, domainName string) error { + return d.domains.Approve(approved, grc721.TokenID(domainName)) +} + +// SetApprovalForAll sets approval for an operator to manage all domains of the owner +func (d *domainRegistry) SetApprovalForAll(operator std.Address, approved bool) error { + return d.domains.SetApprovalForAll(operator, approved) +} + +// GetApproved returns the approved address for a specific domain +func (d *domainRegistry) GetApproved(domainName string) (std.Address, error) { + return d.domains.GetApproved(grc721.TokenID(domainName)) +} + +// IsApprovedForAll checks if an operator is approved to manage all domains of the owner +func (d *domainRegistry) IsApprovedForAll(owner, operator std.Address) bool { + return d.domains.IsApprovedForAll(owner, operator) +} + +// Mint creates a new domain for a given address +func (d *domainRegistry) Mint(to std.Address, domainName string) error { + return d.domains.Mint(to, grc721.TokenID(domainName)) +} + +func (d *domainRegistry) GetExpirationDate(domainName string) time.Time { + return d.expDate +} + +func (d *domainRegistry) SetExpirationDate(domainName string, expDate time.Time) bool { + _, found := d.metadata.Get(domainName) + if !found { + return false + } + d.expDate = expDate + return true +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_registry_test.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_registry_test.gno new file mode 100644 index 00000000..46f270a8 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/domain_registry_test.gno @@ -0,0 +1,378 @@ +package domain + +import ( + "std" + "testing" + "time" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/urequire" + "gno.land/p/varmeta/demo/v6/grc/grc721" +) + +var ( + addr1 = testutils.TestAddress("bob") + addr2 = testutils.TestAddress("alice") +) + +func TestRegisterDomain(t *testing.T) { + registry := NewDomainRegistry("GNO Name Service", "GNS") + + cases := []struct { + name string + owner std.Address + domainName string + metadata Metadata + expectError bool + }{ + { + name: "Successful Registration", + owner: addr1, + domainName: "registered.gno", + metadata: Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A registered domain", + ContactInfo: "contact@registered.com", + }, + expectError: false, + }, + { + name: "Duplicate Registration", + owner: addr1, + domainName: "registered.gno", + metadata: Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A registered domain", + ContactInfo: "gno_name_service@gno.land", + }, + expectError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(c.owner)) + std.TestSetOrigCaller(c.owner) + + err := registry.RegisterDomain(c.owner, c.domainName, c.metadata) + if c.expectError { + urequire.Error(t, err) + } else { + urequire.NoError(t, err) + + retrievedOwner, err := registry.OwnerOf(c.domainName) + urequire.NoError(t, err) + urequire.Equal(t, c.owner, retrievedOwner) + } + }) + } +} + +func TestSetDomainData(t *testing.T) { + registry := NewDomainRegistry("GNO Name Service", "GNS") + + cases := []struct { + name string + owner std.Address + caller std.Address + domainName string + metadata Metadata + expectError bool + }{ + { + name: "Owner Sets Metadata", + owner: addr1, + caller: addr1, + domainName: "test.gno", + metadata: Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A test domain", + ContactInfo: "gno_name_service@gno.land", + }, + expectError: false, + }, + { + name: "Non-Owner Sets Metadata", + owner: addr1, + caller: addr2, + domainName: "test.gno", + metadata: Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A test domain", + ContactInfo: "gno_name_service@gno.land", + }, + expectError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(c.owner)) + std.TestSetOrigCaller(c.owner) + registry.RegisterDomain(c.owner, c.domainName, c.metadata) + + std.TestSetRealm(std.NewUserRealm(c.caller)) + std.TestSetOrigCaller(c.caller) + err := registry.SetDomainData(c.domainName, c.metadata) + + if c.expectError { + urequire.Error(t, err) + urequire.Equal(t, err.Error(), ErrUnauthorized.Error()) + } else { + urequire.NoError(t, err) + retrievedMetadata, err := registry.GetDomainData(c.domainName, FieldAvatar) + urequire.NoError(t, err) + urequire.Equal(t, c.metadata.Avatar, retrievedMetadata.Avatar) + } + }) + } +} + +func TestRenewDomain(t *testing.T) { + registry := NewDomainRegistry("GNO Name Service", "GNS") + + cases := []struct { + name string + owner std.Address + caller std.Address + domainName string + additionalTime time.Duration + expectError bool + expectedExpiry time.Time + }{ + { + name: "Successful Renewal", + owner: addr1, + caller: addr1, + domainName: "renewable.gno", + additionalTime: 30 * 24 * time.Hour, + expectError: false, + }, + { + name: "Non-Owner Attempts Renewal", + owner: addr1, + caller: addr2, + domainName: "renewable.gno", + additionalTime: 30 * 24 * time.Hour, + expectError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(c.owner)) + std.TestSetOrigCaller(c.owner) + + metadata := Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A renewable domain", + ContactInfo: "gno_name_service@gno.land", + } + + registry.RegisterDomain(c.owner, c.domainName, metadata) + + std.TestSetRealm(std.NewUserRealm(c.caller)) + std.TestSetOrigCaller(c.caller) + + err := registry.RenewDomain(c.domainName, c.additionalTime) + if c.expectError { + urequire.Error(t, err) + urequire.Equal(t, err.Error(), ErrUnauthorized.Error()) + } else { + urequire.NoError(t, err) + renewedMetadata, err := registry.GetDomainData(c.domainName, FieldExpirationTime) + urequire.NoError(t, err) + // urequire.True(t, renewedMetadata.ExpirationTime.After(metadata.ExpirationTime)) + } + }) + } +} + +func TestGetDomainData(t *testing.T) { + registry := NewDomainRegistry("GNO Name Service", "GNS") + + cases := []struct { + name string + domainName string + field MetadataField + expectError bool + expectedVal string + }{ + { + name: "Retrieve Avatar", + domainName: "test.gno", + field: FieldAvatar, + expectError: false, + expectedVal: "avatar_url", + }, + { + name: "Invalid Domain Name", + domainName: "invalid.gno", + field: FieldAvatar, + expectError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + if !c.expectError { + owner := addr1 + std.TestSetRealm(std.NewUserRealm(owner)) + std.TestSetOrigCaller(owner) + + metadata := Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A test domain", + ContactInfo: "gno_name_service@gno.land", + } + + err := registry.RegisterDomain(owner, c.domainName, metadata) + urequire.NoError(t, err) + } + + _, err := registry.GetDomainData(c.domainName, c.field) + if c.expectError { + urequire.Error(t, err) + urequire.Equal(t, err.Error(), ErrInvalidDomainName.Error()) + } else { + urequire.NoError(t, err) + } + }) + } +} + +func TestGetDomainFields(t *testing.T) { + registry := NewDomainRegistry("GNO Name Service", "GNS") + + cases := []struct { + name string + domainName string + fields []MetadataField + expectError bool + expected Metadata + }{ + { + name: "Retrieve Multiple Fields", + domainName: "test.gno", + fields: []MetadataField{FieldAvatar, FieldDescription, FieldContactInfo}, + expectError: false, + expected: Metadata{ + Avatar: "avatar_url", + Description: "A test domain", + ContactInfo: "gno_name_service@gno.land", + }, + }, + { + name: "Invalid Domain", + domainName: "invalid.gno", + fields: []MetadataField{FieldAvatar}, + expectError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + if !c.expectError { + owner := addr1 + std.TestSetRealm(std.NewUserRealm(owner)) + std.TestSetOrigCaller(owner) + + metadata := Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A test domain", + ContactInfo: "gno_name_service@gno.land", + } + + err := registry.RegisterDomain(owner, c.domainName, metadata) + urequire.NoError(t, err) + } + + retrievedMetadata, err := registry.GetDomainFields(c.domainName, c.fields) + if c.expectError { + urequire.Error(t, err) + urequire.Equal(t, err.Error(), ErrInvalidDomainName.Error()) + } else { + urequire.NoError(t, err) + urequire.Equal(t, c.expected.Avatar, retrievedMetadata.Avatar) + urequire.Equal(t, c.expected.Description, retrievedMetadata.Description) + urequire.Equal(t, c.expected.ContactInfo, retrievedMetadata.ContactInfo) + } + }) + } +} + +func TestTransferDomain(t *testing.T) { + registry := NewDomainRegistry("GNO Name Service", "GNS") + + cases := []struct { + name string + owner std.Address + newOwner std.Address + caller std.Address + domainName string + expectError bool + }{ + { + name: "Successful Transfer", + owner: addr1, + newOwner: addr2, + caller: addr1, + domainName: "transfer.gno", + expectError: false, + }, + { + name: "Non-Owner Attempts Transfer", + owner: addr1, + newOwner: addr2, + caller: addr2, + domainName: "transfer.gno", + expectError: true, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(c.owner)) + std.TestSetOrigCaller(c.owner) + + metadata := Metadata{ + Avatar: "avatar_url", + RegistrationTime: time.Now(), + ExpirationTime: time.Now().Add(365 * 24 * time.Hour), + Description: "A transferable domain", + ContactInfo: "gno_name_service@gno.land", + } + + registry.RegisterDomain(c.owner, c.domainName, metadata) + + std.TestSetRealm(std.NewUserRealm(c.caller)) + std.TestSetOrigCaller(c.caller) + + err := registry.TransferFrom(c.owner, c.newOwner, c.domainName) + if c.expectError { + urequire.Error(t, err) + urequire.Equal(t, err.Error(), grc721.ErrTransferFromIncorrectOwner.Error()) + } else { + urequire.NoError(t, err) + + retrievedOwner, err := registry.OwnerOf(c.domainName) + urequire.NoError(t, err) + urequire.Equal(t, c.newOwner, retrievedOwner) + } + }) + } +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/errors.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/errors.gno new file mode 100644 index 00000000..3de5d750 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/errors.gno @@ -0,0 +1,12 @@ +package domain + +import ( + "errors" +) + +var ( + ErrUnauthorized = errors.New("caller is not domain owner") + ErrInvalidDomainName = errors.New("invalid domain name") + ErrInvalidMetadataField = errors.New("invalid metadata field") + ErrInsufficientFunds = errors.New("insufficient funds for renewal") +) diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/pkg_metadata.json b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/pkg_metadata.json new file mode 100644 index 00000000..f7c3b6e7 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/pkg_metadata.json @@ -0,0 +1 @@ +{"creator":"g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9","deposit":""} \ No newline at end of file diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/utils.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/utils.gno new file mode 100644 index 00000000..13e40d99 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/domain:2465174/utils.gno @@ -0,0 +1,13 @@ +package domain + +type MetadataField int + +const ( + FieldAvatar MetadataField = iota + FieldRegistrationTime + FieldRenewalFee + FieldExpirationTime + FieldAttributes + FieldDescription + FieldContactInfo +) diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/basic_nft.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/basic_nft.gno new file mode 100644 index 00000000..bec7338d --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/basic_nft.gno @@ -0,0 +1,378 @@ +package grc721 + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ufmt" +) + +type basicNFT struct { + name string + symbol string + owners avl.Tree // tokenId -> OwnerAddress + balances avl.Tree // OwnerAddress -> TokenCount + tokenApprovals avl.Tree // TokenId -> ApprovedAddress + tokenURIs avl.Tree // TokenId -> URIs + operatorApprovals avl.Tree // "OwnerAddress:OperatorAddress" -> bool +} + +// Returns new basic NFT +func NewBasicNFT(name string, symbol string) *basicNFT { + return &basicNFT{ + name: name, + symbol: symbol, + + owners: avl.Tree{}, + balances: avl.Tree{}, + tokenApprovals: avl.Tree{}, + tokenURIs: avl.Tree{}, + operatorApprovals: avl.Tree{}, + } +} + +func (s *basicNFT) Name() string { return s.name } +func (s *basicNFT) Symbol() string { return s.symbol } +func (s *basicNFT) TokenCount() uint64 { return uint64(s.owners.Size()) } + +// BalanceOf returns balance of input address +func (s *basicNFT) BalanceOf(addr std.Address) (uint64, error) { + if err := isValidAddress(addr); err != nil { + return 0, err + } + + balance, found := s.balances.Get(addr.String()) + if !found { + return 0, nil + } + + return balance.(uint64), nil +} + +// OwnerOf returns owner of input token id +func (s *basicNFT) OwnerOf(tid TokenID) (std.Address, error) { + owner, found := s.owners.Get(string(tid)) + if !found { + return "", ErrInvalidTokenId + } + + return owner.(std.Address), nil +} + +// TokenURI returns the URI of input token id +func (s *basicNFT) TokenURI(tid TokenID) (string, error) { + uri, found := s.tokenURIs.Get(string(tid)) + if !found { + return "", ErrInvalidTokenId + } + + return uri.(string), nil +} + +func (s *basicNFT) SetTokenURI(tid TokenID, tURI TokenURI) (bool, error) { + // check for invalid TokenID + if !s.exists(tid) { + return false, ErrInvalidTokenId + } + + // check for the right owner + owner, err := s.OwnerOf(tid) + if err != nil { + return false, err + } + caller := std.PrevRealm().Addr() + if caller != owner { + return false, ErrCallerIsNotOwner + } + s.tokenURIs.Set(string(tid), string(tURI)) + return true, nil +} + +// IsApprovedForAll returns true if operator is approved for all by the owner. +// Otherwise, returns false +func (s *basicNFT) IsApprovedForAll(owner, operator std.Address) bool { + key := owner.String() + ":" + operator.String() + _, found := s.operatorApprovals.Get(key) + if !found { + return false + } + + return true +} + +// Approve approves the input address for particular token +func (s *basicNFT) Approve(to std.Address, tid TokenID) error { + if err := isValidAddress(to); err != nil { + return err + } + + owner, err := s.OwnerOf(tid) + if err != nil { + return err + } + if owner == to { + return ErrApprovalToCurrentOwner + } + + caller := std.PrevRealm().Addr() + if caller != owner && !s.IsApprovedForAll(owner, caller) { + return ErrCallerIsNotOwnerOrApproved + } + + s.tokenApprovals.Set(string(tid), to.String()) + event := ApprovalEvent{owner, to, tid} + emit(&event) + + return nil +} + +// GetApproved return the approved address for token +func (s *basicNFT) GetApproved(tid TokenID) (std.Address, error) { + addr, found := s.tokenApprovals.Get(string(tid)) + if !found { + return zeroAddress, ErrTokenIdNotHasApproved + } + + return std.Address(addr.(string)), nil +} + +// SetApprovalForAll can approve the operator to operate on all tokens +func (s *basicNFT) SetApprovalForAll(operator std.Address, approved bool) error { + if err := isValidAddress(operator); err != nil { + return ErrInvalidAddress + } + + caller := std.PrevRealm().Addr() + return s.setApprovalForAll(caller, operator, approved) +} + +// Safely transfers `tokenId` token from `from` to `to`, checking that +// contract recipients are aware of the GRC721 protocol to prevent +// tokens from being forever locked. +func (s *basicNFT) SafeTransferFrom(from, to std.Address, tid TokenID) error { + caller := std.PrevRealm().Addr() + if !s.isApprovedOrOwner(caller, tid) { + return ErrCallerIsNotOwnerOrApproved + } + + err := s.transfer(from, to, tid) + if err != nil { + return err + } + + if !s.checkOnGRC721Received(from, to, tid) { + return ErrTransferToNonGRC721Receiver + } + + return nil +} + +// Transfers `tokenId` token from `from` to `to`. +func (s *basicNFT) TransferFrom(from, to std.Address, tid TokenID) error { + caller := std.PrevRealm().Addr() + if !s.isApprovedOrOwner(caller, tid) { + return ErrCallerIsNotOwnerOrApproved + } + + err := s.transfer(from, to, tid) + if err != nil { + return err + } + + return nil +} + +// Mints `tokenId` and transfers it to `to`. +func (s *basicNFT) Mint(to std.Address, tid TokenID) error { + return s.mint(to, tid) +} + +// Mints `tokenId` and transfers it to `to`. Also checks that +// contract recipients are using GRC721 protocol +func (s *basicNFT) SafeMint(to std.Address, tid TokenID) error { + err := s.mint(to, tid) + if err != nil { + return err + } + + if !s.checkOnGRC721Received(zeroAddress, to, tid) { + return ErrTransferToNonGRC721Receiver + } + + return nil +} + +func (s *basicNFT) Burn(tid TokenID) error { + owner, err := s.OwnerOf(tid) + if err != nil { + return err + } + + s.beforeTokenTransfer(owner, zeroAddress, tid, 1) + + s.tokenApprovals.Remove(string(tid)) + balance, err := s.BalanceOf(owner) + if err != nil { + return err + } + balance -= 1 + s.balances.Set(owner.String(), balance) + s.owners.Remove(string(tid)) + + event := TransferEvent{owner, zeroAddress, tid} + emit(&event) + + s.afterTokenTransfer(owner, zeroAddress, tid, 1) + + return nil +} + +/* Helper methods */ + +// Helper for SetApprovalForAll() +func (s *basicNFT) setApprovalForAll(owner, operator std.Address, approved bool) error { + if owner == operator { + return ErrApprovalToCurrentOwner + } + + key := owner.String() + ":" + operator.String() + s.operatorApprovals.Set(key, approved) + + event := ApprovalForAllEvent{owner, operator, approved} + emit(&event) + + return nil +} + +// Helper for TransferFrom() and SafeTransferFrom() +func (s *basicNFT) transfer(from, to std.Address, tid TokenID) error { + if err := isValidAddress(from); err != nil { + return ErrInvalidAddress + } + if err := isValidAddress(to); err != nil { + return ErrInvalidAddress + } + + if from == to { + return ErrCannotTransferToSelf + } + + owner, err := s.OwnerOf(tid) + if err != nil { + return err + } + if owner != from { + return ErrTransferFromIncorrectOwner + } + + s.beforeTokenTransfer(from, to, tid, 1) + + // Check that tokenId was not transferred by `beforeTokenTransfer` + owner, err = s.OwnerOf(tid) + if err != nil { + return err + } + if owner != from { + return ErrTransferFromIncorrectOwner + } + + s.tokenApprovals.Remove(string(tid)) + fromBalance, err := s.BalanceOf(from) + if err != nil { + return err + } + toBalance, err := s.BalanceOf(to) + if err != nil { + return err + } + fromBalance -= 1 + toBalance += 1 + s.balances.Set(from.String(), fromBalance) + s.balances.Set(to.String(), toBalance) + s.owners.Set(string(tid), to) + + event := TransferEvent{from, to, tid} + emit(&event) + + s.afterTokenTransfer(from, to, tid, 1) + + return nil +} + +// Helper for Mint() and SafeMint() +func (s *basicNFT) mint(to std.Address, tid TokenID) error { + if err := isValidAddress(to); err != nil { + return err + } + + if s.exists(tid) { + return ErrTokenIdAlreadyExists + } + + s.beforeTokenTransfer(zeroAddress, to, tid, 1) + + // Check that tokenId was not minted by `beforeTokenTransfer` + if s.exists(tid) { + return ErrTokenIdAlreadyExists + } + + toBalance, err := s.BalanceOf(to) + if err != nil { + return err + } + toBalance += 1 + s.balances.Set(to.String(), toBalance) + s.owners.Set(string(tid), to) + + event := TransferEvent{zeroAddress, to, tid} + emit(&event) + + s.afterTokenTransfer(zeroAddress, to, tid, 1) + + return nil +} + +func (s *basicNFT) isApprovedOrOwner(addr std.Address, tid TokenID) bool { + owner, found := s.owners.Get(string(tid)) + if !found { + return false + } + + if addr == owner.(std.Address) || s.IsApprovedForAll(owner.(std.Address), addr) { + return true + } + + _, err := s.GetApproved(tid) + if err != nil { + return false + } + + return true +} + +// Checks if token id already exists +func (s *basicNFT) exists(tid TokenID) bool { + _, found := s.owners.Get(string(tid)) + return found +} + +func (s *basicNFT) beforeTokenTransfer(from, to std.Address, firstTokenId TokenID, batchSize uint64) { + // TODO: Implementation +} + +func (s *basicNFT) afterTokenTransfer(from, to std.Address, firstTokenId TokenID, batchSize uint64) { + // TODO: Implementation +} + +func (s *basicNFT) checkOnGRC721Received(from, to std.Address, tid TokenID) bool { + // TODO: Implementation + return true +} + +func (s *basicNFT) RenderHome() (str string) { + str += ufmt.Sprintf("# %s ($%s)\n\n", s.name, s.symbol) + str += ufmt.Sprintf("* **Total supply**: %d\n", s.TokenCount()) + str += ufmt.Sprintf("* **Known accounts**: %d\n", s.balances.Size()) + + return +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/basic_nft_test.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/basic_nft_test.gno new file mode 100644 index 00000000..6375b030 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/basic_nft_test.gno @@ -0,0 +1,283 @@ +package grc721 + +import ( + "std" + "testing" + + "gno.land/p/demo/uassert" +) + +var ( + dummyNFTName = "DummyNFT" + dummyNFTSymbol = "DNFT" +) + +func TestNewBasicNFT(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") +} + +func TestName(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + name := dummy.Name() + uassert.Equal(t, dummyNFTName, name) +} + +func TestSymbol(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + symbol := dummy.Symbol() + uassert.Equal(t, dummyNFTSymbol, symbol) +} + +func TestTokenCount(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + count := dummy.TokenCount() + uassert.Equal(t, uint64(0), count) + + dummy.mint("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm", TokenID("1")) + dummy.mint("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm", TokenID("2")) + + count = dummy.TokenCount() + uassert.Equal(t, uint64(2), count) +} + +func TestBalanceOf(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + addr1 := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + addr2 := std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") + + balanceAddr1, err := dummy.BalanceOf(addr1) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, uint64(0), balanceAddr1) + + dummy.mint(addr1, TokenID("1")) + dummy.mint(addr1, TokenID("2")) + dummy.mint(addr2, TokenID("3")) + + balanceAddr1, err = dummy.BalanceOf(addr1) + uassert.NoError(t, err, "should not result in error") + + balanceAddr2, err := dummy.BalanceOf(addr2) + uassert.NoError(t, err, "should not result in error") + + uassert.Equal(t, uint64(2), balanceAddr1) + uassert.Equal(t, uint64(1), balanceAddr2) +} + +func TestOwnerOf(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + addr1 := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + addr2 := std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") + + owner, err := dummy.OwnerOf(TokenID("invalid")) + uassert.Error(t, err, "should not result in error") + + dummy.mint(addr1, TokenID("1")) + dummy.mint(addr2, TokenID("2")) + + // Checking for token id "1" + owner, err = dummy.OwnerOf(TokenID("1")) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, addr1.String(), owner.String()) + + // Checking for token id "2" + owner, err = dummy.OwnerOf(TokenID("2")) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, addr2.String(), owner.String()) +} + +func TestIsApprovedForAll(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + addr1 := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + addr2 := std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") + + isApprovedForAll := dummy.IsApprovedForAll(addr1, addr2) + uassert.False(t, isApprovedForAll) +} + +func TestSetApprovalForAll(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + caller := std.PrevRealm().Addr() + addr := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + + isApprovedForAll := dummy.IsApprovedForAll(caller, addr) + uassert.False(t, isApprovedForAll) + + err := dummy.SetApprovalForAll(addr, true) + uassert.NoError(t, err, "should not result in error") + + isApprovedForAll = dummy.IsApprovedForAll(caller, addr) + uassert.True(t, isApprovedForAll) +} + +func TestGetApproved(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + approvedAddr, err := dummy.GetApproved(TokenID("invalid")) + uassert.Error(t, err, "should result in error") +} + +func TestApprove(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + caller := std.PrevRealm().Addr() + addr := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + + dummy.mint(caller, TokenID("1")) + + _, err := dummy.GetApproved(TokenID("1")) + uassert.Error(t, err, "should result in error") + + err = dummy.Approve(addr, TokenID("1")) + uassert.NoError(t, err, "should not result in error") + + approvedAddr, err := dummy.GetApproved(TokenID("1")) + uassert.NoError(t, err, "should result in error") + uassert.Equal(t, addr.String(), approvedAddr.String()) +} + +func TestTransferFrom(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + caller := std.PrevRealm().Addr() + addr := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + + dummy.mint(caller, TokenID("1")) + dummy.mint(caller, TokenID("2")) + + err := dummy.TransferFrom(caller, addr, TokenID("1")) + uassert.NoError(t, err, "should result in error") + + // Check balance of caller after transfer + balanceOfCaller, err := dummy.BalanceOf(caller) + uassert.NoError(t, err, "should result in error") + uassert.Equal(t, uint64(1), balanceOfCaller) + + // Check balance of addr after transfer + balanceOfAddr, err := dummy.BalanceOf(addr) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, uint64(1), balanceOfAddr) + + // Check Owner of transferred Token id + owner, err := dummy.OwnerOf(TokenID("1")) + uassert.NoError(t, err, "should result in error") + uassert.Equal(t, addr.String(), owner.String()) +} + +func TestSafeTransferFrom(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + caller := std.PrevRealm().Addr() + addr := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + + dummy.mint(caller, TokenID("1")) + dummy.mint(caller, TokenID("2")) + + err := dummy.SafeTransferFrom(caller, addr, TokenID("1")) + uassert.NoError(t, err, "should not result in error") + + // Check balance of caller after transfer + balanceOfCaller, err := dummy.BalanceOf(caller) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, uint64(1), balanceOfCaller) + + // Check balance of addr after transfer + balanceOfAddr, err := dummy.BalanceOf(addr) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, uint64(1), balanceOfAddr) + + // Check Owner of transferred Token id + owner, err := dummy.OwnerOf(TokenID("1")) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, addr.String(), owner.String()) +} + +func TestMint(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + addr1 := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + addr2 := std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") + + err := dummy.Mint(addr1, TokenID("1")) + uassert.NoError(t, err, "should not result in error") + err = dummy.Mint(addr1, TokenID("2")) + uassert.NoError(t, err, "should not result in error") + err = dummy.Mint(addr2, TokenID("3")) + uassert.NoError(t, err, "should not result in error") + + // Try minting duplicate token id + err = dummy.Mint(addr2, TokenID("1")) + uassert.Error(t, err, "should not result in error") + + // Check Owner of Token id + owner, err := dummy.OwnerOf(TokenID("1")) + uassert.NoError(t, err, "should not result in error") + uassert.Equal(t, addr1.String(), owner.String()) +} + +func TestBurn(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + addr := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + + dummy.mint(addr, TokenID("1")) + dummy.mint(addr, TokenID("2")) + + err := dummy.Burn(TokenID("1")) + uassert.NoError(t, err, "should not result in error") + + // Check Owner of Token id + owner, err := dummy.OwnerOf(TokenID("1")) + uassert.Error(t, err, "should result in error") +} + +func TestSetTokenURI(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + addr1 := std.Address("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + addr2 := std.Address("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") + tokenURI := "http://example.com/token" + + std.TestSetOrigCaller(std.Address(addr1)) // addr1 + + dummy.mint(addr1, TokenID("1")) + _, derr := dummy.SetTokenURI(TokenID("1"), TokenURI(tokenURI)) + uassert.NoError(t, derr, "should not result in error") + + // Test case: Invalid token ID + _, err := dummy.SetTokenURI(TokenID("3"), TokenURI(tokenURI)) + uassert.ErrorIs(t, err, ErrInvalidTokenId) + + std.TestSetOrigCaller(std.Address(addr2)) // addr2 + + _, cerr := dummy.SetTokenURI(TokenID("1"), TokenURI(tokenURI)) // addr2 trying to set URI for token 1 + uassert.ErrorIs(t, cerr, ErrCallerIsNotOwner) + + // Test case: Retrieving TokenURI + std.TestSetOrigCaller(std.Address(addr1)) // addr1 + + dummyTokenURI, err := dummy.TokenURI(TokenID("1")) + uassert.NoError(t, err, "TokenURI error") + uassert.Equal(t, string(tokenURI), string(dummyTokenURI)) +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/errors.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/errors.gno new file mode 100644 index 00000000..2d512db3 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/errors.gno @@ -0,0 +1,22 @@ +package grc721 + +import "errors" + +var ( + ErrInvalidTokenId = errors.New("invalid token id") + ErrInvalidAddress = errors.New("invalid address") + ErrTokenIdNotHasApproved = errors.New("token id not approved for anyone") + ErrApprovalToCurrentOwner = errors.New("approval to current owner") + ErrCallerIsNotOwner = errors.New("caller is not token owner") + ErrCallerNotApprovedForAll = errors.New("caller is not approved for all") + ErrCannotTransferToSelf = errors.New("cannot send transfer to self") + ErrTransferFromIncorrectOwner = errors.New("transfer from incorrect owner") + ErrTransferToNonGRC721Receiver = errors.New("transfer to non GRC721Receiver implementer") + ErrCallerIsNotOwnerOrApproved = errors.New("caller is not token owner or approved") + ErrTokenIdAlreadyExists = errors.New("token id already exists") + + // ERC721Royalty + ErrInvalidRoyaltyPercentage = errors.New("invalid royalty percentage") + ErrInvalidRoyaltyPaymentAddress = errors.New("invalid royalty paymentAddress") + ErrCannotCalculateRoyaltyAmount = errors.New("cannot calculate royalty amount") +) diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_metadata.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_metadata.gno new file mode 100644 index 00000000..360f73ed --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_metadata.gno @@ -0,0 +1,95 @@ +package grc721 + +import ( + "std" + + "gno.land/p/demo/avl" +) + +// metadataNFT represents an NFT with metadata extensions. +type metadataNFT struct { + *basicNFT // Embedded basicNFT struct for basic NFT functionality + extensions *avl.Tree // AVL tree for storing metadata extensions +} + +// Ensure that metadataNFT implements the IGRC721MetadataOnchain interface. +var _ IGRC721MetadataOnchain = (*metadataNFT)(nil) + +// NewNFTWithMetadata creates a new basic NFT with metadata extensions. +func NewNFTWithMetadata(name string, symbol string) *metadataNFT { + // Create a new basic NFT + nft := NewBasicNFT(name, symbol) + + // Return a metadataNFT with basicNFT embedded and an empty AVL tree for extensions + return &metadataNFT{ + basicNFT: nft, + extensions: avl.NewTree(), + } +} + +// SetTokenMetadata sets metadata for a given token ID. +func (s *metadataNFT) SetTokenMetadata(tid TokenID, metadata Metadata) error { + // Check if the caller is the owner of the token + owner, err := s.basicNFT.OwnerOf(tid) + if err != nil { + return err + } + caller := std.PrevRealm().Addr() + if caller != owner { + return ErrCallerIsNotOwner + } + + // Set the metadata for the token ID in the extensions AVL tree + s.extensions.Set(string(tid), metadata) + return nil +} + +// TokenMetadata retrieves metadata for a given token ID. +func (s *metadataNFT) TokenMetadata(tid TokenID) (Metadata, error) { + // Retrieve metadata from the extensions AVL tree + metadata, found := s.extensions.Get(string(tid)) + if !found { + return Metadata{}, ErrInvalidTokenId + } + + return metadata.(Metadata), nil +} + +// mint mints a new token and assigns it to the specified address. +func (s *metadataNFT) mint(to std.Address, tid TokenID) error { + // Check if the address is valid + if err := isValidAddress(to); err != nil { + return err + } + + // Check if the token ID already exists + if s.basicNFT.exists(tid) { + return ErrTokenIdAlreadyExists + } + + s.basicNFT.beforeTokenTransfer(zeroAddress, to, tid, 1) + + // Check if the token ID was minted by beforeTokenTransfer + if s.basicNFT.exists(tid) { + return ErrTokenIdAlreadyExists + } + + // Increment balance of the recipient address + toBalance, err := s.basicNFT.BalanceOf(to) + if err != nil { + return err + } + toBalance += 1 + s.basicNFT.balances.Set(to.String(), toBalance) + + // Set owner of the token ID to the recipient address + s.basicNFT.owners.Set(string(tid), to) + + // Emit transfer event + event := TransferEvent{zeroAddress, to, tid} + emit(&event) + + s.basicNFT.afterTokenTransfer(zeroAddress, to, tid, 1) + + return nil +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_metadata_test.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_metadata_test.gno new file mode 100644 index 00000000..ad002a7c --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_metadata_test.gno @@ -0,0 +1,107 @@ +package grc721 + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" +) + +func TestSetMetadata(t *testing.T) { + // Create a new dummy NFT with metadata + dummy := NewNFTWithMetadata(dummyNFTName, dummyNFTSymbol) + if dummy == nil { + t.Errorf("should not be nil") + } + + // Define addresses for testing purposes + addr1 := testutils.TestAddress("alice") + addr2 := testutils.TestAddress("bob") + + // Define metadata attributes + name := "test" + description := "test" + image := "test" + imageData := "test" + externalURL := "test" + attributes := []Trait{} + backgroundColor := "test" + animationURL := "test" + youtubeURL := "test" + + // Set the original caller to addr1 + std.TestSetOrigCaller(addr1) // addr1 + + // Mint a new token for addr1 + dummy.mint(addr1, TokenID("1")) + + // Set metadata for token 1 + derr := dummy.SetTokenMetadata(TokenID("1"), Metadata{ + Name: name, + Description: description, + Image: image, + ImageData: imageData, + ExternalURL: externalURL, + Attributes: attributes, + BackgroundColor: backgroundColor, + AnimationURL: animationURL, + YoutubeURL: youtubeURL, + }) + + // Check if there was an error setting metadata + uassert.NoError(t, derr, "Should not result in error") + + // Test case: Invalid token ID + err := dummy.SetTokenMetadata(TokenID("3"), Metadata{ + Name: name, + Description: description, + Image: image, + ImageData: imageData, + ExternalURL: externalURL, + Attributes: attributes, + BackgroundColor: backgroundColor, + AnimationURL: animationURL, + YoutubeURL: youtubeURL, + }) + + // Check if the error returned matches the expected error + uassert.ErrorIs(t, err, ErrInvalidTokenId) + + // Set the original caller to addr2 + std.TestSetOrigCaller(addr2) // addr2 + + // Try to set metadata for token 1 from addr2 (should fail) + cerr := dummy.SetTokenMetadata(TokenID("1"), Metadata{ + Name: name, + Description: description, + Image: image, + ImageData: imageData, + ExternalURL: externalURL, + Attributes: attributes, + BackgroundColor: backgroundColor, + AnimationURL: animationURL, + YoutubeURL: youtubeURL, + }) + + // Check if the error returned matches the expected error + uassert.ErrorIs(t, cerr, ErrCallerIsNotOwner) + + // Set the original caller back to addr1 + std.TestSetOrigCaller(addr1) // addr1 + + // Retrieve metadata for token 1 + dummyMetadata, err := dummy.TokenMetadata(TokenID("1")) + uassert.NoError(t, err, "Metadata error") + + // Check if metadata attributes match expected values + uassert.Equal(t, image, dummyMetadata.Image) + uassert.Equal(t, imageData, dummyMetadata.ImageData) + uassert.Equal(t, externalURL, dummyMetadata.ExternalURL) + uassert.Equal(t, description, dummyMetadata.Description) + uassert.Equal(t, name, dummyMetadata.Name) + uassert.Equal(t, len(attributes), len(dummyMetadata.Attributes)) + uassert.Equal(t, backgroundColor, dummyMetadata.BackgroundColor) + uassert.Equal(t, animationURL, dummyMetadata.AnimationURL) + uassert.Equal(t, youtubeURL, dummyMetadata.YoutubeURL) +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_royalty.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_royalty.gno new file mode 100644 index 00000000..9831c709 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_royalty.gno @@ -0,0 +1,78 @@ +package grc721 + +import ( + "std" + + "gno.land/p/demo/avl" +) + +// royaltyNFT represents a non-fungible token (NFT) with royalty functionality. +type royaltyNFT struct { + *metadataNFT // Embedding metadataNFT for NFT functionality + tokenRoyaltyInfo *avl.Tree // AVL tree to store royalty information for each token + maxRoyaltyPercentage uint64 // maxRoyaltyPercentage represents the maximum royalty percentage that can be charged every sale +} + +// Ensure that royaltyNFT implements the IGRC2981 interface. +var _ IGRC2981 = (*royaltyNFT)(nil) + +// NewNFTWithRoyalty creates a new royalty NFT with the specified name, symbol, and royalty calculator. +func NewNFTWithRoyalty(name string, symbol string) *royaltyNFT { + // Create a new NFT with metadata + nft := NewNFTWithMetadata(name, symbol) + + return &royaltyNFT{ + metadataNFT: nft, + tokenRoyaltyInfo: avl.NewTree(), + maxRoyaltyPercentage: 100, + } +} + +// SetTokenRoyalty sets the royalty information for a specific token ID. +func (r *royaltyNFT) SetTokenRoyalty(tid TokenID, royaltyInfo RoyaltyInfo) error { + // Validate the payment address + if err := isValidAddress(royaltyInfo.PaymentAddress); err != nil { + return ErrInvalidRoyaltyPaymentAddress + } + + // Check if royalty percentage exceeds maxRoyaltyPercentage + if royaltyInfo.Percentage > r.maxRoyaltyPercentage { + return ErrInvalidRoyaltyPercentage + } + + // Check if the caller is the owner of the token + owner, err := r.metadataNFT.OwnerOf(tid) + if err != nil { + return err + } + caller := std.PrevRealm().Addr() + if caller != owner { + return ErrCallerIsNotOwner + } + + // Set royalty information for the token + r.tokenRoyaltyInfo.Set(string(tid), royaltyInfo) + + return nil +} + +// RoyaltyInfo returns the royalty information for the given token ID and sale price. +func (r *royaltyNFT) RoyaltyInfo(tid TokenID, salePrice uint64) (std.Address, uint64, error) { + // Retrieve royalty information for the token + val, found := r.tokenRoyaltyInfo.Get(string(tid)) + if !found { + return "", 0, ErrInvalidTokenId + } + + royaltyInfo := val.(RoyaltyInfo) + + // Calculate royalty amount + royaltyAmount, _ := r.calculateRoyaltyAmount(salePrice, royaltyInfo.Percentage) + + return royaltyInfo.PaymentAddress, royaltyAmount, nil +} + +func (r *royaltyNFT) calculateRoyaltyAmount(salePrice, percentage uint64) (uint64, error) { + royaltyAmount := (salePrice * percentage) / 100 + return royaltyAmount, nil +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_royalty_test.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_royalty_test.gno new file mode 100644 index 00000000..7893453a --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/grc721_royalty_test.gno @@ -0,0 +1,70 @@ +package grc721 + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" +) + +func TestSetTokenRoyalty(t *testing.T) { + dummy := NewNFTWithRoyalty(dummyNFTName, dummyNFTSymbol) + uassert.True(t, dummy != nil, "should not be nil") + + addr1 := testutils.TestAddress("alice") + addr2 := testutils.TestAddress("bob") + + paymentAddress := testutils.TestAddress("john") + percentage := uint64(10) // 10% + + salePrice := uint64(1000) + expectRoyaltyAmount := uint64(100) + + std.TestSetOrigCaller(addr1) // addr1 + + dummy.mint(addr1, TokenID("1")) + + derr := dummy.SetTokenRoyalty(TokenID("1"), RoyaltyInfo{ + PaymentAddress: paymentAddress, + Percentage: percentage, + }) + uassert.NoError(t, derr, "Should not result in error") + + // Test case: Invalid token ID + err := dummy.SetTokenRoyalty(TokenID("3"), RoyaltyInfo{ + PaymentAddress: paymentAddress, + Percentage: percentage, + }) + uassert.ErrorIs(t, derr, ErrInvalidTokenId) + + std.TestSetOrigCaller(addr2) // addr2 + + cerr := dummy.SetTokenRoyalty(TokenID("1"), RoyaltyInfo{ + PaymentAddress: paymentAddress, + Percentage: percentage, + }) + uassert.ErrorIs(t, cerr, ErrCallerIsNotOwner) + + // Test case: Invalid payment address + aerr := dummy.SetTokenRoyalty(TokenID("4"), RoyaltyInfo{ + PaymentAddress: std.Address("###"), // invalid address + Percentage: percentage, + }) + uassert.ErrorIs(t, aerr, ErrInvalidRoyaltyPaymentAddress) + + // Test case: Invalid percentage + perr := dummy.SetTokenRoyalty(TokenID("5"), RoyaltyInfo{ + PaymentAddress: paymentAddress, + Percentage: uint64(200), // over maxRoyaltyPercentage + }) + uassert.ErrorIs(t, perr, ErrInvalidRoyaltyPercentage) + + // Test case: Retrieving Royalty Info + std.TestSetOrigCaller(addr1) // addr1 + + dummyPaymentAddress, dummyRoyaltyAmount, rerr := dummy.RoyaltyInfo(TokenID("1"), salePrice) + uassert.NoError(t, rerr, "RoyaltyInfo error") + uassert.Equal(t, paymentAddress, dummyPaymentAddress) + uassert.Equal(t, expectRoyaltyAmount, dummyRoyaltyAmount) +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721.gno new file mode 100644 index 00000000..ece48f8e --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721.gno @@ -0,0 +1,39 @@ +package grc721 + +import "std" + +type IGRC721 interface { + BalanceOf(owner std.Address) (uint64, error) + OwnerOf(tid TokenID) (std.Address, error) + SetTokenURI(tid TokenID, tURI TokenURI) (bool, error) + SafeTransferFrom(from, to std.Address, tid TokenID) error + TransferFrom(from, to std.Address, tid TokenID) error + Approve(approved std.Address, tid TokenID) error + SetApprovalForAll(operator std.Address, approved bool) error + GetApproved(tid TokenID) (std.Address, error) + IsApprovedForAll(owner, operator std.Address) bool + Mint(to std.Address, tid TokenID) error +} + +type ( + TokenID string + TokenURI string +) + +type TransferEvent struct { + From std.Address + To std.Address + TokenID TokenID +} + +type ApprovalEvent struct { + Owner std.Address + Approved std.Address + TokenID TokenID +} + +type ApprovalForAllEvent struct { + Owner std.Address + Operator std.Address + Approved bool +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721_metadata.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721_metadata.gno new file mode 100644 index 00000000..8a2be1ff --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721_metadata.gno @@ -0,0 +1,38 @@ +package grc721 + +// IGRC721CollectionMetadata describes basic information about an NFT collection. +type IGRC721CollectionMetadata interface { + Name() string // Name returns the name of the collection. + Symbol() string // Symbol returns the symbol of the collection. +} + +// IGRC721Metadata follows the Ethereum standard +type IGRC721Metadata interface { + IGRC721CollectionMetadata + TokenURI(tid TokenID) (string, error) // TokenURI returns the URI of a specific token. +} + +// IGRC721Metadata follows the OpenSea metadata standard +type IGRC721MetadataOnchain interface { + IGRC721CollectionMetadata + TokenMetadata(tid TokenID) (Metadata, error) +} + +type Trait struct { + DisplayType string + TraitType string + Value string +} + +// see: https://docs.opensea.io/docs/metadata-standards +type Metadata struct { + Image string // URL to the image of the item. Can be any type of image (including SVGs, which will be cached into PNGs by OpenSea), IPFS or Arweave URLs or paths. We recommend using a minimum 3000 x 3000 image. + ImageData string // Raw SVG image data, if you want to generate images on the fly (not recommended). Only use this if you're not including the image parameter. + ExternalURL string // URL that will appear below the asset's image on OpenSea and will allow users to leave OpenSea and view the item on your site. + Description string // Human-readable description of the item. Markdown is supported. + Name string // Name of the item. + Attributes []Trait // Attributes for the item, which will show up on the OpenSea page for the item. + BackgroundColor string // Background color of the item on OpenSea. Must be a six-character hexadecimal without a pre-pended # + AnimationURL string // URL to a multimedia attachment for the item. Supported file extensions: GLTF, GLB, WEBM, MP4, M4V, OGV, OGG, MP3, WAV, OGA, HTML (for rich experiences and interactive NFTs using JavaScript canvas, WebGL, etc.). Scripts and relative paths within the HTML page are now supported. Access to browser extensions is not supported. + YoutubeURL string // URL to a YouTube video (only used if animation_url is not provided). +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721_royalty.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721_royalty.gno new file mode 100644 index 00000000..c4603d63 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/igrc721_royalty.gno @@ -0,0 +1,16 @@ +package grc721 + +import "std" + +// IGRC2981 follows the Ethereum standard +type IGRC2981 interface { + // RoyaltyInfo retrieves royalty information for a tokenID and salePrice. + // It returns the payment address, royalty amount, and an error if any. + RoyaltyInfo(tokenID TokenID, salePrice uint64) (std.Address, uint64, error) +} + +// RoyaltyInfo represents royalty information for a token. +type RoyaltyInfo struct { + PaymentAddress std.Address // PaymentAddress is the address where royalty payment should be sent. + Percentage uint64 // Percentage is the royalty percentage. It indicates the percentage of royalty to be paid for each sale. For example : Percentage = 10 => 10% +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/pkg_metadata.json b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/pkg_metadata.json new file mode 100644 index 00000000..f7c3b6e7 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/pkg_metadata.json @@ -0,0 +1 @@ +{"creator":"g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9","deposit":""} \ No newline at end of file diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/util.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/util.gno new file mode 100644 index 00000000..bb6bf24d --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/grc/grc721:2465164/util.gno @@ -0,0 +1,18 @@ +package grc721 + +import ( + "std" +) + +var zeroAddress = std.Address("") + +func isValidAddress(addr std.Address) error { + if !addr.IsValid() { + return ErrInvalidAddress + } + return nil +} + +func emit(event interface{}) { + // TODO: setup a pubsub system here? +} diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/sbid:2465191/pkg_metadata.json b/test4.gno.land/extracted/p/varmeta/demo/v6/sbid:2465191/pkg_metadata.json new file mode 100644 index 00000000..f7c3b6e7 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/sbid:2465191/pkg_metadata.json @@ -0,0 +1 @@ +{"creator":"g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9","deposit":""} \ No newline at end of file diff --git a/test4.gno.land/extracted/p/varmeta/demo/v6/sbid:2465191/sbid.gno b/test4.gno.land/extracted/p/varmeta/demo/v6/sbid:2465191/sbid.gno new file mode 100644 index 00000000..ad10c1a7 --- /dev/null +++ b/test4.gno.land/extracted/p/varmeta/demo/v6/sbid:2465191/sbid.gno @@ -0,0 +1,277 @@ +package sbid + +import ( + "crypto/sha256" + "encoding/hex" + "strconv" + "time" + + "gno.land/p/demo/avl" +) + +type Bid struct { + Infos *avl.Tree // caller <- *bidInfo because the *Bid point to the current session, so list of key is list of callers + EndHashTime int64 + EndPriceTime int64 + StartTime int64 +} + +type bidInfo struct { + BidObject string + Bidder string + HashString string + Price int64 + IsCommittedHash bool + IsCommittedPrice bool + IsWinner bool +} + +type BidStatus struct { + BidObject string + Status string + EndHashTime int64 + EndPriceTime int64 + ActiveUsers int64 +} + +/* +the flow should be: +this package manages list of bidding session. Store by key: domain name and value is: bidSessionInfo +this structure (bidSesionInfo) includes 2 *avl.Tree. +1 - key: bidID mapping to value is: that ID bid info +2 - key: address (caller string) mapping to value is: ID list +-> if you want to find a joined list, just take value from 2nd tree, take all value with these keys in the first tree :D +lets say: + + you have rootTree: dname <- bidSessionInfo { + IDInfoTree: ID <- bidInfo // update these infos when there is something is committed + CallerID: caller <- []ID // every time you join in new bid, this is updated + } + +if you trigger new bid, check if there is existed bid or not + + 1: -> return the current bid with key dName + 2: -> return the new instance of bid with key dName +*/ + +// SetTime sets the start time, end time for each phase of commit +func (b *Bid) SetTime(startTime, endHashTime, endPriceTime int64) { + b.StartTime = startTime + b.EndHashTime = endHashTime + b.EndPriceTime = endPriceTime +} + +// set new bid record into the root storage -> need function to take the root storage +func (b *Bid) recordNewBid(domainName string) bool { + return true +} + +// GetStatus return status list of all joined session by a caller -> implement on realm +// on this function, just check status of the session that caller joined +func (b *Bid) GetStatus(domainName string, caller string) BidStatus { + // return status + bStt := BidStatus{} + + infoData, ext := b.Infos.Get(caller) + if !ext { + bStt.Status = "free" + return bStt + } + info := infoData.(*bidInfo) + stt := b.getStatusFromInfo(domainName, *info) + bStt.BidObject = domainName + bStt.Status = stt + bStt.EndHashTime = b.EndHashTime + bStt.EndPriceTime = b.EndPriceTime + bStt.ActiveUsers = int64(b.Infos.Size()) + return bStt +} + +// get current status of a bid info in the bidding session that caller joined +func (b *Bid) getStatusFromInfo(object string, info bidInfo) string { + now := time.Now().UnixMilli() + + // Check if the auction is closed then return winner ? + if now > b.EndPriceTime { + if info.IsCommittedPrice { + winner := b.GetCurrentWinnerInfo() + if !winner.IsWinner { + return "no winner yet" + } + return winner.Bidder + " is claiming " + object + } + return "close" + } + + switch { + // Waiting for hash commitment + case !info.IsCommittedHash && !info.IsCommittedPrice && b.StartTime <= now && b.EndHashTime > now: + return "waiting hash" + + // Committed hash + case info.IsCommittedHash && !info.IsCommittedPrice && b.StartTime <= now && b.EndHashTime > now: + return "committed hash" + + // Waiting for price commitment + case info.IsCommittedHash && !info.IsCommittedPrice && b.EndHashTime <= now && b.EndPriceTime > now: + return "waiting price" + + // Committed price + case info.IsCommittedHash && info.IsCommittedPrice && b.EndHashTime <= now && b.EndPriceTime > now: + return "committed price" + + // Winner claiming + case info.IsCommittedHash && info.IsCommittedPrice && !info.IsWinner && b.EndPriceTime <= now: + winner := b.GetCurrentWinnerInfo() + if winner.Bidder == "" { + return "no winner yet" + } + return winner.Bidder + " is claiming " + object + + // Missed hash commitment deadline + case !info.IsCommittedHash && b.EndHashTime <= now: + return "waiting for hash" + + default: + return "undefined" + } +} + +func (b *Bid) GetCurrentWinnerInfo() bidInfo { + winner := bidInfo{} + b.Infos.Iterate("", "", func(key string, value interface{}) bool { + bid := value.(*bidInfo) + if bid.IsWinner { + winner = *bid + return true + } + return false + }) + return winner +} + +// logic for handle new price commit - check winner +func (b *Bid) calculateAndRecordWinner(currentCaller string, newRecord *bidInfo) string { + // need to find the list of caller that refer to a domain. information is included inside the b *Bid + // update logs: not need to get the list anymore, just -> + // iterate over the list and find out the winner + if b.Infos.Size() == 1 { + // update winner now + newRecord.IsWinner = true + b.Infos.Set(currentCaller, newRecord) + return "update first element - winner" + } + // if there is more than 1 joined bid -> set first commit price is winner then check new winner + // before we iterate the list, we need to set a winner? + + // simple solutions: take the list of bidInfo, find the winner, set it. Next time check for IsWinner + infoList := []*bidInfo{} + b.Infos.Iterate("", "", func(key string, value interface{}) bool { + bidInf := value.(*bidInfo) + infoList = append(infoList, bidInf) + return false + }) + // find max of bidInfo.Price + maxIndex := 0 + maxPrice := infoList[0].Price + for index, bif := range infoList { + if bif.Price > maxPrice { + maxPrice = bif.Price + maxIndex = index + } + } + // newRecord is winner _. not need to update old record + if maxPrice < newRecord.Price { + newRecord.IsWinner = true + b.Infos.Set(newRecord.Bidder, newRecord) + loserBid := infoList[maxIndex] + loserBid.IsWinner = false + b.Infos.Set(loserBid.Bidder, loserBid) + } else if maxPrice >= newRecord.Price { + // record new entry and update old winner + newRecord.IsWinner = false + winnerBid := infoList[maxIndex] + winnerBid.IsWinner = true + b.Infos.Set(currentCaller, newRecord) + b.Infos.Set(winnerBid.Bidder, winnerBid) + } + return "update winner for " + currentCaller +} + +// need 2 phases. these functions are for the commit hash string and commit the details +func (b *Bid) CommitHash(domainName string, hashString string, caller string) string { + // TODO: Check for time of each phases? + now := time.Now().UnixMilli() + if now > b.EndHashTime { + return "can not commit hash anymore" + } + // check if caller is committed hash + if b.Infos.Has(caller) { + return "you already committed hash" + } + // new bid + newBidInfo := bidInfo{ + BidObject: domainName, + Bidder: caller, + HashString: hashString, + IsCommittedHash: true, + IsCommittedPrice: false, + IsWinner: false, + } + // set to info storage + b.Infos.Set(caller, &newBidInfo) + // TODO: Charge fee ? + return "committed hash" +} + +// basic logic first +func (b *Bid) CommitPrice(domainName string, price int64, secret string, caller string) string { + if time.Now().UnixMilli() > b.EndPriceTime { + return "can not commit price and secret anymore" + } + if time.Now().UnixMilli() <= b.EndHashTime { + return "price phase is not started" + } + // check in bidInfos list + data, ext := b.Infos.Get(caller) + if !ext { + return "you are not in the info list" + } + info := data.(*bidInfo) + if !info.IsCommittedHash { + return "not committed hash yet" + } + if info.IsCommittedPrice { + return "can not commit price twice" + } + hashCalculated := calculateHS(price, secret) + if hashCalculated != info.HashString { + return "invalid secret key or price" + } + if info.IsCommittedHash && hashCalculated == info.HashString && !info.IsCommittedPrice { + info.IsCommittedPrice = true + info.Price = price + info.IsWinner = false + // setup new record + + b.calculateAndRecordWinner(caller, info) + return "committed price" + } + + return "undefined" +} + +// calculate hash string from secret+price +func calculateHS(price int64, secret string) string { + input := secret + strconv.Itoa(int(price)) + data := []byte(input) + hashed := sha256.Sum256(data) + hashedBytes := hashed[:] + return hex.EncodeToString(hashedBytes) +} + +/* + redegisn the flow with this idea: + this is base package, use realm to really serialize the data and logic checks + +*/ diff --git a/test4.gno.land/extracted/r/g1a0a5gmslvluufduf0xr64tnh9ejswj6jmmg3py/hello:2480742/package.gno b/test4.gno.land/extracted/r/g1a0a5gmslvluufduf0xr64tnh9ejswj6jmmg3py/hello:2480742/package.gno new file mode 100644 index 00000000..d36874bc --- /dev/null +++ b/test4.gno.land/extracted/r/g1a0a5gmslvluufduf0xr64tnh9ejswj6jmmg3py/hello:2480742/package.gno @@ -0,0 +1,5 @@ +package hello + +func Render(path string) string { + return "Hello World!" +} diff --git a/test4.gno.land/extracted/r/g1a0a5gmslvluufduf0xr64tnh9ejswj6jmmg3py/hello:2480742/pkg_metadata.json b/test4.gno.land/extracted/r/g1a0a5gmslvluufduf0xr64tnh9ejswj6jmmg3py/hello:2480742/pkg_metadata.json new file mode 100644 index 00000000..d782a7cc --- /dev/null +++ b/test4.gno.land/extracted/r/g1a0a5gmslvluufduf0xr64tnh9ejswj6jmmg3py/hello:2480742/pkg_metadata.json @@ -0,0 +1 @@ +{"creator":"g1a0a5gmslvluufduf0xr64tnh9ejswj6jmmg3py","deposit":""} \ No newline at end of file diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_model.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_model.gno new file mode 100644 index 00000000..c418eee0 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_model.gno @@ -0,0 +1,35 @@ +package registrar + +import "std" + +type bidStatus struct { + DomainName string + Status string + EndCommitTime int64 + EndPriceTime int64 + ActiveUsers int64 +} + +type bidRecord struct { + DomainName string + Bidder std.Address + HashString string + Price int64 + StartTime int64 + EndCommitTime int64 + EndPriceTime int64 + IsCommittedPrice bool + // CurrentPhase actionCode +} +type bidRecV2 struct { + DomainName string + Bidder string + HashString string + Price int64 + StartTime int64 + EndCommitTime int64 + EndPriceTime int64 + IsCommittedPrice bool + IsCommittedHash bool + IsWinner bool +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_v39.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_v39.gno new file mode 100644 index 00000000..2d923275 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_v39.gno @@ -0,0 +1,382 @@ +package registrar + +// import ( +// "std" +// "strconv" +// "time" + +// "gno.land/p/demo/ufmt" +// // "gno.land/p/demo/mux" +// "gno.land/p/demo/avl" +// ) + +// var ( +// bidStorage *avl.Tree // bidRecord <- []bidRec +// winnerRec *avl.Tree // dName <- bidRec +// joinedBid *avl.Tree // address <- []string DomainName +// ) + +// func recordJoinedBid(domainName string) { +// caller := std.GetOrigCaller() +// dList := []string{} +// data, existed := joinedBid.Get(caller.String()) +// if !existed { +// dList = []string{domainName} +// joinedBid.Set(caller.String(), dList) +// return +// } +// dList = data.([]string) +// dList = append(dList, domainName) +// joinedBid.Set(caller.String(), dList) +// return +// } + +// // GetJoinedBid returns the status of an user's joined bid session information +// func GetJoinedBid(caller string) []bidStatus { +// data, existed := joinedBid.Get(caller) +// if !existed { +// return []bidStatus{} +// } +// list := data.([]string) +// listStatus := []bidStatus{} +// for _, dName := range list { +// stt := GetCurrentStatus(dName, caller) + +// // get the time stamp in [0] element and assign it to status +// data, _ := bidStorage.Get(dName) +// bidRec := data.([]bidRecord)[0] + +// singleStt := bidStatus{ +// DomainName: dName, +// Status: stt, +// EndCommitTime: bidRec.EndCommitTime, +// EndPriceTime: bidRec.EndPriceTime, +// } +// listStatus = append(listStatus, singleStt) +// } +// return listStatus +// } + +// // GetCurrentStatus returns the status of the bidding session about a domain name. This func requires caller to determine in case commited or not (hash or price) +// func GetCurrentStatus(domainName string, caller string) string { +// // if there is record in joinedBid -> user joined +// // check for tine.Now() and startTime +// now := time.Now().UnixMilli() +// owner := GetOwner(domainName) +// if owner != "" { +// return "owned by " + owner.String() +// } +// // find the record +// data, existed := bidStorage.Get(domainName) +// if !existed { +// // no record in bidRec yet -> not commited -> check if user started auction or not - if yes: new auction +// if dataJoined, existedInRegister := joinedBid.Get(caller); existedInRegister { +// dNameList := dataJoined.([]string) +// for _, dName := range dNameList { +// if dName == domainName { +// return "waiting for hash" +// } +// return "domain name is free" +// } +// } +// return "domain name is free" +// } + +// // commited yet +// recList := data.([]bidRecord) +// rec := recList[0] +// endCommitTimeUnix := rec.EndCommitTime +// endPriceTimeUnix := rec.EndPriceTime +// if now < endCommitTimeUnix { +// if rec.HashString != "" { +// return "commited hash" +// } else { +// return "hash" +// } +// } + +// ufmt.Println("now: ", time.UnixMilli(now)) +// ufmt.Println("endPriceTime: ", time.UnixMilli(endPriceTimeUnix)) +// if now < endPriceTimeUnix && now > endCommitTimeUnix { +// // check if commited price +// data, _ := bidStorage.Get(domainName) +// bidRecList := data.([]bidRecord) +// thisRec := bidRecord{} +// for _, bidRec := range bidRecList { +// if bidRec.Bidder.String() == caller { +// thisRec = bidRec +// } +// } +// if thisRec.IsCommittedPrice { +// return "committed price" +// } +// return "price" +// } +// if now > rec.EndPriceTime { +// // check if dName is claiming by winner - check if winner existed +// if data, existed := winnerRec.Get(domainName); existed { +// winner := data.(bidRecord) +// return winner.Bidder.String() + " is claiming domain name: " + domainName +// } +// return "closed" +// } +// return "undefined" +// } + +// // Render() renders welcome message :D +// func Render(path string) string { +// return "welcome to varmeta domain name service" +// } + +// // CommitHash consumes off-chain calculated hash string and the domain name that user want +// func CommitHash(domainName, hashString string) string { +// caller := std.GetOrigCaller() + +// // use now as int64 representation of time.Now() +// now := time.Now().UnixMilli() + +// // update the bid record +// data, existed := bidStorage.Get(domainName) + +// // if not existed -> create new record +// if !existed { +// // for further getStatus +// recordJoinedBid(domainName) + +// var bidRec bidRecord +// endCommitTime := now + defaultCommitHashTime.Milliseconds() +// endPriceTime := endCommitTime + defaultCommitPriceTime.Milliseconds() +// ufmt.Println("[HASH]: request at: ", time.UnixMilli(now)) +// ufmt.Println("[HASH]: endCommitTime: ", time.UnixMilli(endCommitTime)) +// ufmt.Println("[HASH]: endPriceTime: ", time.UnixMilli(endPriceTime)) +// bidRec = bidRecord{ +// DomainName: domainName, +// Bidder: caller, +// HashString: hashString, +// StartTime: now, +// EndCommitTime: endCommitTime, +// EndPriceTime: endPriceTime, +// } +// bidRecList := []bidRecord{bidRec} +// bidStorage.Set(domainName, bidRecList) + +// // charge fee +// chargeFee(fee.BidJoinFee, caller) +// return "new session" +// } +// // if existed +// bidRecList := data.([]bidRecord) +// startTime := bidRecList[0].StartTime +// oldEndCommitTimeUnix := bidRecList[0].EndCommitTime +// oldEndPriceTimeUnix := bidRecList[0].EndPriceTime +// if now > oldEndCommitTimeUnix { +// // now := time.Now().UnixMilli() +// // ufmt.Println("[HASH]: now: ", time.UnixMilli(now)) +// panic("[HASH]: can not commit hash anymore") +// } +// for _, bR := range bidRecList { +// if bR.Bidder == caller { +// panic("[HASH]: you already commited hash") +// } +// } + +// bidRec := bidRecord{ +// DomainName: domainName, +// HashString: hashString, +// Bidder: caller, +// StartTime: startTime, +// EndCommitTime: oldEndCommitTimeUnix, +// EndPriceTime: oldEndPriceTimeUnix, +// } +// bidRecList = append(bidRecList, bidRec) +// // Save record +// bidStorage.Set(domainName, bidRecList) +// // charge commit hash fee +// chargeFee(fee.BidJoinFee, caller) +// return "existed" +// } + +// // for now we dont use panic because this will cause the permanent time.Now() stuck. IDK why // XXX fix me +// // CommitPrice consumes price and secret string that user used in calculating hash string from off-chain. This will re-calculate the hashstring and compare with the one commited before +// func CommitPrice(price int64, secret string, domainName string) string { +// // compute the hash string, compare to saved hash string in record +// // use int64 representation of time.Now() +// now := time.Now().UnixMilli() +// ufmt.Println("[PRICE]: request at: ", time.UnixMilli(now)) +// joinedString := secret + strconv.Itoa(int(price)) +// computedHashString := Get256String(joinedString) +// caller := std.GetOrigCaller() +// data, existed := bidStorage.Get(domainName) +// if !existed { +// panic("[PRICE]: domain name is invalid") +// } +// bidRecList := data.([]bidRecord) +// winnerRec.Set(domainName, bidRecList[len(bidRecList)-1]) +// ufmt.Println("[PRICE] current winner set to: ", bidRecList[len(bidRecList)-1].Bidder.String()) + +// ufmt.Println("[PRICE]: request at: ", time.UnixMilli(now)) +// ufmt.Println("[PRICE]: endPriceTime: ", time.UnixMilli(bidRecList[0].EndPriceTime)) +// // case commit after end - consider panic or not +// if now > bidRecList[0].EndPriceTime { +// ufmt.Println("[PRICE]: commit price phase is ended") +// return "ended" +// } +// // case commit when price phase not started +// if now <= bidRecList[0].EndCommitTime { +// ufmt.Println("[PRICE]: commit price phase is not started yet") +// return "not started yet" +// } + +// // search for the corresponding hash +// for index, bidRec := range bidRecList { +// // panic because wrong price or wrong secret string +// if bidRec.Bidder == caller && bidRec.HashString != computedHashString { +// panic("[PRICE]: invalid hash string") +// } +// // found it, update the winner price +// if bidRec.Bidder == caller && bidRec.HashString == computedHashString { +// data, _ := winnerRec.Get(domainName) +// currentWinnerRec := data.(bidRecord) +// if price > currentWinnerRec.Price && now < currentWinnerRec.EndPriceTime { +// ufmt.Println("[PRICE]: found new winner, setting up") +// currentWinnerRec.Price = price +// currentWinnerRec.Bidder = bidRec.Bidder +// currentWinnerRec.HashString = bidRec.HashString +// currentWinnerRec.StartTime = bidRec.StartTime +// currentWinnerRec.EndCommitTime = bidRec.EndCommitTime +// currentWinnerRec.EndPriceTime = bidRec.EndPriceTime +// winnerRec.Set(domainName, currentWinnerRec) +// ufmt.Println("[PRICE] current winner set to: ", currentWinnerRec.Bidder) +// bidRec.IsCommittedPrice = true +// // set new bidRecord into bidRecList and save into storage +// bidRecList[index] = bidRec +// bidStorage.Set(domainName, bidRecList) +// return "claim" +// } +// } +// } +// // if not match above case, then panic +// panic("commit price failed") +// } + +// // GetCurrentWinner shows the current highest price user +// func GetCurrentWinner(domainName string) bidRecord { +// data, existed := winnerRec.Get(domainName) +// if !existed { +// panic("no winner yet") +// } +// return data.(bidRecord) +// } + +// // find the highest bid in session - incase everyone commited price +// // in old flow, we need to find the winner by retrievaling the bidStorage +// func findTheWinner(domainName string) bidRecord { +// var winnerBid bidRecord +// data, existed := bidStorage.Get(domainName) +// if !existed { +// panic("invalid domain name") +// } +// bidRecList := data.([]bidRecord) +// winnerBid = bidRecList[0] +// for _, bidRec := range bidRecList { +// if bidRec.Price > winnerBid.Price { +// winnerBid = bidRec +// } +// } +// return winnerBid +// } + +// // register the domain for winner +// func registerForWinner(domainName string, winnerRec bidRecord) bool { +// winnerAddr := winnerRec.Bidder +// requestInfo := RequestInfo{ +// WantedDomain: domainName, +// Caller: winnerAddr, +// Mode: "native", +// } +// result := executeRegister(requestInfo) +// if !result.Success { +// panic(result.ResultDetails.Error()) +// } +// // register done. Now charge the fee +// feeProcess(requestInfo) +// return false +// } + +// // everyone can call EndBid() +// // this EndBid checks endTime -> end the auction +// func EndBid(domainName string) error { +// now := time.Now().UnixMilli() +// data, existed := bidStorage.Get(domainName) +// if !existed { +// return ufmt.Errorf("endbid: invalid domain name") +// } +// bidRecList := data.([]bidRecord) +// firstBidRec := bidRecList[0] +// if now < firstBidRec.EndPriceTime { +// return ufmt.Errorf("endbid: this session can not end before the end time") +// } +// // change all state +// // for _, bidRec := range bidRecList { +// // bidRec.IsOpen = false +// // } +// ok := bidStorage.Set(domainName, bidRecList) +// if !ok { +// return ufmt.Errorf("endbid: can not change bid record state") +// } +// // need more conditions for findTheWinner() +// findTheWinner(domainName) +// return nil +// } + +// // get all the price list that joined the bid for displaying in dapp +// func GetRecords(dName string) []bidRecord { +// data, existed := bidStorage.Get(dName) +// if !existed { +// panic("should not") +// } +// return data.([]bidRecord) +// } + +// // chargeFee will charge amount - send from this contract to admin +// func chargeFee(amount int64, from std.Address) { +// bankerContract := std.CurrentRealm().Addr() +// ugnotCoin := std.NewCoin("ugnot", amount) +// coinsToTransfer := std.NewCoins(ugnotCoin) +// coins := checkCoin(from) +// ufmt.Println("check balances: ", coins) +// // ufmt.Println("send from contract ", bankerContract.String(), " to admin ", admin.String(), " amount: ", ugnotCoin) +// bankerUser.SendCoins(bankerContract, admin, coinsToTransfer) +// } + +// // pay fee and claim the domain name if you are winner +// func Claim(domainName string) bool { +// data, existed := winnerRec.Get(domainName) +// if !existed { +// panic("claim: invalid domain name") +// } +// caller := std.GetOrigCaller() +// rec := data.(bidRecord) +// if caller != rec.Bidder { +// panic("only winner can claim") +// } +// requestInfo := RequestInfo{ +// WantedDomain: domainName, +// Caller: rec.Bidder, +// Mode: "native", +// } +// result := executeRegister(requestInfo) +// if !result.Success { +// panic(result.ResultDetails.Error()) +// } +// // register done. Now charge the fee +// chargeFee(rec.Price, caller) +// feeProcess(requestInfo) +// return true +// } + +// // expose winner price - amount of fee that user need to pay in Claim phase +// func GetWinnerPrice(dName string) int64 { +// rec := GetCurrentWinner(dName) +// return rec.Price +// } \ No newline at end of file diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_without_sbid_package.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_without_sbid_package.gno new file mode 100644 index 00000000..cfa7e500 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/bidding_without_sbid_package.gno @@ -0,0 +1,421 @@ +package registrar + +// import ( +// "std" +// "strconv" +// "time" + +// "gno.land/p/demo/ufmt" +// // "gno.land/p/demo/mux" +// "gno.land/p/demo/avl" +// ) + +// // I want to redesign this codebase + +// // all keys are domainName +// var bidStorages avl.Tree // dName <=- []bidRecV2 + +// // record new joined bid +// func record2Storage(domainName string) { +// caller := std.GetOrigCaller() +// now := time.Now().UnixMilli() +// newRec := bidRecV2{ +// Bidder: caller.String(), +// DomainName: domainName, +// StartTime: now, +// //... +// } +// data, existed := bidStorages.Get(domainName) +// if !existed { +// bidStorages.Set(domainName, newRec) +// } +// bidRecList := data.([]bidRecV2) +// oldStartTime := bidRecList[0].StartTime +// newRec.StartTime = oldStartTime +// bidRecList = append(bidRecList, newRec) +// bidStorages.Set(domainName, bidRecList) +// } + +// // GetJoinedBid returns the status of an user's joined bid session information +// func GetJoinedBid(caller string) []bidStatus { +// dNameList := []string{} +// // Iterate the bidStorages, which domainName has Bidder == caller -> take domainNames +// bidStorages.Iterate("", "", func(key string, value interface{}) bool { +// dataList := value.([]bidRecV2) +// for _, rec := range dataList { +// if rec.Bidder == caller { +// ufmt.Println("key to append: ", key) +// dNameList = append(dNameList, key) +// } +// } +// return false +// }) +// ufmt.Println("len: ", len(dNameList)) +// // after get dNameList of a caaller, we check status of each dName +// listStatus := []bidStatus{} +// for _, dName := range dNameList { +// stt := GetCurrentStatus(dName, caller) +// // get the time stamp in [0] element and assign it to status +// data, _ := bidStorages.Get(dName) +// bidRec := data.([]bidRecV2)[0] +// activeCount := getActiveCount(dName) +// singleStt := bidStatus{ +// DomainName: dName, +// Status: stt, +// EndCommitTime: bidRec.EndCommitTime, +// EndPriceTime: bidRec.EndPriceTime, +// ActiveUsers: activeCount, +// } +// listStatus = append(listStatus, singleStt) +// } +// ufmt.Println("len of list status: ", len(listStatus)) +// return listStatus +// } + + +// /* +// I want the CommitHash and CommitPrice will change the status of the record, by this way, we can detect +// the current state of a bidding session for each user +// */ + +// // GetCurrentStatus returns the status of the bidding session about a domain name. This func requires caller to determine in case Committed or not (hash or price) +// func GetCurrentStatus(domainName string, caller string) string { +// // if there is record in joinedBid -> user joined +// // check for tine.Now() and startTime +// now := time.Now().UnixMilli() +// // if domain name is owned already +// owner := GetOwner(domainName) +// if owner != "" { +// return "owned by " + owner.String() +// } +// // find the record +// data, existed := bidStorages.Get(domainName) +// if !existed { +// // no record in bidRec yet -> not Committed -> check if user started auction or not - if yes: new auction +// return "domain name is free" +// } +// recList := data.([]bidRecV2) +// rec0 := recList[0] +// // Auction closed with winner +// if rec0.EndPriceTime <= now && owner != "" { +// return "close" +// } +// // find the record of the caller +// for _, rec := range recList { +// switch { +// // Waiting for hash commitment +// case !rec.IsCommittedHash && !rec.IsCommittedPrice && rec.StartTime <= now && rec.EndCommitTime > now: +// return "waiting hash" + +// // Committed hash +// case rec.IsCommittedHash && !rec.IsCommittedPrice && rec.StartTime <= now && rec.EndCommitTime > now: +// return "committed hash" + +// // Waiting for price commitment +// case rec.IsCommittedHash && !rec.IsCommittedPrice && rec.EndCommitTime <= now && rec.EndPriceTime > now: +// return "waiting price" + +// // Committed price +// case rec.IsCommittedHash && rec.IsCommittedPrice && rec.EndCommitTime <= now && rec.EndPriceTime > now: +// return "committed price" + +// // Winner claiming domain +// case rec.IsCommittedHash && rec.IsCommittedPrice && rec.IsWinner && rec.EndPriceTime <= now: +// return rec.Bidder + " is claiming domain name: " + domainName +// // find the winner +// case rec.IsCommittedHash && rec.IsCommittedPrice && !rec.IsWinner && rec.EndPriceTime <= now: +// _, winnerRec := findTheWinner(domainName) +// return winnerRec.Bidder + " is claiming domain name: " + domainName +// // Missed hash commitment deadline +// case !rec.IsCommittedHash && rec.EndCommitTime <= now: +// return "waiting for hash" +// } +// } + +// return "undefined" +// } + +// // CommitHash consumes off-chain calculated hash string and the domain name that user want +// func CommitHash(domainName, hashString string) string { +// caller := std.GetOrigCaller() + +// // use now as int64 representation of time.Now() +// now := time.Now().UnixMilli() + +// // update the bid record +// data, existed := bidStorages.Get(domainName) + +// // if not existed -> create new record +// if !existed { +// // for further getStatus +// // record2Storage(domainName) + +// var bidRec bidRecV2 +// endCommitTime := now + defaultCommitHashTime.Milliseconds() +// endPriceTime := endCommitTime + defaultCommitPriceTime.Milliseconds() +// ufmt.Println("[HASH]: request at: ", time.UnixMilli(now)) +// ufmt.Println("[HASH]: endCommitTime: ", time.UnixMilli(endCommitTime)) +// ufmt.Println("[HASH]: endPriceTime: ", time.UnixMilli(endPriceTime)) +// bidRec = bidRecV2{ +// DomainName: domainName, +// Bidder: caller.String(), +// HashString: hashString, +// StartTime: now, +// EndCommitTime: endCommitTime, +// EndPriceTime: endPriceTime, +// IsCommittedHash: true, +// IsCommittedPrice: false, +// IsWinner: true, +// Price: 0, +// } +// bidNewRec := []bidRecV2{bidRec} +// bidStorages.Set(domainName, bidNewRec) + +// // charge fee +// chargeFee(fee.BidJoinFee, std.Address(caller)) +// return "new session" +// } +// // if existed +// bidRecList := data.([]bidRecV2) +// startTime := bidRecList[0].StartTime +// oldEndCommitTimeUnix := bidRecList[0].EndCommitTime +// oldEndPriceTimeUnix := bidRecList[0].EndPriceTime +// if now > oldEndCommitTimeUnix { +// // now := time.Now().UnixMilli() +// // ufmt.Println("[HASH]: now: ", time.UnixMilli(now)) +// panic("[HASH]: can not commit hash anymore") +// } +// for _, bR := range bidRecList { +// if bR.Bidder == caller.String() { +// panic("[HASH]: you already committed hash") +// } +// } + +// newRec := bidRecV2{ +// DomainName: domainName, +// HashString: hashString, +// Bidder: caller.String(), +// StartTime: startTime, +// EndCommitTime: oldEndCommitTimeUnix, +// EndPriceTime: oldEndPriceTimeUnix, +// IsCommittedHash: true, +// IsCommittedPrice: false, +// IsWinner: false, +// } +// // append the list, set it to storage +// bidRecList = append(bidRecList, newRec) +// ufmt.Println("len bidRecList: ", len(bidRecList)) +// // Save record +// bidStorages.Set(domainName, bidRecList) +// // charge commit hash fee +// ufmt.Println("saved hash: ", hashString) +// chargeFee(fee.BidJoinFee, caller) +// return "existed" +// } + +// // for now we dont use panic because this will cause the permanent time.Now() stuck. IDK why // XXX fix me +// // CommitPrice consumes price and secret string that user used in calculating hash string from off-chain. This will re-calculate the hashstring and compare with the one Committed before +// func CommitPrice(price int64, secret string, domainName string) string { +// // compute the hash string, compare to saved hash string in record +// // use int64 representation of time.Now() +// now := time.Now().UnixMilli() +// ufmt.Println("[PRICE]: request at: ", time.UnixMilli(now)) +// joinedString := secret + strconv.Itoa(int(price)) +// computedHashString := Get256String(joinedString) +// caller := std.GetOrigCaller() +// data, existed := bidStorages.Get(domainName) +// if !existed { +// panic("[PRICE]: domain name is invalid") +// } +// bidRecList := data.([]bidRecV2) + +// ufmt.Println("[PRICE]: endPriceTime: ", time.UnixMilli(bidRecList[0].EndPriceTime)) +// // case commit after end - consider panic or not +// if now > bidRecList[0].EndPriceTime { +// ufmt.Println("[PRICE]: commit price phase is ended") +// return "ended" +// } +// // case commit when price phase not started +// if now <= bidRecList[0].EndCommitTime { +// ufmt.Println("[PRICE]: commit price phase is not started yet") +// return "not started yet" +// } +// // detect current winner by index and address +// _, currentWinnerRec := findTheWinner(domainName) + +// // search for the corresponding hash +// for index, rec := range bidRecList { +// ufmt.Println("index ", index) +// ufmt.Println("hash: ", rec.HashString) +// if rec.Bidder == caller.String() && rec.IsCommittedPrice { +// ufmt.Println("[PRICE] you already committed price") +// return "committed" +// } +// // panic because wrong price or wrong secret string +// if rec.Bidder == caller.String() && rec.HashString != computedHashString { +// ufmt.Println("[PRICE]: invalid hash string") +// return "invalid hash string" +// } +// // found it, update the price +// ufmt.Println("checking for bid addr: ", rec.Bidder) +// ufmt.Println("hash: ", rec.HashString) +// ufmt.Println("caller: ", caller.String()) +// ufmt.Println("input hash: ", computedHashString) +// if rec.Bidder == caller.String() && rec.HashString == computedHashString { +// // update record with a new list +// // find the winner +// if price > currentWinnerRec.Price && now <= currentWinnerRec.EndPriceTime { +// // set others status to not winner +// data, _ := bidStorages.Get(domainName) +// currentList := data.([]bidRecV2) +// newList := []bidRecV2{} +// for _, rec := range currentList { +// rec.IsWinner = false +// newList = append(newList, rec) +// } + +// // update winner +// ufmt.Println("[PRICE]: found new winner, setting up") +// newRec := bidRecV2{ +// DomainName: domainName, +// Price: price, +// IsCommittedHash: true, +// IsCommittedPrice: true, +// IsWinner: true, +// StartTime: currentWinnerRec.StartTime, +// EndCommitTime: currentWinnerRec.EndCommitTime, +// EndPriceTime: currentWinnerRec.EndPriceTime, +// Bidder: caller.String(), +// HashString: computedHashString, +// } +// // save records +// newList[index] = newRec +// bidStorages.Set(domainName, newList) +// return "claim" +// } +// // if not the winner, update record +// if price <= currentWinnerRec.Price && now <= currentWinnerRec.EndPriceTime { +// updatedRec := bidRecV2{ +// DomainName: domainName, +// Price: price, +// IsCommittedHash: true, +// IsCommittedPrice: true, +// IsWinner: false, +// StartTime: rec.StartTime, +// EndCommitTime: rec.EndCommitTime, +// EndPriceTime: rec.EndPriceTime, +// Bidder: caller.String(), +// HashString: computedHashString, +// } +// data, _ := bidStorages.Get(domainName) +// currentList := data.([]bidRecV2) +// currentList[index] = updatedRec +// bidStorages.Set(domainName, currentList) +// return "loser" +// } +// } +// } +// // if not match above case, then you are not in list +// panic("commit price failed") +// } + +// // GetCurrentWinner shows the current highest price user +// func GetCurrentWinner(domainName string) bidRecV2 { +// data, existed := bidStorages.Get(domainName) +// if !existed { +// panic("no winner yet") +// } +// list := data.([]bidRecV2) +// for _, bidRec := range list { +// if bidRec.IsWinner { +// return bidRec +// } +// } +// // panic - should not happend +// return bidRecV2{} +// } + +// // find the highest bid in session - incase everyone Committed price +// // in old flow, we need to find the winner by retrievaling the bidStorage +// func findTheWinner(domainName string) (index int, winnerBid bidRecV2) { +// data, existed := bidStorages.Get(domainName) +// if !existed { +// panic("invalid domain name") +// } +// index = 0 +// bidRecList := data.([]bidRecV2) +// winnerBid = bidRecList[0] +// for i, bidRec := range bidRecList { +// if bidRec.Price > winnerBid.Price { +// winnerBid.Price = bidRec.Price +// winnerBid.Bidder = bidRec.Bidder +// winnerBid.HashString = bidRec.HashString +// winnerBid.StartTime = bidRec.StartTime +// winnerBid.EndCommitTime = bidRec.EndCommitTime +// winnerBid.EndPriceTime = bidRec.EndPriceTime +// winnerBid.IsCommittedHash = bidRec.IsCommittedHash +// winnerBid.IsCommittedPrice = bidRec.IsCommittedPrice +// index = i +// } +// } +// return index, winnerBid +// } + +// // get all the price list that joined the bid for displaying in dapp +// func GetRecords(dName string) []bidRecV2 { +// data, existed := bidStorages.Get(dName) +// if !existed { +// panic("should not") +// } +// return data.([]bidRecV2) +// } + +// // chargeFee will charge amount - send from this contract to admin +// func chargeFee(amount int64, from std.Address) { +// bankerContract := std.CurrentRealm().Addr() +// ugnotCoin := std.NewCoin("ugnot", amount) +// coinsToTransfer := std.NewCoins(ugnotCoin) +// coins := checkCoin(from) +// ufmt.Println("check balances: ", coins) +// // ufmt.Println("send from contract ", bankerContract.String(), " to admin ", admin.String(), " amount: ", ugnotCoin) +// bankerUser.SendCoins(bankerContract, admin, coinsToTransfer) +// } + +// // pay fee and claim the domain name if you are winner +// func Claim(domainName string) bool { +// if !bidStorages.Has(domainName) { +// panic("claim: invalid domain name") +// } +// caller := std.GetOrigCaller() +// _, winner := findTheWinner(domainName) +// if caller.String() != winner.Bidder { +// panic("only winner can claim") +// } +// requestInfo := RequestInfo{ +// WantedDomain: domainName, +// Caller: caller, +// Mode: "native", +// } +// result := executeRegister(requestInfo) +// if !result.Success { +// panic(result.ResultDetails.Error()) +// } +// // register done. Now charge the fee +// chargeFee(winner.Price, caller) +// feeProcess(requestInfo) +// return true +// } + +// // expose winner price - amount of fee that user need to pay in Claim phase +// func GetWinnerPrice(dName string) int64 { +// rec := GetCurrentWinner(dName) +// baseRegisterFee := GetRegisterFee(dName) +// return rec.Price + baseRegisterFee +// } + +// // get current active bidders +// func getActiveCount(dName string) int64 { +// data, _ := bidStorages.Get(dName) +// recList := data.([]bidRecV2) +// return int64(len(recList)) +// } diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/errors.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/errors.gno new file mode 100644 index 00000000..a186a711 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/errors.gno @@ -0,0 +1,15 @@ +package registrar + +import ( + "errors" +) + +var ( + ErrUnknown = errors.New("unknow errors") + ErrOK = errors.New("ok") + ErrBadCall = errors.New("bad call") + ErrInvalidDomainName = errors.New("ErrInvalidDomainName") + ErrAlreadyRegistered = errors.New("this domain is registered") + ErrCrossRealms = errors.New("cross realms function error") + ErrNotFound = errors.New("domain not found") +) diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee.gno new file mode 100644 index 00000000..9c0eef2f --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee.gno @@ -0,0 +1,36 @@ +package registrar + +import ( + "time" +) + +// only admin can set Fee, other just can read only +type feeInfo struct { + RegisterBaseFee int64 + RenewalFee int64 + RegisterAdditionFee int64 + BidJoinFee int64 +} + +func GetRegisterFee(dName string) int64 { + return fee.RegisterBaseFee +} + +func GetRenewalFee(dName string, amount time.Duration) int64 { + return fee.RenewalFee +} + +// Admin set register fee and renewal fee +func AdminSetFee(regFee int64, renewFee int64) { + // consider logic + assertIsAdmin() + fee.RegisterBaseFee = regFee + fee.RenewalFee = renewFee +} + +// simple err check +func checkErr(err error) { + if err != nil { + panic(err) + } +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_checks.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_checks.gno new file mode 100644 index 00000000..4b054378 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_checks.gno @@ -0,0 +1,9 @@ +package registrar + +// import ( +// "" +// // "std" +// // "time" +// ) + + diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_native.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_native.gno new file mode 100644 index 00000000..aac57354 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_native.gno @@ -0,0 +1,52 @@ +package registrar + +import ( + "std" + + "gno.land/p/demo/ufmt" +) + +// admin access only +func AdminWithdraw(amount int64) { + assertIsAdmin() + thisContract := std.CurrentRealm().Addr() + ugnotCoin := std.NewCoin("ugnot", fee.RegisterBaseFee) + coinsToTransfer := std.NewCoins(ugnotCoin) + superBanker.SendCoins(thisContract, admin, coinsToTransfer) +} + +func nativeProcess() { + bankerContract := std.CurrentRealm().Addr() + ugnotCoin := std.NewCoin("ugnot", fee.RegisterBaseFee) + coinsToTransfer := std.NewCoins(ugnotCoin) + caller := std.GetOrigCaller() + coins := checkCoin(caller) + ufmt.Println("check: ", coins) + ufmt.Println("send from contract ", bankerContract.String(), " to admin ", admin.String(), " amount: ", ugnotCoin) + bankerUser.SendCoins(bankerContract, admin, coinsToTransfer) +} + +// RevertTransfer will revert the transaction - send amount of coin to user +func revertTransfer(userAddr std.Address) { + bankerContract := std.CurrentRealm().Addr() + ugnotCoin := std.NewCoin("ugnot", fee.RegisterBaseFee) + coinsToReturn := std.NewCoins(ugnotCoin) + ufmt.Println("return coins from contract ", bankerContract.String(), " to ", userAddr.String()) + bankerUser.SendCoins(bankerContract, userAddr, coinsToReturn) +} + +// simple check for admin call +func assertIsAdmin() { + // check if GetCallerAt 2 or 3 when deployed + caller := std.GetCallerAt(3) + err := ufmt.Sprintf("unauthorize with caller: %s\n", caller) + if caller != admin && caller != adminVar { + panic(err) + } +} + +// checking for availble coins +func checkCoin(from std.Address) std.Coins { + // caller := std.GetOrigCaller() + return bankerUser.GetCoins(from) +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_token.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_token.gno new file mode 100644 index 00000000..41ef45e8 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/fee_token.gno @@ -0,0 +1,25 @@ +package registrar + +// import ( +// "std" + +// "gno.land/p/demo/ufmt" +// pusers "gno.land/p/demo/users" +// "gno.land/r/varmeta/demo1/domain/vmt" +// ) + +// // expected approved already from client -> transfer from caller to admin +// func tokenProcess(dName string, callerStd std.Address) { +// caller := pusers.AddressOrName(callerStd.String()) + +// now := std.CurrentRealm().Addr() +// nowAddr := pusers.AddressOrName(now.String()) +// ufmt.Println("current realm transfer: ", now.String()) +// callerAllowance := vmt.Allowance(caller, nowAddr) +// callerAllowanceString := ufmt.Sprintf("%d", callerAllowance) +// ufmt.Println("caller allowance ", callerAllowanceString) + +// adminAddr := pusers.AddressOrName(admin.String()) +// ufmt.Println("admin: ", admin.String()) +// vmt.TransferFrom(caller, adminAddr, 1) +// } diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/hashstring.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/hashstring.gno new file mode 100644 index 00000000..674432a1 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/hashstring.gno @@ -0,0 +1,13 @@ +package registrar + +import ( + "crypto/sha256" + "encoding/hex" +) + +func Get256String(input string) string { + data := []byte(input) + hashed := sha256.Sum256(data) + hashedBytes := hashed[:] + return hex.EncodeToString(hashedBytes) +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/metadata_wrapper.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/metadata_wrapper.gno new file mode 100644 index 00000000..0bddb956 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/metadata_wrapper.gno @@ -0,0 +1,40 @@ +package registrar + +import ( + "bytes" + "std" + "time" + + "gno.land/p/demo/ufmt" + + "gno.land/p/varmeta/demo/v6/domain" +) + +// Metadata wrapper +// func NewMetadata(avatar, description, contactInfo, renewalFee string, registrationTime, expirationTime time.Time, attributes []Trait) +func metadataWrapper(owner std.Address, name string, ttl time.Duration) domain.Metadata { + createdAt := time.Now() + expTime := createdAt.Add(ttl) + return domain.NewMetadata("", name, "", "", createdAt, expTime, []domain.Trait{}) +} + +type remapMetadata struct { + Avatar string // avatar - URL or identifier for an avatar image + RegistrationTime string // regtime - The time when the domain was registered + ExpirationTime string // exptime - The time when the domain will be expire + Attributes []domain.Trait // atts - Additional attributes of the domain + Description string // des - A description of the domain + ContactInfo string // contacts - Contact information for the domain owner + RenewalFee string // renewalfee - The fee required to renew the domain, represented as a string +} + +// currently not support for arrays +func (m remapMetadata) MarshalJSON() ([]byte, error) { + json := new(bytes.Buffer) + if m.Attributes == nil { + json.WriteString(ufmt.Sprintf(`{"avatar": %s, "regtime": %s, "exptime": %s, "atts": %s, "des": %s, "contacts": %s, "renewalfee": %s}`, m.Avatar, m.RegistrationTime, m.ExpirationTime, "empty", m.Description, m.ContactInfo, m.RenewalFee)) + return json.Bytes(), nil + } + json.WriteString(ufmt.Sprintf(`{"avatar": %s, "regtime": %s, "exptime": %s, "atts": %s, "des": %s, "contacts": %s, "renewalfee": %s}`, m.Avatar, m.RegistrationTime, m.ExpirationTime, m.Attributes[0], m.Description, m.ContactInfo, m.RenewalFee)) + return json.Bytes(), nil +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/models.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/models.gno new file mode 100644 index 00000000..fe15a6ba --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/models.gno @@ -0,0 +1,22 @@ +package registrar + +import ( + "std" +) + +type RequestInfo struct { + Mode string + WantedDomain string + Caller std.Address + TransInfo TransferInfo + // xxx extendTime, renew... +} +type TransferInfo struct { + From std.Address + To std.Address +} +type ExecuteResult struct { + Success bool + ResultDetails error + Message string +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/pkg_metadata.json b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/pkg_metadata.json new file mode 100644 index 00000000..f7c3b6e7 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/pkg_metadata.json @@ -0,0 +1 @@ +{"creator":"g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9","deposit":""} \ No newline at end of file diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/prestep.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/prestep.gno new file mode 100644 index 00000000..b4ee4c87 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/prestep.gno @@ -0,0 +1,42 @@ +package registrar + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/varmeta/demo/v6/domain" +) + +var ( + domainStorage *avl.Tree // domainName -> std.Address + rootRegistry domain.DomainRegistry + + // fee + superBanker std.Banker // full access to coins that the realm itself owns, including the ones sent with the transaction + bankerUser std.Banker // full access to coins sent with the transaction that called the banker + + admin std.Address // admin + adminVar std.Address // admin in server + fee feeInfo +) + +func init() { + domainStorage = avl.NewTree() + // bidStorage = avl.NewTree() + // winnerRec = avl.NewTree() + // joinedBid = avl.NewTree() + rootRegistry = domain.NewDomainRegistry("Varmeta", "vmt") + + // fee init + admin = "g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9" //@thinhnx + adminVar = "g1p3ylc5w42lrt5345eh7h5l9gcd7qpeyvcl5qjx" //@varmeta-sponsorkey + // ugnot + fee = feeInfo{ + RegisterBaseFee: 100, + RenewalFee: 100, + RegisterAdditionFee: 0, + BidJoinFee: 100, + } + superBanker = std.GetBanker(std.BankerTypeRealmSend) + bankerUser = std.GetBanker(std.BankerTypeOrigSend) +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/register_filetest.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/register_filetest.gno new file mode 100644 index 00000000..90aa28f0 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/register_filetest.gno @@ -0,0 +1,13 @@ +package registrar + +// import ( +// "gno.land/r/varmeta/demo/v6/domain/registrar" +// ) + +// func main() { +// registrar.CommitHash("thinhnx.gno", "6c091667070fa0d70b8bab92755dec7af8d9adce498d08e18c6446e0d71d6cd9") +// println("done") +// } + +// Output +// committed hash diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/registrar.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/registrar.gno new file mode 100644 index 00000000..e9d3b323 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/registrar.gno @@ -0,0 +1,152 @@ +/* +This package contains functions that will actually execute the request from user +Features: Domain Registration, Domain Renewal, Domain Transfer, Domain Deletion... +*/ +// changelogs 1: move fee mgnt to registrar module, in oder to manage the coins sent from user to realm. +// changelogs 2: v2 - added sealed bidding logic - with default time xxx mins for each session + +// currently we dont using too much panic because we dont have defer functions to revert the state of storage +package registrar + +import ( + "std" + "time" + + "gno.land/p/varmeta/demo/v6/domain" +) + +// XXX: consider using panic instead of return string or errors +func Register(domainName string, mode string) string { + requestInfo := RequestInfo{ + WantedDomain: domainName, + Caller: std.PrevRealm().Addr(), + Mode: mode, + } + + regResult := executeRegister(requestInfo) + + // calling panic to stop paying fee + if !regResult.Success { + panic(regResult.ResultDetails.Error()) + } + // pay fee with panic inside + feeProcess(requestInfo) + return "Register Done" +} + +func executeRegister(req RequestInfo) ExecuteResult { + // check if domain name is regex valid + var execRes ExecuteResult + if !isValidDomain(req.WantedDomain) { + execRes.Success = false + execRes.ResultDetails = ErrInvalidDomainName + return execRes + } + + // check if dName is registered + if AlreadyRegistered(req.WantedDomain) { + execRes.Success = false + execRes.ResultDetails = ErrAlreadyRegistered + return execRes + } + + // execute register domain - mint the nft + // changelogs v2: we are using sealed bidding now + + caller := req.Caller + ttl := defaultExpireTime + metadata := metadataWrapper(caller, req.WantedDomain, ttl) + // create a new registry instance to save metadata and mint the NFT + errRegister := rootRegistry.RegisterDomain(caller, req.WantedDomain, metadata, ttl) + if errRegister != nil { + execRes.Success = false + execRes.ResultDetails = ErrCrossRealms + return execRes + } + // now save caller to corressponding tree to manage + domainStorage.Set(req.WantedDomain, caller) + + execRes.Success = true + return execRes +} + +func feeProcess(req RequestInfo) { + if req.Mode == "token" { + nativeProcess() + } else { + nativeProcess() + } +} + +func AlreadyRegistered(domainName string) bool { + // if can get owner -> existed + addr, err := rootRegistry.OwnerOf(domainName) + if err == nil && addr != "" { + return true + } + return false +} + +func GetOwner(domainName string) std.Address { + vl, existed := domainStorage.Get(domainName) + if !existed { + return "" + } + return vl.(std.Address) +} + +func Search(domainName string) (remapMetadata, string) { + validMetadata := remapMetadata{} + md, err := getMetadata(domainName) + if err != nil { + // return validMetadata, err.Error() + panic(err) + } + validMetadata.RegistrationTime = md.RegistrationTime.Format(time.RFC3339) + validMetadata.ExpirationTime = md.ExpirationTime.Format(time.RFC3339) + // jsonData, _ := validMetadata.MarshalJSON() + return validMetadata, "Search Success" +} + +func getMetadata(wantedDomain string) (domain.Metadata, error) { + // confirm the method? -> get all the fields if the fields slice is empty + metadata, err := rootRegistry.GetDomainFields(wantedDomain, []domain.MetadataField{}) + if err != nil { + return metadata, err + } + return metadata, nil +} + +// Transfer +func TransferDomain(from, to, domainName string) string { + requestInfo := RequestInfo{ + WantedDomain: domainName, + Caller: std.PrevRealm().Addr(), + } + if err := excuteTransfer(requestInfo); err != "" { + panic(err) + } + return "Transfer Done" +} + +func excuteTransfer(req RequestInfo) string { + if !AlreadyRegistered(req.WantedDomain) { + return ErrAlreadyRegistered.Error() + } + rootRegistry.TransferFrom(req.TransInfo.From, req.TransInfo.To, req.WantedDomain) + return "" +} + +func GetDomainName(addr string) []string { + domainList := []string{} + // search from local storage + domainStorage.Iterate("", "", func(key string, value interface{}) bool { + caller := value.(std.Address) + // not checking isExpired + if caller.String() == addr { + domainList = append(domainList, key) + } + return false + }) + return domainList +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/registrar_test.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/registrar_test.gno new file mode 100644 index 00000000..9a981ffd --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/registrar_test.gno @@ -0,0 +1,30 @@ +package registrar + +// import ( +// "std" +// "testing" +// ) + +// func TestRegisterDomain(t *testing.T) { +// tcs := []struct { +// dName string +// hashString string +// caller string +// expected string +// }{ +// {"thinhnx.gno", "6c091667070fa0d70b8bab92755dec7af8d9adce498d08e18c6446e0d71d6cd9", "g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9", "committed hash"}, +// } +// for tc := range tcs { +// name := tc.input +// t.Run(name, func(t *testing.T) { +// output := CommitHash(tc.dName, tc.hashString) +// if output != tc.expected { +// t.Errorf("Expected '%q, but got %q", tc.expected, output) +// } +// status := getCurrentStatus(tc.dName, tc.caller) +// if status != tc.expected { +// t.Errorf("got %q", status) +// } +// }) +// } +// } diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/sbid_realm.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/sbid_realm.gno new file mode 100644 index 00000000..5c20a0be --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/sbid_realm.gno @@ -0,0 +1,199 @@ +/* + this realm is implementaion of sealed bidding package, which object is domainName + this realm charges fee itself and not relate to the logic of sealed bidding +*/ + +package registrar + +import ( + "std" + "time" + + "gno.land/p/demo/avl" + sbid "gno.land/p/varmeta/demo/v6/sbid" +) + +func Render(path string) string { + return "Welcome to varmeta realm bidding" +} + +var root *avl.Tree // domainName <- *sbid.Bid{BidInfo} + +func init() { + initTime := time.Now() + println("time now: ", initTime) + root = avl.NewTree() + // infoTree = avl.NewTree() +} + +func newBid(domainName string) *sbid.Bid { + now := time.Now().UnixMilli() + endHashTime := time.Now().Add(defaultCommitHashTime).UnixMilli() + endPriceTime := time.Now().Add(defaultCommitHashTime + defaultCommitPriceTime).UnixMilli() + + bid := &sbid.Bid{ + Infos: avl.NewTree(), + StartTime: now, + EndHashTime: endHashTime, + EndPriceTime: endPriceTime, + } + root.Set(domainName, bid) + return bid +} + +// getBid returns the existed pointer to bid session in root. if there isnt, it will create new bid session +func getBid(domainName string) *sbid.Bid { + var bid *sbid.Bid + data, _ := root.Get(domainName) + bid = data.(*sbid.Bid) + return bid +} + +func isBidExisted(dName string) bool { + return root.Has(dName) +} + +func CommitHash(domainName string, hashString string) string { + var bid *sbid.Bid + if isBidExisted(domainName) { + bid = getBid(domainName) + } else { + bid = newBid(domainName) + } + caller := std.GetOrigCaller().String() + // commit hash will also update info tree of the bid session + result := bid.CommitHash(domainName, hashString, caller) + if result == "committed hash" { + chargeFee(fee.BidJoinFee, std.Address(caller)) + } + return result +} + +func GetTimeNow() int64 { + return time.Now().UnixMilli() +} + +func CommitPrice(domainName string, price int64, secret string) string { + var bid *sbid.Bid + if isBidExisted(domainName) { + bid = getBid(domainName) + } else { + bid = newBid(domainName) + } + caller := std.GetOrigCaller().String() + return bid.CommitPrice(domainName, price, secret, caller) +} + +func GetJoinedBid(caller string) []sbid.BidStatus { + return findStatusInRoot(caller) +} + +func findStatusInRoot(caller string) []sbid.BidStatus { + listStt := []sbid.BidStatus{} + root.Iterate("", "", func(key string, value interface{}) bool { + bid := value.(*sbid.Bid) + if bid.Infos.Has(caller) { + stt := bid.GetStatus(key, caller) + listStt = append(listStt, stt) + } + return false + }) + return listStt +} + +func GetCurrentStatus(dName string, caller string) string { + owner := GetOwner(dName) + if owner != "" { + return "owned by " + owner.String() + } + // TODO: this getBid func will create new bid session if there is no bid existed + // -> fix it please -> separate getBid logic (DONE) + var bid *sbid.Bid + if !isBidExisted(dName) { + return "domain name is free" + } else { + bid = getBid(dName) + } + status := bid.GetStatus(dName, caller) + if status.Status == "free" { + return "this caller is not joining bidding session" + } + // Auction closed with winner + now := time.Now().UnixMilli() + if bid.EndPriceTime <= now && owner != "" { + return "close" + } + // both undefined + return status.Status +} + +func GetCurrentWinner(dName string) string { + var bid *sbid.Bid + if isBidExisted(dName) { + bid = getBid(dName) + } else { + return "no session for this domain yet" + } + winner := bid.GetCurrentWinnerInfo() + if !winner.IsWinner { + return "no winner yet" + } + return winner.Bidder +} + +func now() int64 { + return time.Now().UnixMilli() +} + +func Claim(dName string) string { + if !root.Has(dName) { + panic("invalid domain name") + } + var bid *sbid.Bid + if isBidExisted(dName) { + bid = getBid(dName) + } else { + bid = newBid(dName) + } + caller := std.GetOrigCaller() + winner := bid.GetCurrentWinnerInfo() + if caller.String() != winner.Bidder { + panic("only winner can claim") + } + requestInfo := RequestInfo{ + WantedDomain: dName, + Caller: caller, + Mode: "native", + } + result := executeRegister(requestInfo) + if !result.Success { + panic("bad register") + } + chargeFee(winner.Price, caller) + feeProcess(requestInfo) + return "claimed" +} + +// // expose winner price - amount of fee that user need to pay in Claim phase +func GetWinnerPrice(dName string) int64 { + var bid *sbid.Bid + if isBidExisted(dName) { + bid = getBid(dName) + } else { + bid = newBid(dName) + } + + baseRegisterFee := GetRegisterFee(dName) + winner := bid.GetCurrentWinnerInfo() + return winner.Price + baseRegisterFee +} + +func chargeFee(amount int64, from std.Address) string { + bankerContract := std.CurrentRealm().Addr() + ugnotCoin := std.NewCoin("ugnot", amount) + coinsToTransfer := std.NewCoins(ugnotCoin) + // coins := checkCoin(from) + // ufmt.Println("send from contract ", bankerContract.String(), " to admin ", admin.String(), " amount: ", ugnotCoin) + bankerUser.SendCoins(bankerContract, admin, coinsToTransfer) + return "charged" +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/utils.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/utils.gno new file mode 100644 index 00000000..ccee49b5 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/registrar:2465335/utils.gno @@ -0,0 +1,39 @@ +/* +This check module contains function to do the checking stuffs +*/ +package registrar + +import ( + "regexp" + "time" +) + +var ( + defaultCommitHashTime = time.Second * 30 + defaultCommitPriceTime = time.Second * 30 + defaultExpireTime = time.Hour // 30 days + reName = regexp.MustCompile(`^[a-zA-Z0-9]{1,124}\.gno$`) +) + +// check for registering process +func isValidDomain(d string) bool { + return reName.MatchString(d) +} + +func GetExpirationDate(dName string) time.Time { + return rootRegistry.GetExpirationDate(dName) +} + +// for now, this function only let admin set +func SetExpirationDate(dName string, expDate time.Time) bool { + assertIsAdmin() + return rootRegistry.SetExpirationDate(dName, expDate) +} + +func SetCommitPhaseTime(duration int) { + defaultCommitHashTime = time.Duration(duration) * time.Second +} + +func SetCommitPriceTime(duration int) { + defaultCommitPriceTime = time.Duration(duration) * time.Second +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/checks_resolver.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/checks_resolver.gno new file mode 100644 index 00000000..394f7ec7 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/checks_resolver.gno @@ -0,0 +1,27 @@ +/* +This check module contains function to do the checking stuffs +*/ +package resolver + +import ( + "regexp" + "time" + + "gno.land/r/varmeta/demo/v6/domain/registrar" +) + +// const ( +// admin std.Address = "g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9" // -> @thinhnx +// ) + +var reName = regexp.MustCompile(`^[a-zA-Z0-9]{1,124}\.gno$`) + +// check for registering process +func isValidDomain(d string) bool { + return reName.MatchString(d) +} + +func isExpired(dName string) bool { + expDate := registrar.GetExpirationDate(dName) + return expDate.Before(time.Now()) +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/errors.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/errors.gno new file mode 100644 index 00000000..19709079 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/errors.gno @@ -0,0 +1,11 @@ +package resolver + +import ( + "errors" +) + +var ( + ErrNotFound = errors.New("not found") + ErrBadCall = errors.New("bad call") + ErrInvalidDomainName = errors.New("invalid domain name to register") +) diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/pkg_metadata.json b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/pkg_metadata.json new file mode 100644 index 00000000..f7c3b6e7 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/pkg_metadata.json @@ -0,0 +1 @@ +{"creator":"g162jgpk4740r6a7g53cgz9ahxqtyuekgqchw6w9","deposit":""} \ No newline at end of file diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/resolver.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/resolver.gno new file mode 100644 index 00000000..6ec2e016 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/resolver.gno @@ -0,0 +1,63 @@ +/* +The goal of the Resolver contract is keep track of the address for each ICNS name in a stateful manner. +It serves the purpose of "resolving" the ICNS Name +to the correct address (e.g "alice.gno" -> g1xxx). +*/ +// changelogs: move Register feature into this resolver package +// changelogs2: Removed local storage of resolver as cache, and every querires we query to registrar to get the result + +package resolver + +import ( + "std" + + "gno.land/r/varmeta/demo/v6/domain/registrar" +) + +type Record struct { + Owner std.Address + IsValid bool + Memo string // no more need this + Priority int +} + +// retrieve the record list to get the onchain address +func Resolve(domainName string) *Record { + if !isValidDomain(domainName) { + panic("bad domain name") + } + record := &Record{} + + owner := getOwnerFromDomainStorage(domainName) + if owner == "" { + record.Memo = "not found" + record.IsValid = false + return record + } + + if !isExpired(domainName) { + record.IsValid = true + record.Owner = owner + } else { + record.IsValid = false + } + return record +} + +func GetDomainName(addr string) []string { + return registrar.GetDomainName(addr) +} + +/* +If query in local storage not found +Query to DomainStorage by domainName -> get the registry -> use that registry to get the Owner() +and check the validation time? +*/ + +func existedInDomainStorage(domainName string) bool { + return registrar.AlreadyRegistered(domainName) +} + +func getOwnerFromDomainStorage(domainName string) std.Address { + return registrar.GetOwner(domainName) +} diff --git a/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/resolver_metadata.gno b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/resolver_metadata.gno new file mode 100644 index 00000000..7a2539e7 --- /dev/null +++ b/test4.gno.land/extracted/r/varmeta/demo/v6/domain/resolver:2465351/resolver_metadata.gno @@ -0,0 +1,15 @@ +package resolver + +import ( + "std" + "time" + + "gno.land/p/varmeta/demo/v6/domain" +) + +// Metadata wrapper +func metadataWrapper(owner std.Address, name string, ttl time.Duration) domain.Metadata { + crrTime := time.Now() + expTime := crrTime.Add(ttl) + return domain.NewMetadata("", name, "", "", crrTime, expTime, []domain.Trait{}) +} diff --git a/test4.gno.land/metadata.json b/test4.gno.land/metadata.json index 80b6865e..85f1e689 100644 --- a/test4.gno.land/metadata.json +++ b/test4.gno.land/metadata.json @@ -1,3 +1,3 @@ { - "latest_block_height": 2486724 + "latest_block_height": 2518430 }