diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 48bcfa9b5d..c09b43fa54 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,12 +14,27 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown - - name: Build - run: make build.rust + - name: Format + run: make fmt.rust + + - name: Lint + run: make lint.rust - name: Test run: make test.rust - - name: Check diff - run: git diff --exit-code + - name: Build + run: make build.rust + + - name: Check that there is no diff + run: | + mrdiff=$(git status --porcelain) + if [[ $mrdiff ]]; then + echo 'ERROR: Diff found!' + echo $mrdiff + git diff + exit 1 + fi diff --git a/.gnoversion b/.gnoversion new file mode 100644 index 0000000000..ddef93042d --- /dev/null +++ b/.gnoversion @@ -0,0 +1 @@ +9786fa366f922f04e1251ec6f1df6423b4fd2bf4 diff --git a/Cargo.lock b/Cargo.lock index 4d14d11115..98c163bad7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,7 +174,7 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.2", "sha2 0.10.8", "static_assertions", "thiserror", @@ -245,6 +245,19 @@ dependencies = [ "cosmwasm-std", ] +[[package]] +name = "cw-address-list" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "schemars", + "serde", + "sylvia 0.9.3", + "thiserror", +] + [[package]] name = "cw-multi-test" version = "0.16.5" @@ -256,14 +269,34 @@ dependencies = [ "cw-storage-plus 1.2.0", "cw-utils 1.0.3", "derivative", - "itertools", + "itertools 0.10.5", "k256 0.11.6", - "prost", + "prost 0.9.0", "schemars", "serde", "thiserror", ] +[[package]] +name = "cw-multi-test" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc392a5cb7e778e3f90adbf7faa43c4db7f35b6623224b08886d796718edb875" +dependencies = [ + "anyhow", + "bech32", + "cosmwasm-std", + "cw-storage-plus 1.2.0", + "cw-utils 1.0.3", + "derivative", + "itertools 0.12.1", + "prost 0.12.6", + "schemars", + "serde", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "cw-ownable" version = "0.5.1" @@ -567,7 +600,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "sylvia", + "sylvia 0.7.1", "thiserror", ] @@ -861,6 +894,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -962,7 +1004,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "sylvia", + "sylvia 0.7.1", "thiserror", ] @@ -1048,7 +1090,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive 0.12.6", ] [[package]] @@ -1058,12 +1110,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" dependencies = [ "anyhow", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "quote" version = "1.0.37" @@ -1200,6 +1265,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.210" @@ -1319,14 +1393,33 @@ dependencies = [ "anyhow", "cosmwasm-schema", "cosmwasm-std", - "cw-multi-test", + "cw-multi-test 0.16.5", "derivative", "konst", "schemars", "serde", "serde-cw-value", - "serde-json-wasm", - "sylvia-derive", + "serde-json-wasm 0.5.2", + "sylvia-derive 0.7.1", +] + +[[package]] +name = "sylvia" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99d4ee0dde6fd6bf3f7d93d90021669e33bc4518bcc3f6c882d64c163fa79d4" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test 0.20.1", + "derivative", + "konst", + "schemars", + "serde", + "serde-cw-value", + "serde-json-wasm 1.0.1", + "sylvia-derive 0.9.3", ] [[package]] @@ -1343,6 +1436,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sylvia-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "178e7a0f1a957b2164dc0fd5dfe8c5db1e5fbc7299bffcdac7627b802114ce6f" +dependencies = [ + "convert_case", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Makefile b/Makefile index 3862dec8c4..117ed255db 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ CANDYMACHINE_REPO=teritori-nfts BUNKER_MINTER_PACKAGE=teritori-bunker-minter GO?=go GOFMT?=$(shell $(GO) env GOROOT)/bin/gofmt +CAT := $(if $(filter $(OS),Windows_NT),type,cat) COSMWASM_CONTRACTS_DIR=rust/cw-contracts INTERNAL_COSMWASM_CONTRACTS=$(wildcard $(COSMWASM_CONTRACTS_DIR)/*) @@ -82,7 +83,7 @@ generate.graphql-thegraph: go run github.com/Khan/genqlient@85e2e8dffd211c83a2be626474993ef68e44a242 go/pkg/thegraph/genqlient.yaml .PHONY: lint -lint: lint.buf lint.js +lint: lint.buf lint.js lint.rust .PHONY: lint.buf lint.buf: @@ -93,6 +94,14 @@ lint.buf: lint.js: node_modules yarn lint +.PHONY: lint.rust +lint.rust: + cargo clippy + +.PHONY: fmt.rust +fmt.rust: + cargo fmt + .PHONY: go/pkg/holagql/holaplex-schema.graphql go/pkg/holagql/holaplex-schema.graphql: rover graph introspect https://graph.65.108.73.219.nip.io/v1 > $@ @@ -395,6 +404,7 @@ bump-app-build-number: .PHONY: test.rust test.rust: + set -e ; \ for file in $(INTERNAL_COSMWASM_CONTRACTS); do \ echo "> Testing $${file}" ; \ cd $${file} ; \ @@ -404,6 +414,7 @@ test.rust: .PHONY: build.rust build.rust: + set -e ; \ for file in $(INTERNAL_COSMWASM_CONTRACTS); do \ echo "> Building $${file}" ; \ cd $${file} ; \ @@ -413,10 +424,11 @@ build.rust: .PHONY: generate.internal-contracts-clients generate.internal-contracts-clients: node_modules + set -e ; \ for indir in $(INTERNAL_COSMWASM_CONTRACTS) ; do \ echo "> Generating client for $${indir}" ; \ rm -fr $${indir}/schema ; \ - (cd $${indir} && cargo schema && cd -) || exit 1 ; \ + cd $${indir} && cargo schema && cd - ; \ pkgname="$$(basename $${indir})" ; \ outdir="$(CONTRACTS_CLIENTS_DIR)/$${pkgname}" ; \ rm -fr $${outdir} ; \ @@ -426,8 +438,8 @@ generate.internal-contracts-clients: node_modules --out $${outdir} \ --name $${pkgname} \ --no-bundle \ - || exit 1 ;\ - npx tsx packages/scripts/makeTypescriptIndex $${outdir} || exit 1 ; \ + ;\ + npx tsx packages/scripts/makeTypescriptIndex $${outdir} ; \ done .PHONY: install-gno @@ -446,7 +458,7 @@ start.gnodev-e2e: clone-gno: rm -fr gnobuild mkdir -p gnobuild - cd gnobuild && git clone https://github.com/gnolang/gno.git && cd gno && git checkout 8f800ece85a765113dfa4924da1c06f56865460c + cd gnobuild && git clone https://github.com/gnolang/gno.git && cd gno && git checkout $(shell $(CAT) .gnoversion) cp -r ./gno/p ./gnobuild/gno/examples/gno.land/p/teritori cp -r ./gno/r ./gnobuild/gno/examples/gno.land/r/teritori diff --git a/assets/icons/info.svg b/assets/icons/info.svg index 157da7b8f5..49a4431253 100644 --- a/assets/icons/info.svg +++ b/assets/icons/info.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/assets/icons/warning-triangle-orange.svg b/assets/icons/warning-triangle-orange.svg deleted file mode 100644 index d3fc2d7c81..0000000000 --- a/assets/icons/warning-triangle-orange.svg +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/assets/icons/warning-triangle.svg b/assets/icons/warning-triangle.svg index 7761721a99..9d2a2af883 100644 --- a/assets/icons/warning-triangle.svg +++ b/assets/icons/warning-triangle.svg @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file diff --git a/cypress/e2e/lib.ts b/cypress/e2e/gno/lib.ts similarity index 100% rename from cypress/e2e/lib.ts rename to cypress/e2e/gno/lib.ts diff --git a/cypress/e2e/gno/organization-creation.cy.ts b/cypress/e2e/gno/organization-creation.cy.ts new file mode 100644 index 0000000000..81bc06c404 --- /dev/null +++ b/cypress/e2e/gno/organization-creation.cy.ts @@ -0,0 +1,35 @@ +import { connectWallet, resetChain } from "./lib"; + +describe("Create an organization flow", () => { + it("works", () => { + resetChain(); + + cy.visit("http://localhost:8081/orgs?network=gno-dev", { + timeout: 300000, + }); + + connectWallet(); + + const daoName = "Test Dao"; + const handle = "testdao"; + const url = + "https://images.unsplash.com/photo-1492571350019-22de08371fd3?fm=jpg&q=60&w=3000&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"; + const description = "Test Dao description"; + + cy.contains("Create Dao").click(); + + cy.get("input[placeholder='Type organization\\'s name here']").type( + daoName, + ); + cy.get("input[placeholder='your_organization']").type(handle); + cy.get("input[placeholder='https://example.com/preview.png']").type(url); + cy.get('[data-testid="organization-description"]').type(description); + + cy.contains("Next: Configure voting").click(); + cy.contains("Next: Set tokens or members").click(); + cy.get('[data-testid="member-settings-next"]').click(); + cy.contains("Confirm & Launch the Organization").click(); + + cy.contains("Get Started", { timeout: 10000 }).should("exist"); + }); +}); diff --git a/cypress/e2e/projects-contractor.cy.ts b/cypress/e2e/gno/projects-contractor.cy.ts similarity index 100% rename from cypress/e2e/projects-contractor.cy.ts rename to cypress/e2e/gno/projects-contractor.cy.ts diff --git a/cypress/e2e/projects-funder.cy.ts b/cypress/e2e/gno/projects-funder.cy.ts similarity index 100% rename from cypress/e2e/projects-funder.cy.ts rename to cypress/e2e/gno/projects-funder.cy.ts diff --git a/cypress/e2e/upp-form.cy.ts b/cypress/e2e/gno/upp-form.cy.ts similarity index 100% rename from cypress/e2e/upp-form.cy.ts rename to cypress/e2e/gno/upp-form.cy.ts diff --git a/go/cmd/feed-clean-pinata-keys/main.go b/go/cmd/feed-clean-pinata-keys/main.go index 3e55079a08..63a7f6b281 100644 --- a/go/cmd/feed-clean-pinata-keys/main.go +++ b/go/cmd/feed-clean-pinata-keys/main.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/TERITORI/teritori-dapp/go/pkg/feed" + "github.com/TERITORI/teritori-dapp/go/pkg/pinata" "github.com/go-co-op/gocron" "github.com/peterbourgon/ff/v3" "github.com/pkg/errors" @@ -44,7 +44,7 @@ func main() { panic(errors.Wrap(err, "failed to init logger")) } - pinata := feed.NewPinataService(*pinataJWT) + pinataService := pinata.NewPinataService(*pinataJWT) schedule := gocron.NewScheduler(time.UTC) isProcessing := false @@ -55,7 +55,7 @@ func main() { isProcessing = true - keysResp, err := pinata.GetAPIKeys(0) + keysResp, err := pinataService.GetAPIKeys(0) if err != nil { logger.Error(fmt.Sprintf("failed to count keys: %s", err)) isProcessing = false @@ -64,7 +64,7 @@ func main() { logger.Info("cleaning Pinata API keys", zap.Int("count", keysResp.Count)) - remainingPages := keysResp.Count / feed.PINATA_LIMIT_PER_PAGE + remainingPages := keysResp.Count / pinata.PINATA_LIMIT_PER_PAGE totalSkip := 0 totalRemaining := 0 totalRevoked := 0 @@ -72,7 +72,7 @@ func main() { // Get all existing keys keysData := keysResp.Keys for i := 0; i < remainingPages; i++ { - respI, err := pinata.GetAPIKeys(i) + respI, err := pinataService.GetAPIKeys(i) if err != nil { logger.Error(fmt.Sprintf("failed to get API keys: %s", err)) isProcessing = false @@ -84,7 +84,7 @@ func main() { // Process each key for _, keyData := range keysData { - if keyData.Revoked || keyData.Scopes.Admin || !strings.HasPrefix(keyData.Name, feed.FEED_KEY_PREFIX) { + if keyData.Revoked || keyData.Scopes.Admin || !strings.HasPrefix(keyData.Name, pinata.FEED_KEY_PREFIX) { totalSkip++ continue } @@ -92,12 +92,12 @@ func main() { now := time.Now() diff := now.Sub(keyData.CreatedAt) - if diff.Seconds() <= feed.KEY_TTL { + if diff.Seconds() <= pinata.KEY_TTL { totalRemaining++ continue } - if err := pinata.RevokeAPIKey(keyData.Key); err != nil { + if err := pinataService.RevokeAPIKey(keyData.Key); err != nil { logger.Error(fmt.Sprintf("failed revoke API key %s: %s", keyData.Key, err)) isProcessing = false return diff --git a/go/pkg/feed/service.go b/go/pkg/feed/service.go index 1f6404f90a..6b944bae2e 100644 --- a/go/pkg/feed/service.go +++ b/go/pkg/feed/service.go @@ -10,6 +10,7 @@ import ( "github.com/TERITORI/teritori-dapp/go/internal/indexerdb" "github.com/TERITORI/teritori-dapp/go/pkg/feedpb" "github.com/TERITORI/teritori-dapp/go/pkg/networks" + "github.com/TERITORI/teritori-dapp/go/pkg/pinata" "github.com/dgraph-io/ristretto" "github.com/pkg/errors" "go.uber.org/zap" @@ -62,15 +63,15 @@ func (s *FeedService) IPFSKey(ctx context.Context, req *feedpb.IPFSKeyRequest) ( }, nil } - pinata := NewPinataService(s.conf.PinataJWT) - credential, err := pinata.GenerateAPIKey() + pinataService := pinata.NewPinataService(s.conf.PinataJWT) + credential, err := pinataService.GenerateAPIKey() if err != nil { return nil, errors.Wrap(err, "failed to generate api key") } // With normal upload flow, suppose that time between posts always > TTL => we always generate a new key // In abnormal case, time < TTL then we return the cached key, the worst case user still have 4s to make the upload request - s.cache.SetWithTTL(cacheKey, credential.JWT, 0, time.Second*(KEY_TTL-4)) + s.cache.SetWithTTL(cacheKey, credential.JWT, 0, time.Second*(pinata.KEY_TTL-4)) return &feedpb.IPFSKeyResponse{ Jwt: credential.JWT, diff --git a/go/pkg/networks/features.gen.go b/go/pkg/networks/features.gen.go index a7a133bd78..ff8362cc8f 100644 --- a/go/pkg/networks/features.gen.go +++ b/go/pkg/networks/features.gen.go @@ -103,6 +103,26 @@ func (nb *NetworkBase) GetFeatureLaunchpadERC20() (*FeatureLaunchpadERC20, error return feature.(*FeatureLaunchpadERC20), nil } +type FeatureNFTMarketplace struct { + *FeatureBase + CwAddressListContractAddress string `json:"cwAddressListContractAddress"` + CwAddressListCodeId float64 `json:"cwAddressListCodeId"` +} + +var _ Feature = &FeatureNFTMarketplace{} + +func (f FeatureNFTMarketplace) Type() FeatureType { + return FeatureTypeNFTMarketplace +} + +func (nb *NetworkBase) GetFeatureNFTMarketplace() (*FeatureNFTMarketplace, error) { + feature, err := nb.GetFeature(FeatureTypeNFTMarketplace) + if err != nil { + return nil, err + } + return feature.(*FeatureNFTMarketplace), nil +} + func UnmarshalFeature(b []byte) (Feature, error) { var base FeatureBase if err := json.Unmarshal(b, &base); err != nil { @@ -133,6 +153,12 @@ func UnmarshalFeature(b []byte) (Feature, error) { return nil, errors.Wrap(err, "failed to unmarshal feature LaunchpadERC20") } return &f, nil + case FeatureTypeNFTMarketplace: + var f FeatureNFTMarketplace + if err := json.Unmarshal(b, &f); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal feature NFTMarketplace") + } + return &f, nil } return nil, errors.Errorf("unknown feature type %s", base.Type) } diff --git a/go/pkg/feed/pinata.go b/go/pkg/pinata/pinata.go similarity index 99% rename from go/pkg/feed/pinata.go rename to go/pkg/pinata/pinata.go index 288d70c017..3d8e2ff6be 100644 --- a/go/pkg/feed/pinata.go +++ b/go/pkg/pinata/pinata.go @@ -1,4 +1,4 @@ -package feed +package pinata import ( "encoding/json" diff --git a/networks.json b/networks.json index 724d0927fe..13cc298df9 100644 --- a/networks.json +++ b/networks.json @@ -4526,6 +4526,8 @@ "profilePkgPath": "gno.land/r/demo/profile", "daoInterfacesPkgPath": "gno.land/p/teritori/dao_interfaces", "daoCorePkgPath": "gno.land/p/teritori/dao_core", + "daoUtilsPkgPath": "gno.land/p/teritori/dao_utils", + "toriPkgPath": "gno.land/r/teritori/tori", "nameServiceDefaultImage": "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", "gnowebURL": "http://127.0.0.1:8888", "txIndexerURL": "http://127.0.0.1:8546" @@ -4574,6 +4576,8 @@ "daoProposalSinglePkgPath": "gno.land/p/teritori/dao_proposal_single", "daoInterfacesPkgPath": "gno.land/p/teritori/dao_interfaces", "daoCorePkgPath": "gno.land/p/teritori/dao_core", + "daoUtilsPkgPath": "gno.land/r/teritori/dao_utils", + "toriPkgPath": "gno.land/r/teritori/tori", "profilePkgPath": "gno.land/r/demo/profile", "txIndexerURL": "https://indexer.portal-loop.gno.testnet.teritori.com" }, @@ -4628,7 +4632,9 @@ "votingGroupPkgPath": "gno.land/p/teritori/dao_voting_group_v2", "daoProposalSinglePkgPath": "gno.land/p/teritori/dao_proposal_single_v4", "daoInterfacesPkgPath": "gno.land/p/teritori/dao_interfaces_v5", + "daoUtilsPkgPath": "", "daoCorePkgPath": "gno.land/p/teritori/dao_core_v4", + "toriPkgPath": "", "gnowebURL": "https://testnet.gno.teritori.com", "faucetURL": "https://testnet.gno.teritori.com:5050/?toaddr=$addr" }, @@ -11668,6 +11674,11 @@ { "type": "CosmWasmNFTsBurner", "burnerContractAddress": "tori1qyl0j7a24amk8k8gcmvv07y2zjx7nkcwpk73js24euh64hkja6esd2p2xp" + }, + { + "type": "NFTMarketplace", + "cwAddressListCodeId": 63, + "cwAddressListContractAddress": "tori1x72plnprsjnmszylmdm3cnvu5h6u55fyf0pe02lye9p6q2ws05ps33qmft" } ], "currencies": [ diff --git a/package.json b/package.json index 05078338ab..73a2363631 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@types/leaflet": "^1.9.12", "@types/leaflet.markercluster": "^1.5.4", "@types/papaparse": "^5.3.14", + "@types/pluralize": "^0.0.33", "assert": "^2.1.0", "axios": "^1.6.2", "bech32": "^2.0.0", @@ -126,6 +127,7 @@ "osmojs": "16.9.0", "papaparse": "^5.4.1", "plausible-tracker": "^0.3.8", + "pluralize": "^8.0.0", "protobufjs": "^7.2.5", "react": "18.2.0", "react-dom": "18.2.0", diff --git a/packages/components/Section.tsx b/packages/components/Section.tsx index d2dec203a1..dccc538991 100644 --- a/packages/components/Section.tsx +++ b/packages/components/Section.tsx @@ -8,7 +8,7 @@ import { fontSemibold20 } from "../utils/style/fonts"; import { layout } from "../utils/style/layout"; export const Section: React.FC<{ - title: string; + title?: string; subtitle?: string; topRightChild?: ReactNode; children: ReactNode; @@ -36,16 +36,18 @@ export const Section: React.FC<{ }} > - - {title} - + {!!title && ( + + {title} + + )} {subtitle ? ( = ({ { caller: selectedWallet.address, send: "", - pkg_path: daoAddress, + pkg_path: "gno.land/r/" + selectedWallet, func: "ProposeJSON", args: ["0", JSON.stringify(propReq)], }, diff --git a/packages/components/inputs/AvailableNamesInput.tsx b/packages/components/inputs/AvailableNamesInput.tsx index 0bf7d1e9b6..2644a04893 100644 --- a/packages/components/inputs/AvailableNamesInput.tsx +++ b/packages/components/inputs/AvailableNamesInput.tsx @@ -9,6 +9,7 @@ import { BrandText } from "../BrandText"; import { useNSAvailability } from "@/hooks/useNSAvailability"; import { useSelectedNetworkInfo } from "@/hooks/useSelectedNetwork"; import { NetworkInfo, NetworkKind, getNetwork } from "@/networks"; +import { LETTERS_REGEXP } from "@/utils/regex"; import { neutral17, neutral33, @@ -154,7 +155,7 @@ export const AvailableNamesInput = ({ onPressEnter={onPressEnter} value={value} rules={{ required: true }} - regexp={new RegExp(/^[a-zA-Z]+$/)} + regexp={new RegExp(LETTERS_REGEXP)} style={style} readOnly={readOnly} boxMainContainerStyle={{ diff --git a/packages/components/inputs/FileUploaderSmall/FileUploaderSmall.web.tsx b/packages/components/inputs/FileUploaderSmall/FileUploaderSmall.web.tsx index 6cda44ab67..29288413be 100644 --- a/packages/components/inputs/FileUploaderSmall/FileUploaderSmall.web.tsx +++ b/packages/components/inputs/FileUploaderSmall/FileUploaderSmall.web.tsx @@ -1,3 +1,4 @@ +import pluralize from "pluralize"; import React, { forwardRef, SyntheticEvent, @@ -27,7 +28,6 @@ import { DeleteButton } from "@/components/FilePreview/DeleteButton"; import { OptimizedImage } from "@/components/OptimizedImage"; import { CustomPressable } from "@/components/buttons/CustomPressable"; import { SpacerRow } from "@/components/spacer"; -import { pluralOrNot } from "@/utils/text"; import { LocalFileData } from "@/utils/types/files"; export const FileUploaderSmall = forwardRef< @@ -193,8 +193,7 @@ export const FileUploaderSmall = forwardRef< : !multiple && !filesCount ? "Select file" : multiple && filesCount - ? filesCount + - ` ${pluralOrNot("file", filesCount)} selected` + ? `${pluralize("file", filesCount, true)} selected` : multiple && !filesCount ? "Select files" : ""} diff --git a/packages/components/inputs/TextInputCustomBorder.tsx b/packages/components/inputs/TextInputCustomBorder.tsx index df22703e86..82dd4a072e 100644 --- a/packages/components/inputs/TextInputCustomBorder.tsx +++ b/packages/components/inputs/TextInputCustomBorder.tsx @@ -10,6 +10,8 @@ import { import { neutral22, neutral33 } from "../../utils/style/colors"; import { SVG } from "../SVG"; +import { NUMBERS_REGEXP } from "@/utils/regex"; + // A custom TextInput. You can add children (Ex: An icon or a small container) export const TextInputCustomBorder: React.FC<{ value: string; @@ -39,7 +41,7 @@ export const TextInputCustomBorder: React.FC<{ if (onlyNumbers) { const withoutCommaValue = thousandSeparatedToNumber(value); // Set value only if fully number - const reg = new RegExp(/^\d+$/); + const reg = new RegExp(NUMBERS_REGEXP); if (reg.test(withoutCommaValue)) { onChangeText(numberWithThousandsSeparator(withoutCommaValue)); } diff --git a/packages/components/socialFeed/SocialActions/FlagModal.tsx b/packages/components/socialFeed/SocialActions/FlagModal.tsx index 9c3d46e4c9..128a690116 100644 --- a/packages/components/socialFeed/SocialActions/FlagModal.tsx +++ b/packages/components/socialFeed/SocialActions/FlagModal.tsx @@ -6,8 +6,11 @@ import { useSelectedNetworkInfo } from "../../../hooks/useSelectedNetwork"; import useSelectedWallet from "../../../hooks/useSelectedWallet"; import { mustGetGnoNetwork } from "../../../networks"; import { TERITORI_FEED_ID } from "../../../utils/feed/constants"; -import { adenaDoContract } from "../../../utils/gno"; -import { neutral77, neutral33 } from "../../../utils/style/colors"; +import { + adenaDoContract, + AdenaDoContractMessageType, +} from "../../../utils/gno"; +import { neutral33, neutral77 } from "../../../utils/style/colors"; import { fontSemibold14, fontSemibold16 } from "../../../utils/style/fonts"; import { BrandText } from "../../BrandText"; import FlexRow from "../../FlexRow"; @@ -53,7 +56,7 @@ export const FlagModal: React.FC = ({ try { await adenaDoContract( selectedNetworkId || "", - [{ type: "/vm.m_call", value: vmCall }], + [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], { gasWanted: 2_000_000, }, diff --git a/packages/components/socialFeed/SocialActions/TipModal.tsx b/packages/components/socialFeed/SocialActions/TipModal.tsx index e359b675cf..39f2d14bea 100644 --- a/packages/components/socialFeed/SocialActions/TipModal.tsx +++ b/packages/components/socialFeed/SocialActions/TipModal.tsx @@ -11,14 +11,17 @@ import { useBalances } from "../../../hooks/useBalances"; import { useSelectedNetworkInfo } from "../../../hooks/useSelectedNetwork"; import useSelectedWallet from "../../../hooks/useSelectedWallet"; import { - NetworkKind, getStakingCurrency, keplrCurrencyFromNativeCurrencyInfo, + NetworkKind, } from "../../../networks"; import { prettyPrice } from "../../../utils/coins"; import { defaultSocialFeedFee } from "../../../utils/fee"; import { TERITORI_FEED_ID } from "../../../utils/feed/constants"; -import { adenaDoContract } from "../../../utils/gno"; +import { + adenaDoContract, + AdenaDoContractMessageType, +} from "../../../utils/gno"; import { neutral77, primaryColor } from "../../../utils/style/colors"; import { fontSemibold13, fontSemibold14 } from "../../../utils/style/fonts"; import { BrandText } from "../../BrandText"; @@ -95,7 +98,7 @@ export const TipModal: React.FC<{ try { await adenaDoContract( selectedNetworkId || "", - [{ type: "/vm.m_call", value: vmCall }], + [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], { gasWanted: 1_000_000, }, diff --git a/packages/components/socialFeed/modals/FlagConfirmModal.tsx b/packages/components/socialFeed/modals/FlagConfirmModal.tsx index 230d0680bc..5a6d576825 100644 --- a/packages/components/socialFeed/modals/FlagConfirmModal.tsx +++ b/packages/components/socialFeed/modals/FlagConfirmModal.tsx @@ -7,7 +7,10 @@ import { useFeedbacks } from "../../../context/FeedbacksProvider"; import { useSelectedNetworkId } from "../../../hooks/useSelectedNetwork"; import useSelectedWallet from "../../../hooks/useSelectedWallet"; import { mustGetGnoNetwork } from "../../../networks"; -import { adenaDoContract } from "../../../utils/gno"; +import { + adenaDoContract, + AdenaDoContractMessageType, +} from "../../../utils/gno"; import { GnoDAOVoteRequest } from "../../../utils/gnodao/messages"; import { neutral77 } from "../../../utils/style/colors"; import { @@ -74,7 +77,7 @@ export const FlagConfirmModal: React.FC = ({ const txHash = await adenaDoContract( selectedNetworkId, - [{ type: "/vm.m_call", value: vmCall }], + [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], { gasWanted: 2_000_000, }, diff --git a/packages/components/table/TableWrapper.tsx b/packages/components/table/TableWrapper.tsx index b0e5011ff4..55040c6c80 100644 --- a/packages/components/table/TableWrapper.tsx +++ b/packages/components/table/TableWrapper.tsx @@ -6,7 +6,7 @@ import { SpacerColumn } from "@/components/spacer"; export const TableWrapper: FC<{ children: ReactNode; - paginationProps: PaginationProps; + paginationProps?: PaginationProps; showPagination?: boolean; horizontalScrollBreakpoint?: number; }> = ({ @@ -31,7 +31,7 @@ export const TableWrapper: FC<{ - {showPagination && ( + {showPagination && paginationProps && ( <> diff --git a/packages/components/teritoriNameService/FindAName.tsx b/packages/components/teritoriNameService/FindAName.tsx index d2db86b210..d8953c066c 100644 --- a/packages/components/teritoriNameService/FindAName.tsx +++ b/packages/components/teritoriNameService/FindAName.tsx @@ -6,6 +6,8 @@ import { NameStatus } from "./NameStatus"; import { neutral17 } from "../../utils/style/colors"; import { TextInputCustom } from "../inputs/TextInputCustom"; +import { LETTERS_REGEXP } from "@/utils/regex"; + // TODO: Maybe it can be a screen that is called in Register and Explore flow... TNSRegisterScreen.tsx and TNSExploreScreen.tsx have duplicated code // A title + Name status (minted or available) + NFT card + optional buttons @@ -37,7 +39,7 @@ export const FindAName: React.FC<{ style={{ marginBottom: 12 }} onChangeText={setName} value={name || ""} - regexp={new RegExp(/^[a-zA-Z]+$/)} + regexp={new RegExp(LETTERS_REGEXP)} squaresBackgroundColor={neutral17} /> diff --git a/packages/components/teritoriNameService/NameDataForm.tsx b/packages/components/teritoriNameService/NameDataForm.tsx index 8d0b1b5040..f83c0f31f5 100644 --- a/packages/components/teritoriNameService/NameDataForm.tsx +++ b/packages/components/teritoriNameService/NameDataForm.tsx @@ -10,6 +10,8 @@ import { ExternalLink } from "../ExternalLink"; import { PrimaryButton } from "../buttons/PrimaryButton"; import { TextInputCustom } from "../inputs/TextInputCustom"; +import { LETTERS_REGEXP } from "@/utils/regex"; + export const NameDataForm: React.FC<{ isMintPath?: boolean; btnLabel: string; @@ -122,7 +124,7 @@ export const NameDataForm: React.FC<{ placeHolder="Type name here" value={publicName} onChangeText={setPublicName} - regexp={new RegExp(/^[a-zA-Z]+$/)} + regexp={new RegExp(LETTERS_REGEXP)} squaresBackgroundColor={neutral17} /> diff --git a/packages/components/user/modals/EditProfileModal.tsx b/packages/components/user/modals/EditProfileModal.tsx index 8c838d8bcb..3b5fa3eae4 100644 --- a/packages/components/user/modals/EditProfileModal.tsx +++ b/packages/components/user/modals/EditProfileModal.tsx @@ -6,8 +6,8 @@ import React, { useMemo } from "react"; import { View } from "react-native"; import { - ExecuteMsg as TNSExecuteMsg, Metadata, + ExecuteMsg as TNSExecuteMsg, } from "../../../contracts-clients/teritori-name-service/TeritoriNameService.types"; import { EditProfileForm } from "../forms/EditProfileForm"; import { TNSModalCommonProps } from "../types"; @@ -22,7 +22,12 @@ import { useNSUserInfo } from "@/hooks/useNSUserInfo"; import { useRunOrProposeTransaction } from "@/hooks/useRunOrProposeTransaction"; import useSelectedWallet from "@/hooks/useSelectedWallet"; import { getNetwork, NetworkKind, UserKind } from "@/networks"; -import { adenaDoContract, AdenaDoContractMessage, VmCall } from "@/utils/gno"; +import { + adenaDoContract, + AdenaDoContractMessage, + AdenaDoContractMessageType, + VmCall, +} from "@/utils/gno"; import { neutral17, neutral77 } from "@/utils/style/colors"; import { fontMedium16 } from "@/utils/style/fonts"; import { EMPTY_PROFILE, ProfileData } from "@/utils/upp"; @@ -183,7 +188,7 @@ export const EditProfileModal: React.FC = ({ func: "Register", args: ["", usernameValue, ""], }; - msgs.push({ type: "/vm.m_call", value: vmCall }); + msgs.push({ type: AdenaDoContractMessageType.CALL, value: vmCall }); } // FIXME: the contract supports only update data one by one @@ -242,7 +247,7 @@ export const EditProfileModal: React.FC = ({ args: [field, val], }; - msgs.push({ type: "/vm.m_call", value: vmCall }); + msgs.push({ type: AdenaDoContractMessageType.CALL, value: vmCall }); } if (msgs.length === 0) { diff --git a/packages/context/WalletsProvider/gnotest/index.tsx b/packages/context/WalletsProvider/gnotest/index.tsx index 89eb65347c..04ebacd001 100644 --- a/packages/context/WalletsProvider/gnotest/index.tsx +++ b/packages/context/WalletsProvider/gnotest/index.tsx @@ -1,10 +1,12 @@ import { GnoJSONRPCProvider, GnoWallet, + MemFile, + MemPackage, MsgSend, decodeTxMessages, } from "@gnolang/gno-js-client"; -import { TxFee, TransactionEndpoint, Tx } from "@gnolang/tm2-js-client"; +import { TransactionEndpoint, Tx, TxFee } from "@gnolang/tm2-js-client"; import Long from "long"; import React from "react"; import { Pressable } from "react-native"; @@ -16,7 +18,11 @@ import { getUserId } from "@/networks"; import { gnoDevNetwork } from "@/networks/gno-dev"; import { setSelectedWalletId } from "@/store/slices/settings"; import { useAppDispatch } from "@/store/store"; -import { RequestDocontractMessage } from "@/utils/gno"; +import { + AdenaDoContractMessage, + AdenaDoContractMessageType, + RequestDocontractMessage, +} from "@/utils/gno"; import { WalletProvider } from "@/utils/walletProvider"; type UseGnotestResult = [true, boolean, Wallet[]] | [false, boolean, undefined]; @@ -268,7 +274,10 @@ const setupAdenaMock = () => { throw new Error("Wallet not connected"); } const msg = req.messages[0]; - if (msg.type !== "/vm.m_call") { + if ( + msg.type !== AdenaDoContractMessageType.CALL && + msg.type !== AdenaDoContractMessageType.ADD_PKG + ) { throw new Error("Unsupported message type: " + msg.type); } const txFee: TxFee = { @@ -283,14 +292,19 @@ const setupAdenaMock = () => { console.log("docontract sendAmount", sendAmount); sendMap.set("ugnot", +sendAmount); } - const res = await state.wallet.callMethod( - msg.value.pkg_path, - msg.value.func, - msg.value.args, - TransactionEndpoint.BROADCAST_TX_COMMIT, - sendMap, - txFee, - ); + let res; + if (msg.type === AdenaDoContractMessageType.CALL) { + res = await state.wallet.callMethod( + msg.value.pkg_path, + msg.value.func, + msg.value.args, + TransactionEndpoint.BROADCAST_TX_COMMIT, + sendMap, + txFee, + ); + } else { + res = await deployPackage(state.wallet, msg, sendMap, txFee); + } return { status: "success", data: { @@ -313,6 +327,29 @@ const setupAdenaMock = () => { }; }; +const deployPackage = async ( + wallet: GnoWallet, + msg: AdenaDoContractMessage, + funds: Map | undefined, + txFee: TxFee, +) => { + const files: MemFile[] = msg.value.package.files; + + const pkg: MemPackage = { + name: msg.value.package.name, + path: msg.value.package.path, + files, + }; + + const tx = await wallet.deployPackage( + pkg, + TransactionEndpoint.BROADCAST_TX_COMMIT, + funds, + txFee, + ); + return tx; +}; + export const useGnotest: () => UseGnotestResult = () => { const { wallet, address } = useGnotestStore(); const wallets: Wallet[] = []; diff --git a/packages/contracts-clients/cw-address-list/CwAddressList.client.ts b/packages/contracts-clients/cw-address-list/CwAddressList.client.ts index c26bdb36d7..4ce4e17cc7 100644 --- a/packages/contracts-clients/cw-address-list/CwAddressList.client.ts +++ b/packages/contracts-clients/cw-address-list/CwAddressList.client.ts @@ -1,5 +1,5 @@ /** -* This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. +* This file was automatically generated by @cosmwasm/ts-codegen@0.35.7. * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, * and run the @cosmwasm/ts-codegen generate command to regenerate this file. */ diff --git a/packages/contracts-clients/cw-address-list/CwAddressList.types.ts b/packages/contracts-clients/cw-address-list/CwAddressList.types.ts index ab0bb3c8e7..da8d65f259 100644 --- a/packages/contracts-clients/cw-address-list/CwAddressList.types.ts +++ b/packages/contracts-clients/cw-address-list/CwAddressList.types.ts @@ -1,5 +1,5 @@ /** -* This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. +* This file was automatically generated by @cosmwasm/ts-codegen@0.35.7. * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, * and run the @cosmwasm/ts-codegen generate command to regenerate this file. */ diff --git a/packages/contracts-clients/cw-address-list/index.ts b/packages/contracts-clients/cw-address-list/index.ts new file mode 100644 index 0000000000..6005786b94 --- /dev/null +++ b/packages/contracts-clients/cw-address-list/index.ts @@ -0,0 +1,2 @@ +export * from "./CwAddressList.client"; +export * from "./CwAddressList.types"; \ No newline at end of file diff --git a/packages/hooks/feed/useFeedPosting.ts b/packages/hooks/feed/useFeedPosting.ts index 0139bc52ca..40376b9e24 100644 --- a/packages/hooks/feed/useFeedPosting.ts +++ b/packages/hooks/feed/useFeedPosting.ts @@ -13,16 +13,16 @@ import { useBalances } from "../useBalances"; import { signingSocialFeedClient } from "@/client-creators/socialFeedClient"; import { - parseUserId, getStakingCurrency, mustGetCosmosNetwork, NetworkKind, + parseUserId, } from "@/networks"; import { prettyPrice } from "@/utils/coins"; import { defaultSocialFeedFee } from "@/utils/fee"; import { TERITORI_FEED_ID } from "@/utils/feed/constants"; -import { FeedPostingStepId, feedPostingStep } from "@/utils/feed/posting"; -import { adenaDoContract } from "@/utils/gno"; +import { feedPostingStep, FeedPostingStepId } from "@/utils/feed/posting"; +import { adenaDoContract, AdenaDoContractMessageType } from "@/utils/gno"; import { PostCategory } from "@/utils/types/feed"; export const useFeedPosting = ( @@ -124,7 +124,7 @@ export const useFeedPosting = ( const txHash = await adenaDoContract( network.id, - [{ type: "/vm.m_call", value: vmCall }], + [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], { gasWanted: 2_000_000 }, ); diff --git a/packages/hooks/feed/useSocialReactions.ts b/packages/hooks/feed/useSocialReactions.ts index af9ab02ac9..3dc42f3c6b 100644 --- a/packages/hooks/feed/useSocialReactions.ts +++ b/packages/hooks/feed/useSocialReactions.ts @@ -15,7 +15,7 @@ import { NetworkKind, } from "@/networks"; import { TERITORI_FEED_ID } from "@/utils/feed/constants"; -import { adenaDoContract } from "@/utils/gno"; +import { adenaDoContract, AdenaDoContractMessageType } from "@/utils/gno"; import { DISLIKE_EMOJI, getUpdatedReactions, @@ -77,7 +77,7 @@ export const useSocialReactions = ({ }; const txHash = await adenaDoContract( post.networkId, - [{ type: "/vm.m_call", value: vmCall }], + [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], { gasWanted: 2_000_000, }, diff --git a/packages/networks/features.ts b/packages/networks/features.ts index 56fdd0ee27..9f05394953 100644 --- a/packages/networks/features.ts +++ b/packages/networks/features.ts @@ -19,6 +19,16 @@ export enum NetworkFeature { CosmWasmNFTsBurner = "CosmWasmNFTsBurner", } +// Marketplace + +const zodNFTMarketplace = z.object({ + type: z.literal(NetworkFeature.NFTMarketplace), + cwAddressListContractAddress: z.string(), + cwAddressListCodeId: z.number(), +}); + +export type NFTMarketplace = z.infer; + // CosmWasm Premium Feed const zodCosmWasmPremiumFeed = z.object({ @@ -72,6 +82,7 @@ export const allFeatureObjects = [ zodCosmWasmNFTsBurner, zodGnoProjectManager, zodLaunchpadERC20, + zodNFTMarketplace, ]; export type NetworkFeatureObject = @@ -79,4 +90,5 @@ export type NetworkFeatureObject = | CosmWasmSocialFeed | CosmWasmNFTsBurner | GnoProjectManager - | LaunchpadERC20; + | LaunchpadERC20 + | NFTMarketplace; diff --git a/packages/networks/gno-dev/index.ts b/packages/networks/gno-dev/index.ts index b7349f907f..8f1e2e06f9 100644 --- a/packages/networks/gno-dev/index.ts +++ b/packages/networks/gno-dev/index.ts @@ -47,9 +47,10 @@ export const gnoDevNetwork: GnoNetworkInfo = { votingGroupPkgPath: "gno.land/p/teritori/dao_voting_group", daoProposalSinglePkgPath: "gno.land/p/teritori/dao_proposal_single", profilePkgPath: "gno.land/r/demo/profile", - daoInterfacesPkgPath: "gno.land/p/teritori/dao_interfaces", daoCorePkgPath: "gno.land/p/teritori/dao_core", + daoUtilsPkgPath: "gno.land/p/teritori/dao_utils", + toriPkgPath: "gno.land/r/teritori/tori", nameServiceDefaultImage: "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", gnowebURL: "http://127.0.0.1:8888", diff --git a/packages/networks/gno-portal/index.ts b/packages/networks/gno-portal/index.ts index 5409578d13..a2fa0d2dce 100644 --- a/packages/networks/gno-portal/index.ts +++ b/packages/networks/gno-portal/index.ts @@ -38,6 +38,8 @@ export const gnoPortalNetwork: GnoNetworkInfo = { daoProposalSinglePkgPath: "gno.land/p/teritori/dao_proposal_single", daoInterfacesPkgPath: "gno.land/p/teritori/dao_interfaces", daoCorePkgPath: "gno.land/p/teritori/dao_core", + daoUtilsPkgPath: "gno.land/r/teritori/dao_utils", + toriPkgPath: "gno.land/r/teritori/tori", profilePkgPath: "gno.land/r/demo/profile", txIndexerURL: "https://indexer.portal-loop.gno.testnet.teritori.com", }; diff --git a/packages/networks/gno-teritori/index.ts b/packages/networks/gno-teritori/index.ts index ff21695a40..b511fd9053 100644 --- a/packages/networks/gno-teritori/index.ts +++ b/packages/networks/gno-teritori/index.ts @@ -42,7 +42,9 @@ export const gnoTeritoriNetwork: GnoNetworkInfo = { votingGroupPkgPath: "gno.land/p/teritori/dao_voting_group_v2", daoProposalSinglePkgPath: "gno.land/p/teritori/dao_proposal_single_v4", daoInterfacesPkgPath: "gno.land/p/teritori/dao_interfaces_v5", + daoUtilsPkgPath: "", //TODO: fill with the correct path daoCorePkgPath: "gno.land/p/teritori/dao_core_v4", + toriPkgPath: "", //TODO: fill with the correct path gnowebURL: "https://testnet.gno.teritori.com", faucetURL: "https://testnet.gno.teritori.com:5050/?toaddr=$addr", }; diff --git a/packages/networks/teritori-testnet/index.ts b/packages/networks/teritori-testnet/index.ts index 8ea49b815e..e0ed91553c 100644 --- a/packages/networks/teritori-testnet/index.ts +++ b/packages/networks/teritori-testnet/index.ts @@ -3,6 +3,7 @@ import { CosmWasmNFTsBurner, CosmWasmPremiumFeed, NetworkFeature, + NFTMarketplace, } from "../features"; import { CosmosNetworkInfo, NetworkKind } from "../types"; @@ -21,6 +22,13 @@ const nftsBurnerFeature: CosmWasmNFTsBurner = { "tori1qyl0j7a24amk8k8gcmvv07y2zjx7nkcwpk73js24euh64hkja6esd2p2xp", }; +const nftMarketplace: NFTMarketplace = { + type: NetworkFeature.NFTMarketplace, + cwAddressListCodeId: 63, + cwAddressListContractAddress: + "tori1x72plnprsjnmszylmdm3cnvu5h6u55fyf0pe02lye9p6q2ws05ps33qmft", +}; + const riotContractAddressGen0 = "tori1r8raaqul4j05qtn0t05603mgquxfl8e9p7kcf7smwzcv2hc5rrlq0vket0"; const riotContractAddressGen1 = ""; @@ -45,7 +53,7 @@ export const teritoriTestnetNetwork: CosmosNetworkInfo = { NetworkFeature.NFTMarketplaceLeaderboard, NetworkFeature.CosmWasmNFTsBurner, ], - featureObjects: [premiumFeedFeature, nftsBurnerFeature], + featureObjects: [premiumFeedFeature, nftsBurnerFeature, nftMarketplace], currencies: teritoriTestnetCurrencies, txExplorer: "https://explorer.teritori.com/teritori-testnet/tx/$hash", accountExplorer: diff --git a/packages/networks/types.ts b/packages/networks/types.ts index a591787572..6349851cdb 100644 --- a/packages/networks/types.ts +++ b/packages/networks/types.ts @@ -72,6 +72,8 @@ export type CosmosNetworkInfo = NetworkInfoBase & { daoVotingCw4CodeId?: number; cwAdminFactoryContractAddress?: string; coreDAOAddress?: string; + cwAddressListContractAddress?: string; + cwAddressListCodeId?: number; }; export type EthereumNetworkInfo = NetworkInfoBase & { @@ -117,6 +119,8 @@ export type GnoNetworkInfo = NetworkInfoBase & { daoInterfacesPkgPath?: string; daoCorePkgPath?: string; groupsPkgPath?: string; + daoUtilsPkgPath?: string; + toriPkgPath?: string; profilePkgPath?: string; faucetURL?: string; txIndexerURL?: string; diff --git a/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx b/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx index c7e5e8dc04..2b82e3faa4 100644 --- a/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx +++ b/packages/screens/FeedNewArticle/FeedNewArticleScreen.tsx @@ -1,3 +1,4 @@ +import pluralize from "pluralize"; import React, { useEffect, useRef, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { ScrollView, View } from "react-native"; @@ -45,7 +46,6 @@ import { } from "@/utils/style/colors"; import { fontSemibold13, fontSemibold20 } from "@/utils/style/fonts"; import { layout, screenContentMaxWidth } from "@/utils/style/layout"; -import { pluralOrNot } from "@/utils/text"; import { CustomLatLngExpression, NewArticleFormValues, @@ -291,7 +291,7 @@ export const FeedNewArticleScreen: ScreenFC<"FeedNewArticle"> = () => { ]} > {freePostCount - ? `You have ${freePostCount} free ${pluralOrNot( + ? `You have ${freePostCount} free ${pluralize( "Article", freePostCount, )} left` diff --git a/packages/screens/Launchpad/LaunchpadApply/LaunchpadApplyScreen.tsx b/packages/screens/Launchpad/LaunchpadApply/LaunchpadApplyScreen.tsx index 2df6c998b0..3236178dae 100644 --- a/packages/screens/Launchpad/LaunchpadApply/LaunchpadApplyScreen.tsx +++ b/packages/screens/Launchpad/LaunchpadApply/LaunchpadApplyScreen.tsx @@ -1,7 +1,7 @@ import React from "react"; import { StyleSheet, View } from "react-native"; -import { LaunchpadBanner } from "../components/LaunchpadBanner"; +import { LaunchpadBanner } from "./LaunchpadCreate/components/LaunchpadBanner"; import { BrandText } from "@/components/BrandText"; import { ScreenContainer } from "@/components/ScreenContainer"; diff --git a/packages/screens/Launchpad/components/LaunchpadBanner.tsx b/packages/screens/Launchpad/LaunchpadApply/LaunchpadCreate/components/LaunchpadBanner.tsx similarity index 89% rename from packages/screens/Launchpad/components/LaunchpadBanner.tsx rename to packages/screens/Launchpad/LaunchpadApply/LaunchpadCreate/components/LaunchpadBanner.tsx index c6beedf612..2dbb2263ac 100644 --- a/packages/screens/Launchpad/components/LaunchpadBanner.tsx +++ b/packages/screens/Launchpad/LaunchpadApply/LaunchpadCreate/components/LaunchpadBanner.tsx @@ -1,9 +1,8 @@ import React from "react"; import { Image, View } from "react-native"; -import LaunchpadBannerImage from "../../../../assets/banners/launchpad.jpg"; -import LogoSimpleSvg from "../../../../assets/icons/logo-simple.svg"; - +import LaunchpadBannerImage from "@/assets/banners/launchpad.jpg"; +import LogoSimpleSvg from "@/assets/icons/logo-simple.svg"; import { BrandText } from "@/components/BrandText"; import { SVG } from "@/components/SVG"; import { SpacerColumn } from "@/components/spacer"; diff --git a/packages/screens/Launchpad/MintCollectionScreen.tsx b/packages/screens/Launchpad/MintCollectionScreen.tsx index 4ae81ab245..525b131f43 100644 --- a/packages/screens/Launchpad/MintCollectionScreen.tsx +++ b/packages/screens/Launchpad/MintCollectionScreen.tsx @@ -15,6 +15,7 @@ import { import ConfettiCannon from "react-native-confetti-cannon"; import CountDown from "react-native-countdown-component"; +import { NUMBERS_REGEXP } from "./../../utils/regex"; import balanceSVG from "../../../assets/icons/balance.svg"; import minusSVG from "../../../assets/icons/minus.svg"; import plusSVG from "../../../assets/icons/plus.svg"; @@ -83,7 +84,7 @@ import { import { layout } from "@/utils/style/layout"; const maxImageSize = 532; -const cardsHalfGap = 6; +const cardsHalfGap = layout.spacing_x0_75; const sleep = (duration: number) => new Promise((resolve) => setTimeout(resolve, duration)); @@ -131,7 +132,7 @@ export const MintCollectionScreen: ScreenFC<"MintCollection"> = ({ const mintButtonDisabled = minted; const updateTotalBulkMint = (newTotalBulkMint: number | string) => { - const numOnlyRegexp = new RegExp(/^\d+$/); + const numOnlyRegexp = new RegExp(NUMBERS_REGEXP); if (!numOnlyRegexp.test("" + newTotalBulkMint)) { return; } @@ -348,7 +349,13 @@ export const MintCollectionScreen: ScreenFC<"MintCollection"> = ({ if (notFound) { return ( - + Collection not found @@ -376,9 +383,11 @@ export const MintCollectionScreen: ScreenFC<"MintCollection"> = ({ margin: layout.spacing_x2, }} > - {info.name} + + {info.name} + - + = ({ margin: layout.spacing_x2, }} > - + {info.image ? ( = ({ justifyContent: "center", }} > - + )} - + Activity {info.mintStarted ? ( @@ -767,12 +781,15 @@ const AttributesCard: React.FC<{ height={62} mainContainerStyle={{ alignItems: "flex-start", - paddingHorizontal: 12, - paddingVertical: 14, + paddingHorizontal: layout.spacing_x1_5, + paddingVertical: layout.spacing_x1_75, }} > {label} @@ -814,7 +831,7 @@ const PresaleActivy: React.FC<{ Whitelist @@ -841,7 +861,7 @@ const PresaleActivy: React.FC<{ borderRadius: 999, width: 2, height: 2, - marginHorizontal: 12, + marginHorizontal: layout.spacing_x1_5, backgroundColor: neutral77, }} /> @@ -849,20 +869,29 @@ const PresaleActivy: React.FC<{ {maxPerAddress && maxPerAddress !== "0" ? ( <> Max {maxPerAddress} Token ) : ( Unlimited diff --git a/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx b/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx index f8c1a3c15a..9b462a541e 100644 --- a/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx +++ b/packages/screens/LaunchpadERC20/component/LaunchpadERC20AirdropsTable.tsx @@ -69,17 +69,7 @@ export const AirdropsTable: React.FC = ({ networkId }) => { > Latest ERC20 Airdrops Created - {}, - onChangePage: () => {}, - }} - horizontalScrollBreakpoint={breakpointM} - > + {airdrops && ( = ({ networkId }) => { > Latest ERC20 Sales Created - {}, - onChangePage: () => {}, - }} - horizontalScrollBreakpoint={breakpointM} - > + {sales && ( = ({ networkId }) => { > Latest ERC20 Tokens Created - {}, - onChangePage: () => {}, - }} - horizontalScrollBreakpoint={breakpointM} - > + {tokens && ( = React.memo(({ networkId, timePeriodHours }) => { - const [pageIndex, setPageIndex] = useState(0); - const [itemsPerPage, setItemsPerPage] = useState(100); const { data: leaderboard } = useMarketplaceLeaderboard( networkId, timePeriodHours, ); - // NOTE: we only show the first 100 items, because getting the total count to properly find maxPage will slow the query down + // NOTE: we only show the first 100 items (limit from backend), because getting the total count to properly find maxPage will slow the query down // see https://stackoverflow.com/questions/28888375/run-a-query-with-a-limit-offset-and-also-get-the-total-number-of-rows - const numItems = leaderboard?.length || 0; - const maxPage = Math.max(Math.ceil(numItems / itemsPerPage), 1); - return ( - + = ({ // isAsterickSign multiline numberOfLines={3} + testID="organization-description" /> diff --git a/packages/screens/Organizations/components/MemberSettingsSection.tsx b/packages/screens/Organizations/components/MemberSettingsSection.tsx index 03d724349f..6c44dc3d40 100644 --- a/packages/screens/Organizations/components/MemberSettingsSection.tsx +++ b/packages/screens/Organizations/components/MemberSettingsSection.tsx @@ -112,6 +112,7 @@ export const MemberSettingsSection: React.FC = ({ size="M" text={`Next: ${ORGANIZATION_DEPLOYER_STEPS[3]}`} onPress={handleSubmit(onSubmit)} + testID="member-settings-next" /> diff --git a/packages/screens/Stake/components/WarningBox.tsx b/packages/screens/Stake/components/WarningBox.tsx index 86272253b0..4626ed3adf 100644 --- a/packages/screens/Stake/components/WarningBox.tsx +++ b/packages/screens/Stake/components/WarningBox.tsx @@ -1,8 +1,7 @@ import React from "react"; -import { StyleSheet, View, useWindowDimensions } from "react-native"; - -import warningTriangleSVG from "../../../../assets/icons/warning-triangle.svg"; +import { View, useWindowDimensions } from "react-native"; +import warningTriangleSVG from "@/assets/icons/warning-triangle.svg"; import { BrandText } from "@/components/BrandText"; import { SVG } from "@/components/SVG"; import { SpacerColumn, SpacerRow } from "@/components/spacer"; @@ -17,11 +16,22 @@ export const WarningBox: React.FC<{ const { width: windowWidth } = useWindowDimensions(); const maxWidth = windowWidth - 20 >= 490 ? 350 : 300; return ( - + @@ -30,28 +40,10 @@ export const WarningBox: React.FC<{ {title} - + {description} ); }; - -// FIXME: remove StyleSheet.create -// eslint-disable-next-line no-restricted-syntax -const styles = StyleSheet.create({ - stakeWarningContainer: { - flexDirection: "row", - alignItems: "center", - borderWidth: 1, - borderColor: errorColor, - borderRadius: layout.borderRadius * 0.65, - paddingVertical: layout.spacing_x1_5, - paddingHorizontal: layout.spacing_x3, - }, - alternateText: { - ...StyleSheet.flatten(fontSemibold12), - color: neutral77, - }, -}); diff --git a/packages/screens/Swap/components/SwapView/SwapSettings.tsx b/packages/screens/Swap/components/SwapView/SwapSettings.tsx index 74769d3c1b..c6bba344a4 100644 --- a/packages/screens/Swap/components/SwapView/SwapSettings.tsx +++ b/packages/screens/Swap/components/SwapView/SwapSettings.tsx @@ -8,15 +8,16 @@ import React, { } from "react"; import { Pressable, View } from "react-native"; -import infoSVG from "../../../../../assets/icons/info.svg"; import { FadeInView } from "../FadeInView"; +import infoSVG from "@/assets/icons/info.svg"; import { BrandText } from "@/components/BrandText"; import { SVG } from "@/components/SVG"; -import { LegacyTertiaryBox } from "@/components/boxes/LegacyTertiaryBox"; +import { Box } from "@/components/boxes/Box"; import { CustomPressable } from "@/components/buttons/CustomPressable"; import { TextInputCustom } from "@/components/inputs/TextInputCustom"; import { + neutral33, neutral77, neutralA3, primaryColor, @@ -131,13 +132,13 @@ export const SwapSettings: React.FC<{ if (settingsOpened) return ( - Transaction Settings @@ -161,17 +162,17 @@ export const SwapSettings: React.FC<{ onHoverIn={() => setInfoVisible(true)} onHoverOut={() => setInfoVisible(false)} > - + - {slippageItems.map((item, index) => ( - + {/*====== Info box */} {infoVisible && ( - Your transaction will revert if the price changes {"\n"} unfavorably by more than this percentage. - + )} - + ); return <>; diff --git a/packages/scripts/codegen/generateGoNetworkFeatures.ts b/packages/scripts/codegen/generateGoNetworkFeatures.ts index b5a5718d2c..60897ffc80 100644 --- a/packages/scripts/codegen/generateGoNetworkFeatures.ts +++ b/packages/scripts/codegen/generateGoNetworkFeatures.ts @@ -1,4 +1,4 @@ -import { ZodString } from "zod"; +import { ZodNumber, ZodString } from "zod"; import { NetworkFeature, allFeatureObjects } from "@/networks/features"; import { capitalize } from "@/utils/text"; @@ -42,6 +42,8 @@ const zodToGoType = (zodType: unknown) => { switch (true) { case zodType instanceof ZodString: return "string"; + case zodType instanceof ZodNumber: + return "float64"; default: throw new Error(`failed to convert zod type ${zodType} to go type`); } diff --git a/packages/scripts/install-gno.ts b/packages/scripts/install-gno.ts index 66db6555c3..d3185e536e 100644 --- a/packages/scripts/install-gno.ts +++ b/packages/scripts/install-gno.ts @@ -5,10 +5,13 @@ import path from "path"; import sqh from "./sqh"; -const ref = "fec2d18f630b44ccc2121472aa2284cd9c8caf6f"; +const gnoVersionFilepath = path.join(__dirname, "..", "..", ".gnoversion"); const remote = "https://github.com/gnolang/gno.git"; const main = async () => { + const ref = ( + await fs.readFile(gnoVersionFilepath, { encoding: "utf-8" }) + ).trim(); const buildDir = await fs.mkdtemp(path.join(os.tmpdir(), "gno-build-")); const cmd = `git clone ${sqh(remote)} ${sqh(buildDir)} && cd ${sqh(buildDir)} && git checkout ${sqh(ref)} && make install`; console.log(">", cmd); diff --git a/packages/scripts/network-setup/deployCwAddressList.ts b/packages/scripts/network-setup/deployCwAddressList.ts new file mode 100644 index 0000000000..0d77f89cbc --- /dev/null +++ b/packages/scripts/network-setup/deployCwAddressList.ts @@ -0,0 +1,119 @@ +import { bech32 } from "bech32"; +import { program } from "commander"; +import { cloneDeep } from "lodash"; +import os from "os"; +import path from "path"; + +import { + CosmosNetworkInfo, + getCosmosNetwork, + getNetworkFeature, + NetworkFeature, +} from "@/networks"; +import { execPromise } from "@/scripts/lib"; +import { + instantiateContract, + storeWASM, +} from "@/scripts/network-setup/deployLib"; + +const deployCwAddressList = async ({ + opts, + networkId, + wallet, +}: { + networkId: string; + wallet: string; + opts: { home: string; binaryPath: string; keyringBackend?: string }; +}) => { + const network = cloneDeep(getCosmosNetwork(networkId)); + if (!network) { + console.error(`Cosmos network ${networkId} not found`); + process.exit(1); + } + console.log(`Deploying to ${network.displayName}`); + + let walletAddr = ( + await execPromise( + `${opts.binaryPath} keys show --keyring-backend ${opts.keyringBackend || "test"} -a ${wallet} --home ${opts.home}`, + { encoding: "utf-8" }, + ) + ).stdout.trim(); + if (walletAddr.startsWith("Successfully migrated")) { + walletAddr = walletAddr.substring(walletAddr.indexOf("\n")).trim(); + } + bech32.decode(walletAddr); + console.log("Wallet address:", walletAddr); + + const nftMarketplaceFeature = cloneDeep( + getNetworkFeature(networkId, NetworkFeature.NFTMarketplace), + ); + if (!nftMarketplaceFeature) { + console.error(`NFT Marketplace feature not found on ${networkId}`); + process.exit(1); + } + + if (!nftMarketplaceFeature.cwAddressListContractAddress) { + console.log("Storing cw address list"); + const cwAddressListWasmFilePath = path.join( + __dirname, + "cw_address_list.wasm", + ); + network.cwAdminFactoryCodeId = await storeWASM( + opts, + wallet, + network, + cwAddressListWasmFilePath, + ); + + console.log("Instantiating cw address list", network.cwAdminFactoryCodeId); + nftMarketplaceFeature.cwAddressListContractAddress = + await instantiateCwAddressList(opts, wallet, walletAddr, network); + + network.featureObjects = network.featureObjects?.map((featureObject) => { + if (featureObject.type === NetworkFeature.NFTMarketplace) { + return nftMarketplaceFeature; + } else return featureObject; + }); + } +}; + +const instantiateCwAddressList = async ( + opts: { home: string; binaryPath: string; keyringBackend?: string }, + wallet: string, + adminAddr: string, + network: CosmosNetworkInfo, +) => { + const codeId = network.cwAdminFactoryCodeId; + if (!codeId) { + throw new Error("CW Address List code ID not found"); + } + return await instantiateContract( + opts, + wallet, + network, + codeId, + adminAddr, + "Teritori CW Address List", + {}, + ); +}; + +const main = async () => { + program.argument("", "Network id to deploy to"); + program.argument("", "Wallet to deploy from"); + program.option("--keyring-backend [keyring-backend]", "Keyring backend"); + program.parse(); + const [networkId, wallet] = program.args; + const { keyringBackend } = program.opts(); + + await deployCwAddressList({ + opts: { + home: path.join(os.homedir(), ".teritorid"), + binaryPath: "teritorid", + keyringBackend, + }, + networkId, + wallet, + }); +}; +main(); diff --git a/packages/utils/errors.ts b/packages/utils/errors.ts index 7286d73a92..1996d18b6a 100644 --- a/packages/utils/errors.ts +++ b/packages/utils/errors.ts @@ -1,6 +1,11 @@ export const DEFAULT_FORM_ERRORS = { required: "This field is required", datetime: "Invalid", + onlyLetters: "Only letters are allowed", + onlyNumbers: "Only numbers are allowed", + onlyUrl: "Only URL is allowed", + onlyIpfsUri: "Only IPFS URI is allowed", + onlyEmail: "Only email address is allowed", }; export const DETAULT_PROCESS_ERROR = diff --git a/packages/utils/formRules.ts b/packages/utils/formRules.ts index 9a121bae94..0f457a873f 100644 --- a/packages/utils/formRules.ts +++ b/packages/utils/formRules.ts @@ -1,6 +1,10 @@ import { bech32 } from "bech32"; import { ValidationRule } from "react-hook-form"; +import { LETTERS_REGEXP, NUMBERS_REGEXP } from "./regex"; + +import { DEFAULT_FORM_ERRORS } from "@/utils/errors"; + // validator should return false or string to trigger error export const validateAddress = (value: string) => { try { @@ -13,13 +17,13 @@ export const validateAddress = (value: string) => { }; export const patternOnlyLetters: ValidationRule = { - value: /^[A-Za-z]+$/, - message: "Only letters are allowed", + value: LETTERS_REGEXP, + message: DEFAULT_FORM_ERRORS.onlyLetters, }; export const patternOnlyNumbers: ValidationRule = { - value: /^\d+$/, - message: "Only numbers are allowed", + value: NUMBERS_REGEXP, + message: DEFAULT_FORM_ERRORS.onlyNumbers, }; export const validateMaxNumber = (value: string, max: number) => { diff --git a/packages/utils/gno.ts b/packages/utils/gno.ts index 42ff6dfdd6..de41e6fcda 100644 --- a/packages/utils/gno.ts +++ b/packages/utils/gno.ts @@ -2,6 +2,11 @@ import { GnoJSONRPCProvider } from "@gnolang/gno-js-client"; import { mustGetGnoNetwork } from "../networks"; +export enum AdenaDoContractMessageType { + CALL = "/vm.m_call", + ADD_PKG = "/vm.m_addpkg", +} + export interface AdenaDoContractMessage { type: string; value: { [key in string]: any }; @@ -72,20 +77,20 @@ export const adenaVMCall = async ( ) => { await adenaDoContract( networkId, - [{ type: "/vm.m_call", value: vmCall }], + [{ type: AdenaDoContractMessageType.CALL, value: vmCall }], opts, ); }; interface Package { - Name: string; - Path: string; - Files: PackageFile[]; + name: string; + path: string; + files: PackageFile[]; } interface PackageFile { - Name: string; - Body: string; + name: string; + body: string; } interface VmAddPackage { @@ -101,7 +106,7 @@ export const adenaAddPkg = async ( ) => { await adenaDoContract( networkId, - [{ type: "/vm.m_addpkg", value: vmAddPackage }], + [{ type: AdenaDoContractMessageType.ADD_PKG, value: vmAddPackage }], { ...opts, }, diff --git a/packages/utils/gnodao/deploy.ts b/packages/utils/gnodao/deploy.ts index eafbd5bb3e..7c50be7315 100644 --- a/packages/utils/gnodao/deploy.ts +++ b/packages/utils/gnodao/deploy.ts @@ -27,33 +27,29 @@ const generateDAORealmSource = (networkId: string, conf: GnoDAOConfig) => { dao_core "${network.daoCorePkgPath}" dao_interfaces "${network.daoInterfacesPkgPath}" proposal_single "${network.daoProposalSinglePkgPath}" + "${network.daoUtilsPkgPath}" voting_group "${network.votingGroupPkgPath}" - "${network.groupsPkgPath}" - modboards "${network.modboardsPkgPath}" "${network.daoRegistryPkgPath}" ) - var ( - daoCore dao_interfaces.IDAOCore - mainBoardName = "${conf.name}" - groupName = mainBoardName + "_voting_group" - groupID groups.GroupID - ) - - func init() { - modboards.CreateBoard(mainBoardName) - - votingModuleFactory := func(core dao_interfaces.IDAOCore) dao_interfaces.IVotingModule { - groupID = groups.CreateGroup(groupName) +var ( + daoCore dao_interfaces.IDAOCore + group *voting_group.VotingGroup + registered bool +) + +func init() { + votingModuleFactory := func(core dao_interfaces.IDAOCore) dao_interfaces.IVotingModule { + group = voting_group.NewVotingGroup() ${conf.initialMembers .map( (member) => - `groups.AddMember(groupID, "${member.address}", ${member.weight}, "");`, + `group.SetMemberPower("${member.address}", ${member.weight});`, ) .join("\n\t")} - return voting_group.NewVotingGroup(groupID) - } - + return group + } + // TODO: consider using factories that return multiple modules and handlers proposalModulesFactories := []dao_interfaces.ProposalModuleFactory{ @@ -65,7 +61,7 @@ const generateDAORealmSource = (networkId: string, conf: GnoDAOConfig) => { conf.quorumPercent * 100, )}) // ${Math.ceil(conf.quorumPercent * 100) / 100}% return proposal_single.NewDAOProposalSingle(core, &proposal_single.DAOProposalSingleOpts{ - MaxVotingPeriod: time.Second * ${conf.maxVotingPeriodSeconds}, + MaxVotingPeriod: dao_utils.DurationTime(time.Second * ${conf.maxVotingPeriodSeconds}), Threshold: &proposal_single.ThresholdThresholdQuorum{ Threshold: &tt, Quorum: &tq, @@ -74,35 +70,24 @@ const generateDAORealmSource = (networkId: string, conf: GnoDAOConfig) => { }, } - messageHandlersFactories := []dao_interfaces.MessageHandlerFactory{ - func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler { - return groups.NewAddMemberHandler() - }, - func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler { - return groups.NewDeleteMemberHandler() - }, - func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler { - // TODO: add a router to support multiple proposal modules - propMod := core.ProposalModules()[0] - return proposal_single.NewUpdateSettingsHandler(propMod.Module.(*proposal_single.DAOProposalSingle)) - }, - func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler { - return modboards.NewCreateBoardHandler() - }, - func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler { - return modboards.NewDeletePostHandler() - }, - } - - daoCore = dao_core.NewDAOCore(votingModuleFactory, proposalModulesFactories, messageHandlersFactories) + messageHandlersFactories := []dao_interfaces.MessageHandlerFactory{ + func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler { + return group.UpdateMembersHandler() + }, + func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler { + // TODO: add a router to support multiple proposal modules + propMod := core.ProposalModules()[0] + return proposal_single.NewUpdateSettingsHandler(propMod.Module.(*proposal_single.DAOProposalSingle)) + }, + } - dao_registry.Register(${JSON.stringify(conf.displayName)}, ${JSON.stringify( - conf.description, - )}, ${JSON.stringify(conf.imageURI)}) + daoCore = dao_core.NewDAOCore(votingModuleFactory, proposalModulesFactories, messageHandlersFactories) + + dao_registry.Register(func() dao_interfaces.IDAOCore { return daoCore }, "${conf.displayName}", "${conf.description}", "${conf.imageURI}") } func Render(path string) string { - return "[[board](/r/demo/modboards:" + mainBoardName + ")]\\n\\n" + daoCore.Render(path) + return daoCore.Render(path) } func VoteJSON(moduleIndex int, proposalID int, voteJSON string) { @@ -111,32 +96,41 @@ const generateDAORealmSource = (networkId: string, conf: GnoDAOConfig) => { if !module.Enabled { panic("proposal module is not enabled") } + module.Module.VoteJSON(proposalID, voteJSON) } - + func Execute(moduleIndex int, proposalID int) { // move check in dao core module := dao_core.GetProposalModule(daoCore, moduleIndex) if !module.Enabled { panic("proposal module is not enabled") } + module.Module.Execute(proposalID) } - - func ProposeJSON(moduleIndex int, proposalJSON string) { + + func ProposeJSON(moduleIndex int, proposalJSON string) int { // move check in dao core module := dao_core.GetProposalModule(daoCore, moduleIndex) if !module.Enabled { panic("proposal module is not enabled") } - module.Module.ProposeJSON(proposalJSON) + + return module.Module.ProposeJSON(proposalJSON) } - + func getProposalsJSON(moduleIndex int, limit int, startAfter string, reverse bool) string { // move logic in dao core module := dao_core.GetProposalModule(daoCore, moduleIndex) return module.Module.ProposalsJSON(limit, startAfter, reverse) } + + func getProposalJSON(moduleIndex int, proposalIndex int) string { + // move logic in dao core + module := dao_core.GetProposalModule(daoCore, moduleIndex) + return module.Module.ProposalJSON(proposalIndex) + } `; }; @@ -146,19 +140,19 @@ export const adenaDeployGnoDAO = async ( conf: GnoDAOConfig, ) => { const source = generateDAORealmSource(networkId, conf); - const pkgPath = `gno.land/r/demo/${conf.name}`; + const pkgPath = `gno.land/r/${creator}/${conf.name}`; await adenaAddPkg( networkId, { creator, deposit: "1ugnot", package: { - Name: conf.name, - Path: pkgPath, - Files: [{ Name: `${conf.name}.gno`, Body: source }], + name: conf.name, + path: pkgPath, + files: [{ name: `${conf.name}.gno`, body: source }], }, }, - { gasWanted: 10000000 }, + { gasWanted: 20000000 }, ); return pkgPath; }; diff --git a/packages/utils/regex.ts b/packages/utils/regex.ts index de79f51d4c..a21ab7a143 100644 --- a/packages/utils/regex.ts +++ b/packages/utils/regex.ts @@ -4,3 +4,5 @@ export const URL_REGEX = export const HASHTAG_REGEX = /#\S+/; export const HTML_TAG_REGEXP = /(<([^>]+)>)/gi; export const GIF_URL_REGEX = /https?:\/\/.*\.(gif)(\?.*)?$/; +export const NUMBERS_REGEXP = /^\d+$/; +export const LETTERS_REGEXP = /^[A-Za-z]+$/; diff --git a/packages/utils/style/layout.ts b/packages/utils/style/layout.ts index 43ac47f75f..fcd3dde5f0 100644 --- a/packages/utils/style/layout.ts +++ b/packages/utils/style/layout.ts @@ -62,6 +62,10 @@ export const layout = Object.freeze({ get spacing_x1_5() { return this.base * 1.5; }, + // 14 + get spacing_x1_75() { + return this.base * 1.75; + }, // 16 get spacing_x2() { return this.base * 2; diff --git a/packages/utils/text.tsx b/packages/utils/text.tsx index 8b9ceaaf63..2ecc03bcee 100644 --- a/packages/utils/text.tsx +++ b/packages/utils/text.tsx @@ -32,6 +32,3 @@ export const replaceBetweenString = ( insertion: string, ) => `${origin.substring(0, startIndex)}${insertion}${origin.substring(endIndex)}`; - -export const pluralOrNot = (word: string, quantity: number) => - quantity > 1 ? word + "s" : word; diff --git a/rust/cw-contracts/cw-address-list/.cargo/config.toml b/rust/cw-contracts/cw-address-list/.cargo/config.toml new file mode 100644 index 0000000000..9354fae229 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/.cargo/config.toml @@ -0,0 +1,4 @@ +[alias] +wasm = "build --target wasm32-unknown-unknown --release --lib" +wasm-debug = "build --target wasm32-unknown-unknown --lib" +schema = "run schema" diff --git a/rust/cw-contracts/cw-address-list/.gitignore b/rust/cw-contracts/cw-address-list/.gitignore new file mode 100644 index 0000000000..42dafca836 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/.gitignore @@ -0,0 +1,2 @@ +/target +/artifacts \ No newline at end of file diff --git a/rust/cw-contracts/cw-address-list/Cargo.lock b/rust/cw-contracts/cw-address-list/Cargo.lock new file mode 100644 index 0000000000..c2e87686c0 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/Cargo.lock @@ -0,0 +1,1146 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bnum" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "const_panic" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cosmwasm-crypto" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bb3c77c3b7ce472056968c745eb501c440fbc07be5004eba02782c35bfbbe3" +dependencies = [ + "digest 0.10.7", + "ecdsa 0.16.9", + "ed25519-zebra", + "k256 0.13.2", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea73e9162e6efde00018d55ed0061e93a108b5d6ec4548b4f8ce3c706249687" +dependencies = [ + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-schema" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43609e92ce1b9368aa951b334dd354a2d0dd4d484931a5f83ae10e12a26c8ba9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" +dependencies = [ + "base64", + "bech32", + "bnum", + "cosmwasm-crypto", + "cosmwasm-derive", + "derivative", + "forward_ref", + "hex", + "schemars", + "serde", + "serde-json-wasm", + "sha2 0.10.8", + "static_assertions", + "thiserror", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cw-multi-test" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "127c7bb95853b8e828bdab97065c81cb5ddc20f7339180b61b2300565aaa99d1" +dependencies = [ + "anyhow", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "derivative", + "itertools", + "k256 0.11.6", + "prost", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw-storage-plus" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" +dependencies = [ + "cosmwasm-std", + "schemars", + "serde", +] + +[[package]] +name = "cw-utils" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw2", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw_address_list" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "schemars", + "serde", + "sylvia", + "thiserror", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", + "signature 2.2.0", + "spki 0.7.3", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.5", + "digest 0.10.7", + "ff 0.13.0", + "generic-array", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "forward_ref" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", +] + +[[package]] +name = "k256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +dependencies = [ + "cfg-if", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "once_cell", + "sha2 0.10.8", + "signature 2.2.0", +] + +[[package]] +name = "konst" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a6ee015a18f4a121ba670f947f801b207139f0f810b3cd266e319065129782" +dependencies = [ + "const_panic", + "konst_kernel", + "konst_proc_macros", + "typewit", +] + +[[package]] +name = "konst_kernel" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3376133edc39f027d551eb77b077c2865a0ef252b2e7d0dd6b6dc303db95d8b5" +dependencies = [ + "typewit", +] + +[[package]] +name = "konst_proc_macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e28ab1dc35e09d60c2b8c90d12a9a8d9666c876c10a3739a3196db0103b6043" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.3", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9cc1a3263e07e0bf68e96268f37665207b49560d98739662cdfaae215c720fe" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac", + "zeroize", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", + "generic-array", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-cw-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d32da6b8ed758b7d850b6c3c08f1d7df51a4df3cb201296e63e34a78e99d4" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.8", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "sylvia" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f358d505f46900e55154f028f18811961ebb58f7a92954ec03086ffb2b26cf51" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "derivative", + "konst", + "schemars", + "serde", + "serde-cw-value", + "serde-json-wasm", + "sylvia-derive", +] + +[[package]] +name = "sylvia-derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed182fb775d756fdfe7e87174a4e43f1c446c8f9aff1de38a2165dd04b7d805" +dependencies = [ + "convert_case", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "typewit" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6779a69cc5f9a7388274a0a8a353eb1c9e45195f9ae74a26690b055a7cf9592a" +dependencies = [ + "typewit_proc_macros", +] + +[[package]] +name = "typewit_proc_macros" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winnow" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +dependencies = [ + "memchr", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/rust/cw-contracts/cw-address-list/Cargo.toml b/rust/cw-contracts/cw-address-list/Cargo.toml new file mode 100644 index 0000000000..23f3abf018 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "cw-address-list" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cosmwasm-std = { version = "1.5.3", features = ["staking"] } +sylvia = "0.9.3" +schemars = "0.8.16" +cosmwasm-schema = "1.5.3" +serde = "1.0.197" +cw-storage-plus = "1.2.0" +thiserror = "1.0.57" + +[dev-dependencies] +sylvia = { version = "0.9.3", features = ["mt"] } \ No newline at end of file diff --git a/rust/cw-contracts/cw-address-list/schema/cw-address-list.json b/rust/cw-contracts/cw-address-list/schema/cw-address-list.json new file mode 100644 index 0000000000..dcd90e9c8b --- /dev/null +++ b/rust/cw-contracts/cw-address-list/schema/cw-address-list.json @@ -0,0 +1,243 @@ +{ + "contract_name": "cw-address-list", + "contract_version": "0.1.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "admin" + ], + "properties": { + "admin": { + "type": "string" + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "anyOf": [ + { + "$ref": "#/definitions/ExecMsg" + } + ], + "definitions": { + "ExecMsg": { + "oneOf": [ + { + "type": "object", + "required": [ + "add" + ], + "properties": { + "add": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "remove" + ], + "properties": { + "remove": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_admin" + ], + "properties": { + "change_admin": { + "type": "object", + "properties": { + "addr": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "anyOf": [ + { + "$ref": "#/definitions/QueryMsg" + } + ], + "definitions": { + "Bound": { + "oneOf": [ + { + "type": "object", + "required": [ + "inclusive" + ], + "properties": { + "inclusive": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "exclusive" + ], + "properties": { + "exclusive": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Order": { + "type": "string", + "enum": [ + "ascending", + "descending" + ] + }, + "QueryMsg": { + "oneOf": [ + { + "type": "object", + "required": [ + "admin" + ], + "properties": { + "admin": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "addresses" + ], + "properties": { + "addresses": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "max": { + "anyOf": [ + { + "$ref": "#/definitions/Bound" + }, + { + "type": "null" + } + ] + }, + "min": { + "anyOf": [ + { + "$ref": "#/definitions/Bound" + }, + { + "type": "null" + } + ] + }, + "order": { + "anyOf": [ + { + "$ref": "#/definitions/Order" + }, + { + "type": "null" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "migrate": null, + "sudo": null, + "responses": { + "addresses": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Addr", + "type": "array", + "items": { + "$ref": "#/definitions/Addr" + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } + }, + "admin": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Addr", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } + } + } +} diff --git a/rust/cw-contracts/cw-address-list/schema/raw/execute.json b/rust/cw-contracts/cw-address-list/schema/raw/execute.json new file mode 100644 index 0000000000..66c89283f0 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/schema/raw/execute.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "anyOf": [ + { + "$ref": "#/definitions/ExecMsg" + } + ], + "definitions": { + "ExecMsg": { + "oneOf": [ + { + "type": "object", + "required": [ + "add" + ], + "properties": { + "add": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "remove" + ], + "properties": { + "remove": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "change_admin" + ], + "properties": { + "change_admin": { + "type": "object", + "properties": { + "addr": { + "type": [ + "string", + "null" + ] + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/rust/cw-contracts/cw-address-list/schema/raw/instantiate.json b/rust/cw-contracts/cw-address-list/schema/raw/instantiate.json new file mode 100644 index 0000000000..04a6e5114d --- /dev/null +++ b/rust/cw-contracts/cw-address-list/schema/raw/instantiate.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "admin" + ], + "properties": { + "admin": { + "type": "string" + } + } +} diff --git a/rust/cw-contracts/cw-address-list/schema/raw/query.json b/rust/cw-contracts/cw-address-list/schema/raw/query.json new file mode 100644 index 0000000000..2e2052e94b --- /dev/null +++ b/rust/cw-contracts/cw-address-list/schema/raw/query.json @@ -0,0 +1,114 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "anyOf": [ + { + "$ref": "#/definitions/QueryMsg" + } + ], + "definitions": { + "Bound": { + "oneOf": [ + { + "type": "object", + "required": [ + "inclusive" + ], + "properties": { + "inclusive": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "exclusive" + ], + "properties": { + "exclusive": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Order": { + "type": "string", + "enum": [ + "ascending", + "descending" + ] + }, + "QueryMsg": { + "oneOf": [ + { + "type": "object", + "required": [ + "admin" + ], + "properties": { + "admin": { + "type": "object" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "addresses" + ], + "properties": { + "addresses": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "max": { + "anyOf": [ + { + "$ref": "#/definitions/Bound" + }, + { + "type": "null" + } + ] + }, + "min": { + "anyOf": [ + { + "$ref": "#/definitions/Bound" + }, + { + "type": "null" + } + ] + }, + "order": { + "anyOf": [ + { + "$ref": "#/definitions/Order" + }, + { + "type": "null" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/rust/cw-contracts/cw-address-list/schema/raw/response_to_addresses.json b/rust/cw-contracts/cw-address-list/schema/raw/response_to_addresses.json new file mode 100644 index 0000000000..bf06b01c2f --- /dev/null +++ b/rust/cw-contracts/cw-address-list/schema/raw/response_to_addresses.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Addr", + "type": "array", + "items": { + "$ref": "#/definitions/Addr" + }, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } +} diff --git a/rust/cw-contracts/cw-address-list/schema/raw/response_to_admin.json b/rust/cw-contracts/cw-address-list/schema/raw/response_to_admin.json new file mode 100644 index 0000000000..970164b3e7 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/schema/raw/response_to_admin.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Addr", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } +} diff --git a/rust/cw-contracts/cw-address-list/src/bin/schema.rs b/rust/cw-contracts/cw-address-list/src/bin/schema.rs new file mode 100644 index 0000000000..92d9dac1b4 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/src/bin/schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use cw_address_list::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: ContractExecMsg, + query: ContractQueryMsg, + } +} diff --git a/rust/cw-contracts/cw-address-list/src/contract.rs b/rust/cw-contracts/cw-address-list/src/contract.rs new file mode 100644 index 0000000000..8a490631ac --- /dev/null +++ b/rust/cw-contracts/cw-address-list/src/contract.rs @@ -0,0 +1,180 @@ +use crate::error::ContractError; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Order as CwOrder, Response, StdResult}; +use cw_storage_plus::{Bound as CwBound, Item, Map}; +use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx}; +use sylvia::{contract, entry_points}; + +pub struct AddressListContract { + pub(crate) admin: Item<'static, Option>, + pub(crate) addresses: Map<'static, Addr, ()>, +} + +pub const QUERY_BATCH_MAX_LIMIT: u32 = 50; + +#[cw_serde] +pub enum Order { + Ascending, + Descending, +} + +impl Into for Order { + fn into(self) -> CwOrder { + match self { + Order::Ascending => CwOrder::Ascending, + Order::Descending => CwOrder::Descending, + } + } +} + +#[cw_serde] +pub enum Bound { + Inclusive(String), + Exclusive(String), +} + +impl Into> for Bound { + fn into(self) -> CwBound<'static, Addr> { + match self { + Bound::Inclusive(addr) => CwBound::inclusive(Addr::unchecked(addr)), + Bound::Exclusive(addr) => CwBound::exclusive(Addr::unchecked(addr)), + } + } +} + +#[entry_points] +#[contract] +#[error(ContractError)] +impl AddressListContract { + pub const fn new() -> Self { + Self { + admin: Item::new("admin_v0"), + addresses: Map::new("addresses_v0"), + } + } + + #[msg(instantiate)] + pub fn instantiate(&self, ctx: InstantiateCtx, admin: String) -> StdResult { + let admin = ctx.deps.api.addr_validate(admin.as_str())?; + self.admin.save(ctx.deps.storage, &Some(admin))?; + Ok(Response::default()) + } + + // Mutations + + #[msg(exec)] + pub fn add(&self, ctx: ExecCtx, addr: String) -> Result { + self.assert_admin(&ctx)?; + + let addr = ctx.deps.api.addr_validate(addr.as_str())?; + + self.addresses + .update(ctx.deps.storage, addr, |value| match value { + Some(_) => Err(ContractError::AddressAlreadyRegistered), + None => Ok(()), + })?; + + Ok(Response::default()) + } + + #[msg(exec)] + pub fn remove(&self, ctx: ExecCtx, addr: String) -> Result { + self.assert_admin(&ctx)?; + + let addr = ctx.deps.api.addr_validate(addr.as_str())?; + + if !self.addresses.has(ctx.deps.storage, addr.clone()) { + return Err(ContractError::AddressNotRegistered); + } + + self.addresses.remove(ctx.deps.storage, addr); + + Ok(Response::default()) + } + + #[msg(exec)] + pub fn change_admin( + &self, + ctx: ExecCtx, + addr: Option, + ) -> Result { + self.assert_admin(&ctx)?; + + let admin = self.admin.load(ctx.deps.storage)?; + + let opt = match addr { + Some(addr) => { + let addr = ctx.deps.api.addr_validate(addr.as_str())?; + if admin == Some(addr.clone()) { + return Err(ContractError::AlreadyAdmin); + } + Some(addr) + } + None => None, + }; + + self.admin.save(ctx.deps.storage, &opt)?; + + Ok(Response::default()) + } + + fn assert_admin(&self, ctx: &ExecCtx) -> Result<(), ContractError> { + let admin = self.admin.load(ctx.deps.storage)?; + match admin { + Some(admin) => { + if admin != ctx.info.sender { + Err(ContractError::Unauthorized) + } else { + Ok(()) + } + } + None => Err(ContractError::Unauthorized), + } + } + + // Queries + + #[msg(query)] + pub fn admin(&self, ctx: QueryCtx) -> Result, ContractError> { + let admin = self.admin.load(ctx.deps.storage)?; + Ok(admin) + } + + #[msg(query)] + pub fn addresses( + &self, + ctx: QueryCtx, + min: Option, + max: Option, + order: Option, + limit: Option, + ) -> Result, ContractError> { + let limit = limit + .map(|limit| { + if limit > QUERY_BATCH_MAX_LIMIT { + QUERY_BATCH_MAX_LIMIT + } else { + limit + } + }) + .unwrap_or(QUERY_BATCH_MAX_LIMIT); + + let order = order.unwrap_or(Order::Ascending); + + let addrs: Result, _> = self + .addresses + .range( + ctx.deps.storage, + min.map(|m| m.into()), + max.map(|m| m.into()), + order.into(), + ) + .take(limit as usize) + .map(|item| -> Result { + let (addr, _) = item?; + Ok(addr) + }) + .collect(); + Ok(addrs?) + } +} diff --git a/rust/cw-contracts/cw-address-list/src/error.rs b/rust/cw-contracts/cw-address-list/src/error.rs new file mode 100644 index 0000000000..cffb2ed4ce --- /dev/null +++ b/rust/cw-contracts/cw-address-list/src/error.rs @@ -0,0 +1,23 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("Address already registered.")] + AddressAlreadyRegistered, + + #[error("Address not registered.")] + AddressNotRegistered, + + #[error("Invalid cursor.")] + InvalidCursor, + + #[error("Unauthorized.")] + Unauthorized, + + #[error("Already admin.")] + AlreadyAdmin, +} diff --git a/rust/cw-contracts/cw-address-list/src/lib.rs b/rust/cw-contracts/cw-address-list/src/lib.rs new file mode 100644 index 0000000000..ae5b855d2f --- /dev/null +++ b/rust/cw-contracts/cw-address-list/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod error; +#[cfg(test)] +pub mod multitest; diff --git a/rust/cw-contracts/cw-address-list/src/multitest.rs b/rust/cw-contracts/cw-address-list/src/multitest.rs new file mode 100644 index 0000000000..4ea0a4a328 --- /dev/null +++ b/rust/cw-contracts/cw-address-list/src/multitest.rs @@ -0,0 +1,58 @@ +use cosmwasm_std::Addr; +use sylvia::multitest::App; + +use crate::contract::sv::multitest_utils::CodeId; + +#[test] +fn instantiate() { + let app = App::default(); + let code_id = CodeId::store_code(&app); + + let owner = "owner"; + let owner_addr = Addr::unchecked(owner); + + let contract = code_id.instantiate(owner.to_string()).call(owner).unwrap(); + + let admin = contract.admin().unwrap(); + assert_eq!(admin, Some(owner_addr)); + + let addrs = contract.addresses(None, None, None, None).unwrap(); + assert_eq!(addrs, Vec::::new()); + + let col1 = "col1"; + let col1_addr = Addr::unchecked(col1); + contract.add(col1.to_string()).call(owner).unwrap(); + let addrs = contract.addresses(None, None, None, None).unwrap(); + assert_eq!(addrs, vec![col1_addr.clone()]); + + let col2 = "col2"; + let col2_addr = Addr::unchecked(col2); + contract.add(col2.to_string()).call(owner).unwrap(); + let addrs = contract.addresses(None, None, None, None).unwrap(); + assert_eq!(addrs, vec![col1_addr.clone(), col2_addr.clone()]); + let addrs = contract + .addresses(None, None, Some(crate::contract::Order::Descending), None) + .unwrap(); + assert_eq!(addrs, vec![col2_addr.clone(), col1_addr.clone()]); + + let addrs = contract.addresses(None, None, None, Some(1)).unwrap(); + assert_eq!(addrs, vec![col1_addr.clone()]); + + let addrs = contract + .addresses( + Some(crate::contract::Bound::Exclusive(col1.to_string())), + None, + None, + None, + ) + .unwrap(); + assert_eq!(addrs, vec![col2_addr.clone()]); + + contract.remove(col1.to_string()).call(owner).unwrap(); + let addrs = contract.addresses(None, None, None, None).unwrap(); + assert_eq!(addrs, vec![col2_addr]); + + contract.remove(col2.to_string()).call(owner).unwrap(); + let addrs = contract.addresses(None, None, None, None).unwrap(); + assert_eq!(addrs, Vec::::new()); +} diff --git a/rust/cw-contracts/nft-marketplace/.cargo/config b/rust/cw-contracts/nft-marketplace/.cargo/config.toml similarity index 100% rename from rust/cw-contracts/nft-marketplace/.cargo/config rename to rust/cw-contracts/nft-marketplace/.cargo/config.toml diff --git a/yarn.lock b/yarn.lock index 7b057ed53d..764cde2450 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6878,6 +6878,13 @@ __metadata: languageName: node linkType: hard +"@types/pluralize@npm:^0.0.33": + version: 0.0.33 + resolution: "@types/pluralize@npm:0.0.33" + checksum: 282d42dc0187e5e0912f9f36ee0f5615bfd273a08d40afe5bf5881cb28daf1977abe10564543032aa0f42352ebba739ff3d86bf5562ac4691c6d1761fcc7cf39 + languageName: node + linkType: hard + "@types/prettier@npm:^2.6.1": version: 2.7.3 resolution: "@types/prettier@npm:2.7.3" @@ -17023,6 +17030,13 @@ __metadata: languageName: node linkType: hard +"pluralize@npm:^8.0.0": + version: 8.0.0 + resolution: "pluralize@npm:8.0.0" + checksum: 08931d4a6a4a5561a7f94f67a31c17e6632cb21e459ab3ff4f6f629d9a822984cf8afef2311d2005fbea5d7ef26016ebb090db008e2d8bce39d0a9a9d218736e + languageName: node + linkType: hard + "pngjs@npm:^3.3.0": version: 3.4.0 resolution: "pngjs@npm:3.4.0" @@ -20213,6 +20227,7 @@ __metadata: "@types/markdown-it": ^13.0.7 "@types/node": ^20.9.1 "@types/papaparse": ^5.3.14 + "@types/pluralize": ^0.0.33 "@types/react": ~18.2.45 "@types/react-native-countdown-component": ^2.7.0 "@types/uuid": ^9.0.0 @@ -20279,6 +20294,7 @@ __metadata: papaparse: ^5.4.1 patch-package: ^7.0.2 plausible-tracker: ^0.3.8 + pluralize: ^8.0.0 postinstall-postinstall: ^2.1.0 prettier: ^3.0.3 protobufjs: ^7.2.5