From a71835a5c1683962e11b35d1e855b4ea9e8c1e34 Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Fri, 27 Dec 2024 16:40:30 +0100 Subject: [PATCH 01/11] feat: add dao kind in dao registry contract --- gno/r/dao_registry/dao_registry.gno | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gno/r/dao_registry/dao_registry.gno b/gno/r/dao_registry/dao_registry.gno index 9b1b7c20f3..f4d03deb9f 100644 --- a/gno/r/dao_registry/dao_registry.gno +++ b/gno/r/dao_registry/dao_registry.gno @@ -17,6 +17,7 @@ type Registration struct { CreatedAt time.Time `json:"createdAt"` Name string `json:"name"` Description string `json:"description"` + Kind string `json:"kind"` ImageURI string `json:"imageURI"` Getter DAOGetter `json:"-"` } @@ -30,18 +31,18 @@ var ( type DAOGetter func() dao_interfaces.IDAOCore -func Register(getter DAOGetter, name string, description string, imageURI string) { +func Register(getter DAOGetter, name string, description string, kind string, imageURI string) { realm := std.PrevRealm() pkgPath := realm.PkgPath() if pkgPath == "" { panic("caller is not a realm") } - doRegister(getter, pkgPath, realm.Addr(), name, description, imageURI) + doRegister(getter, pkgPath, realm.Addr(), name, description, kind, imageURI) } // splitted for tests -func doRegister(getter DAOGetter, pkgPath string, addr std.Address, name string, description string, imageURI string) { +func doRegister(getter DAOGetter, pkgPath string, addr std.Address, name string, description string, kind string, imageURI string) { if byPkgPath.Has(pkgPath) { panic("already registered") } @@ -52,6 +53,7 @@ func doRegister(getter DAOGetter, pkgPath string, addr std.Address, name string, CreatedAt: time.Now(), Name: name, Description: description, + Kind: kind, ImageURI: imageURI, Getter: getter, } @@ -118,6 +120,7 @@ func (r Registration) ToJSON() *json.Node { "createdAt": jsonutil.TimeNode(r.CreatedAt), "name": json.StringNode("", r.Name), "description": json.StringNode("", r.Description), + "kind": json.StringNode("", r.Kind), "imageURI": json.StringNode("", r.ImageURI), }) } From 8a4adcca542708b3a5cca7aa3ce32b3bdb3432ba Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Sat, 28 Dec 2024 18:21:52 +0100 Subject: [PATCH 02/11] feat: add dao kind in dao registry contract --- gno/r/dao_realm/dao_realm.gno | 2 +- gno/r/dao_roles_realm.gno/dao_roles_realm.gno | 2 +- packages/utils/gnodao/deploy.ts | 4 +- .../gnodao/generateMembershipDAOSource.ts | 22 ++++---- .../utils/gnodao/generateRolesDAOSource.ts | 56 +++++++++---------- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/gno/r/dao_realm/dao_realm.gno b/gno/r/dao_realm/dao_realm.gno index c9be11cc4d..feb3c564f0 100644 --- a/gno/r/dao_realm/dao_realm.gno +++ b/gno/r/dao_realm/dao_realm.gno @@ -94,7 +94,7 @@ func RegisterSelf() { panic("already registered") } - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "membership", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") registered = true } diff --git a/gno/r/dao_roles_realm.gno/dao_roles_realm.gno b/gno/r/dao_roles_realm.gno/dao_roles_realm.gno index f44253c377..84d423f289 100644 --- a/gno/r/dao_roles_realm.gno/dao_roles_realm.gno +++ b/gno/r/dao_roles_realm.gno/dao_roles_realm.gno @@ -105,7 +105,7 @@ func RegisterSelf() { panic("already registered") } - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "roles", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") registered = true } diff --git a/packages/utils/gnodao/deploy.ts b/packages/utils/gnodao/deploy.ts index 347c45bc24..ed5a243af3 100644 --- a/packages/utils/gnodao/deploy.ts +++ b/packages/utils/gnodao/deploy.ts @@ -1,7 +1,7 @@ -import { generateMembershipDAOSource } from "./generateMembershipDAOSource"; -import { generateRolesDAOSource } from "./generateRolesDAOSource"; import { adenaAddPkg } from "../gno"; import { DaoType } from "../types/organizations"; +import { generateMembershipDAOSource } from "./generateMembershipDAOSource"; +import { generateRolesDAOSource } from "./generateRolesDAOSource"; interface GnoDAOMember { address: string; diff --git a/packages/utils/gnodao/generateMembershipDAOSource.ts b/packages/utils/gnodao/generateMembershipDAOSource.ts index b9d3ae7e92..9e5e192509 100644 --- a/packages/utils/gnodao/generateMembershipDAOSource.ts +++ b/packages/utils/gnodao/generateMembershipDAOSource.ts @@ -1,5 +1,5 @@ -import { GnoDAOConfig } from "./deploy"; import { mustGetGnoNetwork } from "../../networks"; +import { GnoDAOConfig } from "./deploy"; // TODO: Allow the role modules to be optional and don't use in MembershipDAO export const generateMembershipDAOSource = ( @@ -33,11 +33,11 @@ export const generateMembershipDAOSource = ( votingModuleFactory := func(core dao_interfaces.IDAOCore) dao_interfaces.IVotingModule { group = voting_group.NewVotingGroup() ${conf.initialMembers - .map( - (member) => - `group.SetMemberPower("${member.address}", ${member.weight})`, - ) - .join("\n\t")} + .map( + (member) => + `group.SetMemberPower("${member.address}", ${member.weight})`, + ) + .join("\n\t")} return group } @@ -46,11 +46,11 @@ export const generateMembershipDAOSource = ( proposalModulesFactories := []dao_interfaces.ProposalModuleFactory{ func(core dao_interfaces.IDAOCore) dao_interfaces.IProposalModule { tt := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.thresholdPercent * 100, - )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% + conf.thresholdPercent * 100, + )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% tq := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.quorumPercent * 100, - )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% + conf.quorumPercent * 100, + )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% return proposal_single.NewDAOProposalSingle(core, &proposal_single.DAOProposalSingleOpts{ MaxVotingPeriod: dao_utils.DurationTime(time.Second * ${conf.maxVotingPeriodSeconds}), Threshold: &proposal_single.ThresholdThresholdQuorum{ @@ -82,7 +82,7 @@ export const generateMembershipDAOSource = ( profile.SetStringField(profile.Bio, "${conf.description}") profile.SetStringField(profile.Avatar, "${conf.imageURI}") - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "${conf.imageURI}") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "memberships", "${conf.imageURI}") } func Render(path string) string { diff --git a/packages/utils/gnodao/generateRolesDAOSource.ts b/packages/utils/gnodao/generateRolesDAOSource.ts index 2dfafa3844..645cd3c0eb 100644 --- a/packages/utils/gnodao/generateRolesDAOSource.ts +++ b/packages/utils/gnodao/generateRolesDAOSource.ts @@ -1,5 +1,5 @@ -import { GnoDAOConfig } from "./deploy"; import { mustGetGnoNetwork } from "../../networks"; +import { GnoDAOConfig } from "./deploy"; export const generateRolesDAOSource = ( networkId: string, @@ -35,33 +35,33 @@ var ( func init() { roles = dao_roles_group.NewRolesGroup() ${(conf.roles ?? []) - .map( - (role) => - `roles.NewRoleJSON("${role.name}", "[${(role.resources ?? []) - .map( - (resource) => - `{\\"resource\\": \\"${resource}\\", \\"power\\": \\"999\\"}`, - ) - .join(", ")}]")`, - ) - .join("\n\t")} + .map( + (role) => + `roles.NewRoleJSON("${role.name}", "[${(role.resources ?? []) + .map( + (resource) => + `{\\"resource\\": \\"${resource}\\", \\"power\\": \\"999\\"}`, + ) + .join(", ")}]")`, + ) + .join("\n\t")} ${conf.initialMembers - .filter((member) => member.roles.length > 0) - .map((member) => - member.roles - .map((role) => `roles.GrantRole("${member.address}", "${role}")`) - .join("\n\t"), - ) - .join("\n\t")} + .filter((member) => member.roles.length > 0) + .map((member) => + member.roles + .map((role) => `roles.GrantRole("${member.address}", "${role}")`) + .join("\n\t"), + ) + .join("\n\t")} votingModuleFactory := func(core dao_interfaces.IDAOCore) dao_interfaces.IVotingModule { group = voting_group.NewRolesVotingGroup(roles) ${conf.initialMembers - .map( - (member) => - `group.SetMemberPower("${member.address}", ${member.weight})`, - ) - .join("\n\t")} + .map( + (member) => + `group.SetMemberPower("${member.address}", ${member.weight})`, + ) + .join("\n\t")} return group } @@ -71,11 +71,11 @@ func init() { proposalModulesFactories := []dao_interfaces.ProposalModuleFactory{ func(core dao_interfaces.IDAOCore) dao_interfaces.IProposalModule { tt := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.thresholdPercent * 100, - )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% + conf.thresholdPercent * 100, + )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% tq := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.quorumPercent * 100, - )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% + conf.quorumPercent * 100, + )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% return proposal_single.NewDAOProposalSingle(core, &proposal_single.DAOProposalSingleOpts{ MaxVotingPeriod: dao_utils.DurationTime(time.Second * ${conf.maxVotingPeriodSeconds}), Threshold: &proposal_single.ThresholdThresholdQuorum{ @@ -107,7 +107,7 @@ func init() { profile.SetStringField(profile.Bio, "${conf.description}") profile.SetStringField(profile.Avatar, "${conf.imageURI}") - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "${conf.imageURI}") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "roles", "${conf.imageURI}") } func Render(path string) string { From ea93476f387e4635f6657590ed9d17b49dd79b5a Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 11:14:56 +0100 Subject: [PATCH 03/11] feat(gno/dao): retrieve & display dao kind --- .../TeritoriNameService.types.ts | 1 + packages/hooks/gno/useGnoDAOs.ts | 1 + packages/hooks/useNSNameInfo.ts | 1 + .../UserPublicProfile/components/UPPIntro.tsx | 14 +++++ packages/utils/gnodao/deploy.ts | 4 +- .../gnodao/generateMembershipDAOSource.ts | 22 ++++---- .../utils/gnodao/generateRolesDAOSource.ts | 54 +++++++++---------- 7 files changed, 57 insertions(+), 40 deletions(-) diff --git a/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts b/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts index c68f4902eb..878f9e262a 100644 --- a/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts +++ b/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts @@ -72,6 +72,7 @@ export interface Metadata { telegram_id?: string | null; twitter_id?: string | null; validator_operator_address?: string | null; + dao_kind?: string | null; [k: string]: unknown; } export interface AllOperatorsResponse { diff --git a/packages/hooks/gno/useGnoDAOs.ts b/packages/hooks/gno/useGnoDAOs.ts index 414770a53c..e8644a9630 100644 --- a/packages/hooks/gno/useGnoDAOs.ts +++ b/packages/hooks/gno/useGnoDAOs.ts @@ -11,6 +11,7 @@ export type GnoDAORegistration = { name: string; description: string; imageURI: string; + kind: string; }; export const useGnoDAOs = (networkId: string | undefined) => { diff --git a/packages/hooks/useNSNameInfo.ts b/packages/hooks/useNSNameInfo.ts index 1219b1d8ed..30128b26d4 100644 --- a/packages/hooks/useNSNameInfo.ts +++ b/packages/hooks/useNSNameInfo.ts @@ -45,6 +45,7 @@ const gnoGetNSNameInfo = async ( public_name: res.name, public_bio: res.description, image: res.imageURI, + dao_kind: res.kind, }; const user: NftInfoResponse = { extension: data, diff --git a/packages/screens/UserPublicProfile/components/UPPIntro.tsx b/packages/screens/UserPublicProfile/components/UPPIntro.tsx index 308bd30dba..056d16520c 100644 --- a/packages/screens/UserPublicProfile/components/UPPIntro.tsx +++ b/packages/screens/UserPublicProfile/components/UPPIntro.tsx @@ -21,6 +21,7 @@ import { SocialButton } from "@/components/buttons/SocialButton"; import { SocialButtonSecondary } from "@/components/buttons/SocialButtonSecondary"; import { ProfileButton } from "@/components/hub/ProfileButton"; import { UserAvatarWithFrame } from "@/components/images/AvatarWithFrame"; +import { useIsDAO } from "@/hooks/cosmwasm/useCosmWasmContractInfo"; import { usePremiumChannel } from "@/hooks/feed/usePremiumChannel"; import { usePremiumIsSubscribed } from "@/hooks/feed/usePremiumIsSubscribed"; import { useMaxResolution } from "@/hooks/useMaxResolution"; @@ -51,6 +52,8 @@ export const UPPIntro: React.FC<{ }> = ({ userId, isUserOwner, setIsEditProfileModal = (val) => {} }) => { const selectedWallet = useSelectedWallet(); const { metadata } = useNSUserInfo(userId); + console.log(metadata); + const { isDAO } = useIsDAO(userId); const { copyToClipboard } = useCopyToClipboard(); const socialButtonStyle = { margin: layout.spacing_x0_75 }; const [network, userAddress] = parseUserId(userId); @@ -295,6 +298,17 @@ export const UPPIntro: React.FC<{ > {metadata?.public_bio} + {isDAO && metadata.dao_kind && ( + + {metadata?.dao_kind} + + )} {/* Stats and public address */} - `group.SetMemberPower("${member.address}", ${member.weight})`, - ) - .join("\n\t")} + .map( + (member) => + `group.SetMemberPower("${member.address}", ${member.weight})`, + ) + .join("\n\t")} return group } @@ -46,11 +46,11 @@ export const generateMembershipDAOSource = ( proposalModulesFactories := []dao_interfaces.ProposalModuleFactory{ func(core dao_interfaces.IDAOCore) dao_interfaces.IProposalModule { tt := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.thresholdPercent * 100, - )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% + conf.thresholdPercent * 100, + )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% tq := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.quorumPercent * 100, - )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% + conf.quorumPercent * 100, + )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% return proposal_single.NewDAOProposalSingle(core, &proposal_single.DAOProposalSingleOpts{ MaxVotingPeriod: dao_utils.DurationTime(time.Second * ${conf.maxVotingPeriodSeconds}), Threshold: &proposal_single.ThresholdThresholdQuorum{ @@ -82,7 +82,7 @@ export const generateMembershipDAOSource = ( profile.SetStringField(profile.Bio, "${conf.description}") profile.SetStringField(profile.Avatar, "${conf.imageURI}") - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "memberships", "${conf.imageURI}") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "membership", "${conf.imageURI}") } func Render(path string) string { diff --git a/packages/utils/gnodao/generateRolesDAOSource.ts b/packages/utils/gnodao/generateRolesDAOSource.ts index 645cd3c0eb..58bb17b27b 100644 --- a/packages/utils/gnodao/generateRolesDAOSource.ts +++ b/packages/utils/gnodao/generateRolesDAOSource.ts @@ -1,5 +1,5 @@ -import { mustGetGnoNetwork } from "../../networks"; import { GnoDAOConfig } from "./deploy"; +import { mustGetGnoNetwork } from "../../networks"; export const generateRolesDAOSource = ( networkId: string, @@ -35,33 +35,33 @@ var ( func init() { roles = dao_roles_group.NewRolesGroup() ${(conf.roles ?? []) - .map( - (role) => - `roles.NewRoleJSON("${role.name}", "[${(role.resources ?? []) - .map( - (resource) => - `{\\"resource\\": \\"${resource}\\", \\"power\\": \\"999\\"}`, - ) - .join(", ")}]")`, - ) - .join("\n\t")} + .map( + (role) => + `roles.NewRoleJSON("${role.name}", "[${(role.resources ?? []) + .map( + (resource) => + `{\\"resource\\": \\"${resource}\\", \\"power\\": \\"999\\"}`, + ) + .join(", ")}]")`, + ) + .join("\n\t")} ${conf.initialMembers - .filter((member) => member.roles.length > 0) - .map((member) => - member.roles - .map((role) => `roles.GrantRole("${member.address}", "${role}")`) - .join("\n\t"), - ) - .join("\n\t")} + .filter((member) => member.roles.length > 0) + .map((member) => + member.roles + .map((role) => `roles.GrantRole("${member.address}", "${role}")`) + .join("\n\t"), + ) + .join("\n\t")} votingModuleFactory := func(core dao_interfaces.IDAOCore) dao_interfaces.IVotingModule { group = voting_group.NewRolesVotingGroup(roles) ${conf.initialMembers - .map( - (member) => - `group.SetMemberPower("${member.address}", ${member.weight})`, - ) - .join("\n\t")} + .map( + (member) => + `group.SetMemberPower("${member.address}", ${member.weight})`, + ) + .join("\n\t")} return group } @@ -71,11 +71,11 @@ func init() { proposalModulesFactories := []dao_interfaces.ProposalModuleFactory{ func(core dao_interfaces.IDAOCore) dao_interfaces.IProposalModule { tt := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.thresholdPercent * 100, - )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% + conf.thresholdPercent * 100, + )}) // ${Math.ceil(conf.thresholdPercent * 100) / 100}% tq := proposal_single.PercentageThresholdPercent(${Math.ceil( - conf.quorumPercent * 100, - )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% + conf.quorumPercent * 100, + )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% return proposal_single.NewDAOProposalSingle(core, &proposal_single.DAOProposalSingleOpts{ MaxVotingPeriod: dao_utils.DurationTime(time.Second * ${conf.maxVotingPeriodSeconds}), Threshold: &proposal_single.ThresholdThresholdQuorum{ From 5cbf38e12216e469b351d4225fb03e80dd6464fe Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 11:24:49 +0100 Subject: [PATCH 04/11] feat(gno/dao): enhance visibility of dao kind in upp --- .../UserPublicProfile/components/UPPIntro.tsx | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/screens/UserPublicProfile/components/UPPIntro.tsx b/packages/screens/UserPublicProfile/components/UPPIntro.tsx index 056d16520c..4da15a045c 100644 --- a/packages/screens/UserPublicProfile/components/UPPIntro.tsx +++ b/packages/screens/UserPublicProfile/components/UPPIntro.tsx @@ -37,10 +37,11 @@ import { DEFAULT_NAME } from "@/utils/social-feed"; import { neutral00, neutral55, + neutralA3, secondaryColor, yellowPremium, } from "@/utils/style/colors"; -import { fontBold16, fontMedium14 } from "@/utils/style/fonts"; +import { fontBold16, fontMedium14, fontSemibold12 } from "@/utils/style/fonts"; import { layout, RESPONSIVE_BREAKPOINT_S } from "@/utils/style/layout"; import { tinyAddress } from "@/utils/text"; import { normalizeTwitterId } from "@/utils/twitter"; @@ -288,7 +289,32 @@ export const UPPIntro: React.FC<{ @{metadata.tokenId || userAddress} - + {isDAO && metadata.dao_kind && ( + + + {metadata.dao_kind} + + + )} {metadata?.public_bio} - {isDAO && metadata.dao_kind && ( - - {metadata?.dao_kind} - - )} {/* Stats and public address */} Date: Thu, 2 Jan 2025 11:35:51 +0100 Subject: [PATCH 05/11] feat(gno/dao): enhance naming for dao kind --- packages/utils/gnodao/generateMembershipDAOSource.ts | 2 +- packages/utils/gnodao/generateRolesDAOSource.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/utils/gnodao/generateMembershipDAOSource.ts b/packages/utils/gnodao/generateMembershipDAOSource.ts index fbd721a72d..51a319453b 100644 --- a/packages/utils/gnodao/generateMembershipDAOSource.ts +++ b/packages/utils/gnodao/generateMembershipDAOSource.ts @@ -82,7 +82,7 @@ export const generateMembershipDAOSource = ( profile.SetStringField(profile.Bio, "${conf.description}") profile.SetStringField(profile.Avatar, "${conf.imageURI}") - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "membership", "${conf.imageURI}") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "memberships based organization", "${conf.imageURI}") } func Render(path string) string { diff --git a/packages/utils/gnodao/generateRolesDAOSource.ts b/packages/utils/gnodao/generateRolesDAOSource.ts index 58bb17b27b..f99eb3dfb9 100644 --- a/packages/utils/gnodao/generateRolesDAOSource.ts +++ b/packages/utils/gnodao/generateRolesDAOSource.ts @@ -107,7 +107,7 @@ func init() { profile.SetStringField(profile.Bio, "${conf.description}") profile.SetStringField(profile.Avatar, "${conf.imageURI}") - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "roles", "${conf.imageURI}") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "roles based organization", "${conf.imageURI}") } func Render(path string) string { From 931e9ae44bdd9fd5af561834eb14f528f6af0af7 Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 11:36:46 +0100 Subject: [PATCH 06/11] feat(gno/dao): enhance naming for dao kind --- gno/r/dao_realm/dao_realm.gno | 2 +- gno/r/dao_roles_realm.gno/dao_roles_realm.gno | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gno/r/dao_realm/dao_realm.gno b/gno/r/dao_realm/dao_realm.gno index feb3c564f0..2e7989ce88 100644 --- a/gno/r/dao_realm/dao_realm.gno +++ b/gno/r/dao_realm/dao_realm.gno @@ -94,7 +94,7 @@ func RegisterSelf() { panic("already registered") } - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "membership", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "memberships based organization", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") registered = true } diff --git a/gno/r/dao_roles_realm.gno/dao_roles_realm.gno b/gno/r/dao_roles_realm.gno/dao_roles_realm.gno index 84d423f289..c0f6086001 100644 --- a/gno/r/dao_roles_realm.gno/dao_roles_realm.gno +++ b/gno/r/dao_roles_realm.gno/dao_roles_realm.gno @@ -105,7 +105,7 @@ func RegisterSelf() { panic("already registered") } - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "roles", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "roles based organization", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") registered = true } From bdcef0034c8de6c6a2894884fe80bd45d5476ac0 Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 11:39:26 +0100 Subject: [PATCH 07/11] chore(gno/dao): remove console log --- packages/screens/UserPublicProfile/components/UPPIntro.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/screens/UserPublicProfile/components/UPPIntro.tsx b/packages/screens/UserPublicProfile/components/UPPIntro.tsx index 4da15a045c..5cc5f7de15 100644 --- a/packages/screens/UserPublicProfile/components/UPPIntro.tsx +++ b/packages/screens/UserPublicProfile/components/UPPIntro.tsx @@ -53,7 +53,6 @@ export const UPPIntro: React.FC<{ }> = ({ userId, isUserOwner, setIsEditProfileModal = (val) => {} }) => { const selectedWallet = useSelectedWallet(); const { metadata } = useNSUserInfo(userId); - console.log(metadata); const { isDAO } = useIsDAO(userId); const { copyToClipboard } = useCopyToClipboard(); const socialButtonStyle = { margin: layout.spacing_x0_75 }; From cb2b583c810320369c23a517b70df522e7a90517 Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 11:41:34 +0100 Subject: [PATCH 08/11] test(gno/dao): adapt unit test to handle kind field in dao registry --- gno/r/dao_registry/dao_registry_test.gno | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gno/r/dao_registry/dao_registry_test.gno b/gno/r/dao_registry/dao_registry_test.gno index 73ad99b22f..799a41a05e 100644 --- a/gno/r/dao_registry/dao_registry_test.gno +++ b/gno/r/dao_registry/dao_registry_test.gno @@ -24,7 +24,7 @@ func TestRegistry(t *testing.T) { addr := std.DerivePkgAddr(pkgPath) createdAt := time.Now() core := dao_interfaces.NewDummyCore() - doRegister(func() dao_interfaces.IDAOCore { return core }, pkgPath, addr, "Test DAO", `This is a test DAO"\n\r\t\`+"\n\r\t", "https://example.com/image.png") + doRegister(func() dao_interfaces.IDAOCore { return core }, pkgPath, addr, "Test DAO", `This is a test DAO"\n\r\t\`+"\n\r\t", "kind", "https://example.com/image.png") registrations = List("", "", 0, false) if len(registrations) != 1 { @@ -45,13 +45,13 @@ func TestRegistry(t *testing.T) { } registrationsJSON = ListJSON("", "", 0, false) - expected := ufmt.Sprintf(`[{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","imageURI":"https://example.com/image.png"}]`) + expected := ufmt.Sprintf(`[{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","kind":"kind","imageURI":"https://example.com/image.png"}]`) if registrationsJSON != expected { t.Fatal("expected JSON to match, got:\n" + registrationsJSON + "\nexpected:\n" + expected) } registrationJSON := GetJSON(pkgPath) - expected = ufmt.Sprintf(`{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","imageURI":"https://example.com/image.png"}`) + expected = ufmt.Sprintf(`{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","kind":"kind","imageURI":"https://example.com/image.png"}`) if registrationJSON != expected { t.Fatal("expected JSON to match, got:\n" + registrationJSON + "\nexpected:\n" + expected) } From fcdd659a697a325a6a1164052b2b0871eafadf61 Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 15:25:18 +0100 Subject: [PATCH 09/11] feat(gno/dao): deduce org. kind through voting module info --- .../roles_voting_group.gno | 2 +- packages/hooks/dao/useDAOKind.ts | 38 +++++++++++++++++++ .../UserPublicProfile/components/UPPIntro.tsx | 4 +- packages/utils/gnodao/helpers.ts | 13 +++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 packages/hooks/dao/useDAOKind.ts diff --git a/gno/p/dao_roles_voting_group/roles_voting_group.gno b/gno/p/dao_roles_voting_group/roles_voting_group.gno index 07c8d293ef..09c125b46c 100644 --- a/gno/p/dao_roles_voting_group/roles_voting_group.gno +++ b/gno/p/dao_roles_voting_group/roles_voting_group.gno @@ -50,7 +50,7 @@ func NewRolesVotingGroup(rm *dao_roles_group.RolesGroup) *RolesVotingGroup { func (v *RolesVotingGroup) Info() dao_interfaces.ModuleInfo { return dao_interfaces.ModuleInfo{ - Kind: "gno.land/p/teritori/dao_voting_group", + Kind: "gno.land/p/teritori/dao_roles_voting_group", Version: "0.1.0", } } diff --git a/packages/hooks/dao/useDAOKind.ts b/packages/hooks/dao/useDAOKind.ts new file mode 100644 index 0000000000..18919ef761 --- /dev/null +++ b/packages/hooks/dao/useDAOKind.ts @@ -0,0 +1,38 @@ +import { GnoJSONRPCProvider } from "@gnolang/gno-js-client"; +import { useQuery } from "@tanstack/react-query"; + +import { NetworkKind, parseUserId } from "@/networks"; +import { extractGnoString } from "@/utils/gno"; +import { extractDaoKind } from "@/utils/gnodao/helpers"; + +export const useDAOKind = (daoId: string | undefined) => { + const { data, ...other } = useQuery( + ["daoKind", daoId], + async () => { + if (!daoId) { + return null; + } + const [network, packagePath] = parseUserId(daoId); + if (network?.kind !== NetworkKind.Gno) { + return null; + } + // Ensure is a DAO by checking the addr is a realm addr and not an EOA + if (!packagePath?.startsWith("gno.land/")) { + return null; + } + + const provider = new GnoJSONRPCProvider(network.endpoint); + const info = extractGnoString( + await provider.evaluateExpression( + packagePath, + `daoCore.VotingModule().Info().String()`, + 0, + ), + ); + + return extractDaoKind(info); + }, + { staleTime: Infinity, enabled: !!daoId }, + ); + return { daoKind: data, ...other }; +}; diff --git a/packages/screens/UserPublicProfile/components/UPPIntro.tsx b/packages/screens/UserPublicProfile/components/UPPIntro.tsx index 5cc5f7de15..97606cc53b 100644 --- a/packages/screens/UserPublicProfile/components/UPPIntro.tsx +++ b/packages/screens/UserPublicProfile/components/UPPIntro.tsx @@ -22,6 +22,7 @@ import { SocialButtonSecondary } from "@/components/buttons/SocialButtonSecondar import { ProfileButton } from "@/components/hub/ProfileButton"; import { UserAvatarWithFrame } from "@/components/images/AvatarWithFrame"; import { useIsDAO } from "@/hooks/cosmwasm/useCosmWasmContractInfo"; +import { useDAOKind } from "@/hooks/dao/useDAOKind"; import { usePremiumChannel } from "@/hooks/feed/usePremiumChannel"; import { usePremiumIsSubscribed } from "@/hooks/feed/usePremiumIsSubscribed"; import { useMaxResolution } from "@/hooks/useMaxResolution"; @@ -54,6 +55,7 @@ export const UPPIntro: React.FC<{ const selectedWallet = useSelectedWallet(); const { metadata } = useNSUserInfo(userId); const { isDAO } = useIsDAO(userId); + const { daoKind } = useDAOKind(userId); const { copyToClipboard } = useCopyToClipboard(); const socialButtonStyle = { margin: layout.spacing_x0_75 }; const [network, userAddress] = parseUserId(userId); @@ -288,7 +290,7 @@ export const UPPIntro: React.FC<{ @{metadata.tokenId || userAddress} - {isDAO && metadata.dao_kind && ( + {isDAO && daoKind && ( { ret_num = ret_num > 100 ? 100 : ret_num; return (ret_num / 100).toFixed(2); }; + +export const extractDaoKind = (votingModuleInfo: string): string => { + const rawDaoKind = votingModuleInfo.split("@v")[0]; + console.log("rawDaoKind", rawDaoKind); + switch (rawDaoKind) { + case "gno.land/p/teritori/dao_roles_voting_group": + return "roles based organization"; + case "gno.land/p/teritori/dao_voting_group": + return "memberships based organization"; + default: + return ""; + } +}; From 4a78eda630a6b1483844de5bd1024de2255d969d Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 15:43:30 +0100 Subject: [PATCH 10/11] feat(gno/dao): remove previous approach --- gno/r/dao_realm/dao_realm.gno | 2 +- gno/r/dao_registry/dao_registry.gno | 9 +++------ gno/r/dao_registry/dao_registry_test.gno | 6 +++--- gno/r/dao_roles_realm.gno/dao_roles_realm.gno | 2 +- .../teritori-name-service/TeritoriNameService.types.ts | 1 - packages/hooks/gno/useGnoDAOs.ts | 1 - packages/hooks/useNSNameInfo.ts | 1 - packages/utils/gnodao/generateMembershipDAOSource.ts | 2 +- packages/utils/gnodao/generateRolesDAOSource.ts | 2 +- 9 files changed, 10 insertions(+), 16 deletions(-) diff --git a/gno/r/dao_realm/dao_realm.gno b/gno/r/dao_realm/dao_realm.gno index 2e7989ce88..c9be11cc4d 100644 --- a/gno/r/dao_realm/dao_realm.gno +++ b/gno/r/dao_realm/dao_realm.gno @@ -94,7 +94,7 @@ func RegisterSelf() { panic("already registered") } - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "memberships based organization", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") registered = true } diff --git a/gno/r/dao_registry/dao_registry.gno b/gno/r/dao_registry/dao_registry.gno index f4d03deb9f..9b1b7c20f3 100644 --- a/gno/r/dao_registry/dao_registry.gno +++ b/gno/r/dao_registry/dao_registry.gno @@ -17,7 +17,6 @@ type Registration struct { CreatedAt time.Time `json:"createdAt"` Name string `json:"name"` Description string `json:"description"` - Kind string `json:"kind"` ImageURI string `json:"imageURI"` Getter DAOGetter `json:"-"` } @@ -31,18 +30,18 @@ var ( type DAOGetter func() dao_interfaces.IDAOCore -func Register(getter DAOGetter, name string, description string, kind string, imageURI string) { +func Register(getter DAOGetter, name string, description string, imageURI string) { realm := std.PrevRealm() pkgPath := realm.PkgPath() if pkgPath == "" { panic("caller is not a realm") } - doRegister(getter, pkgPath, realm.Addr(), name, description, kind, imageURI) + doRegister(getter, pkgPath, realm.Addr(), name, description, imageURI) } // splitted for tests -func doRegister(getter DAOGetter, pkgPath string, addr std.Address, name string, description string, kind string, imageURI string) { +func doRegister(getter DAOGetter, pkgPath string, addr std.Address, name string, description string, imageURI string) { if byPkgPath.Has(pkgPath) { panic("already registered") } @@ -53,7 +52,6 @@ func doRegister(getter DAOGetter, pkgPath string, addr std.Address, name string, CreatedAt: time.Now(), Name: name, Description: description, - Kind: kind, ImageURI: imageURI, Getter: getter, } @@ -120,7 +118,6 @@ func (r Registration) ToJSON() *json.Node { "createdAt": jsonutil.TimeNode(r.CreatedAt), "name": json.StringNode("", r.Name), "description": json.StringNode("", r.Description), - "kind": json.StringNode("", r.Kind), "imageURI": json.StringNode("", r.ImageURI), }) } diff --git a/gno/r/dao_registry/dao_registry_test.gno b/gno/r/dao_registry/dao_registry_test.gno index 799a41a05e..73ad99b22f 100644 --- a/gno/r/dao_registry/dao_registry_test.gno +++ b/gno/r/dao_registry/dao_registry_test.gno @@ -24,7 +24,7 @@ func TestRegistry(t *testing.T) { addr := std.DerivePkgAddr(pkgPath) createdAt := time.Now() core := dao_interfaces.NewDummyCore() - doRegister(func() dao_interfaces.IDAOCore { return core }, pkgPath, addr, "Test DAO", `This is a test DAO"\n\r\t\`+"\n\r\t", "kind", "https://example.com/image.png") + doRegister(func() dao_interfaces.IDAOCore { return core }, pkgPath, addr, "Test DAO", `This is a test DAO"\n\r\t\`+"\n\r\t", "https://example.com/image.png") registrations = List("", "", 0, false) if len(registrations) != 1 { @@ -45,13 +45,13 @@ func TestRegistry(t *testing.T) { } registrationsJSON = ListJSON("", "", 0, false) - expected := ufmt.Sprintf(`[{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","kind":"kind","imageURI":"https://example.com/image.png"}]`) + expected := ufmt.Sprintf(`[{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","imageURI":"https://example.com/image.png"}]`) if registrationsJSON != expected { t.Fatal("expected JSON to match, got:\n" + registrationsJSON + "\nexpected:\n" + expected) } registrationJSON := GetJSON(pkgPath) - expected = ufmt.Sprintf(`{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","kind":"kind","imageURI":"https://example.com/image.png"}`) + expected = ufmt.Sprintf(`{"pkgPath":"gno.land/r/demo/test_dao","addr":"g1m56y6xlx95sykjchn9h9q4e7k5u2zte4as9rdr","createdAt":"2009-02-13T23:31:30Z","name":"Test DAO","description":"This is a test DAO\"\\n\\r\\t\\\n\r\t","imageURI":"https://example.com/image.png"}`) if registrationJSON != expected { t.Fatal("expected JSON to match, got:\n" + registrationJSON + "\nexpected:\n" + expected) } diff --git a/gno/r/dao_roles_realm.gno/dao_roles_realm.gno b/gno/r/dao_roles_realm.gno/dao_roles_realm.gno index c0f6086001..f44253c377 100644 --- a/gno/r/dao_roles_realm.gno/dao_roles_realm.gno +++ b/gno/r/dao_roles_realm.gno/dao_roles_realm.gno @@ -105,7 +105,7 @@ func RegisterSelf() { panic("already registered") } - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "roles based organization", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "DAO Realm", "Default testing DAO", "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=1080&fit=max") registered = true } diff --git a/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts b/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts index 878f9e262a..c68f4902eb 100644 --- a/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts +++ b/packages/contracts-clients/teritori-name-service/TeritoriNameService.types.ts @@ -72,7 +72,6 @@ export interface Metadata { telegram_id?: string | null; twitter_id?: string | null; validator_operator_address?: string | null; - dao_kind?: string | null; [k: string]: unknown; } export interface AllOperatorsResponse { diff --git a/packages/hooks/gno/useGnoDAOs.ts b/packages/hooks/gno/useGnoDAOs.ts index e8644a9630..414770a53c 100644 --- a/packages/hooks/gno/useGnoDAOs.ts +++ b/packages/hooks/gno/useGnoDAOs.ts @@ -11,7 +11,6 @@ export type GnoDAORegistration = { name: string; description: string; imageURI: string; - kind: string; }; export const useGnoDAOs = (networkId: string | undefined) => { diff --git a/packages/hooks/useNSNameInfo.ts b/packages/hooks/useNSNameInfo.ts index 30128b26d4..1219b1d8ed 100644 --- a/packages/hooks/useNSNameInfo.ts +++ b/packages/hooks/useNSNameInfo.ts @@ -45,7 +45,6 @@ const gnoGetNSNameInfo = async ( public_name: res.name, public_bio: res.description, image: res.imageURI, - dao_kind: res.kind, }; const user: NftInfoResponse = { extension: data, diff --git a/packages/utils/gnodao/generateMembershipDAOSource.ts b/packages/utils/gnodao/generateMembershipDAOSource.ts index 51a319453b..b9d3ae7e92 100644 --- a/packages/utils/gnodao/generateMembershipDAOSource.ts +++ b/packages/utils/gnodao/generateMembershipDAOSource.ts @@ -82,7 +82,7 @@ export const generateMembershipDAOSource = ( profile.SetStringField(profile.Bio, "${conf.description}") profile.SetStringField(profile.Avatar, "${conf.imageURI}") - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "memberships based organization", "${conf.imageURI}") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "${conf.imageURI}") } func Render(path string) string { diff --git a/packages/utils/gnodao/generateRolesDAOSource.ts b/packages/utils/gnodao/generateRolesDAOSource.ts index f99eb3dfb9..2dfafa3844 100644 --- a/packages/utils/gnodao/generateRolesDAOSource.ts +++ b/packages/utils/gnodao/generateRolesDAOSource.ts @@ -107,7 +107,7 @@ func init() { profile.SetStringField(profile.Bio, "${conf.description}") profile.SetStringField(profile.Avatar, "${conf.imageURI}") - dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "roles based organization", "${conf.imageURI}") + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "${conf.imageURI}") } func Render(path string) string { From 532c09e0a584906b9ce7c47088beb6fad801932d Mon Sep 17 00:00:00 2001 From: MikaelVallenet Date: Thu, 2 Jan 2025 15:52:25 +0100 Subject: [PATCH 11/11] feat(gno/dao): allow dao kind to be undefined --- packages/screens/UserPublicProfile/components/UPPIntro.tsx | 2 +- packages/utils/gnodao/helpers.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/screens/UserPublicProfile/components/UPPIntro.tsx b/packages/screens/UserPublicProfile/components/UPPIntro.tsx index 97606cc53b..53275911f2 100644 --- a/packages/screens/UserPublicProfile/components/UPPIntro.tsx +++ b/packages/screens/UserPublicProfile/components/UPPIntro.tsx @@ -312,7 +312,7 @@ export const UPPIntro: React.FC<{ }, ]} > - {metadata.dao_kind} + {daoKind} )} diff --git a/packages/utils/gnodao/helpers.ts b/packages/utils/gnodao/helpers.ts index 129d01c987..70532c1537 100644 --- a/packages/utils/gnodao/helpers.ts +++ b/packages/utils/gnodao/helpers.ts @@ -17,15 +17,16 @@ export const getPercent = (num: number | undefined): string => { return (ret_num / 100).toFixed(2); }; -export const extractDaoKind = (votingModuleInfo: string): string => { +export const extractDaoKind = ( + votingModuleInfo: string, +): string | undefined => { const rawDaoKind = votingModuleInfo.split("@v")[0]; - console.log("rawDaoKind", rawDaoKind); switch (rawDaoKind) { case "gno.land/p/teritori/dao_roles_voting_group": return "roles based organization"; case "gno.land/p/teritori/dao_voting_group": return "memberships based organization"; default: - return ""; + return undefined; } };