diff --git a/.github/workflows/buf-lint.yml b/.github/workflows/buf-lint.yml index 720134d9c6..df78049252 100644 --- a/.github/workflows/buf-lint.yml +++ b/.github/workflows/buf-lint.yml @@ -11,11 +11,11 @@ jobs: check-codegen: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: bufbuild/buf-setup-action@v1.28.0 + - uses: bufbuild/buf-setup-action@v1.31.0 with: - version: 1.28.0 + version: 1.31.0 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Buf lint diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index cd9e42debc..5189e35dd4 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -14,16 +14,16 @@ jobs: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.12.1 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: cache: "yarn" node-version-file: ".nvmrc" - uses: actions/setup-go@v3 with: - go-version: "1.19" + go-version: "1.20" - name: Install node modules run: yarn @@ -54,16 +54,16 @@ jobs: runs-on: ${{ matrix.runner }} continue-on-error: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: - cache: "npm" + cache: "yarn" node-version-file: ".nvmrc" - uses: actions/setup-go@v3 with: - go-version: "1.19" + go-version: "1.20" - name: Download web build uses: actions/download-artifact@v3 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000000..9af144d5b9 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,37 @@ +name: E2E Tests + +on: + push: + branches: + - main + pull_request: + merge_group: + +jobs: + e2e-tests: + runs-on: ubuntu-latest + steps: + - name: Cancel Previous Runs + uses: styfle/cancel-workflow-action@0.12.1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + cache: "yarn" + node-version-file: ".nvmrc" + - uses: actions/setup-go@v3 + with: + go-version: "1.22" + - uses: cypress-io/github-action@v6 + with: + browser: chrome + build: make install-gno + start: | + yarn start --minify + make start.gnodev-e2e + wait-on: "http://localhost:8888, http://localhost:8081" + - name: Upload errors screenshots + uses: actions/upload-artifact@v4 + if: failure() + with: + name: cypress-screenshots + path: cypress/screenshots diff --git a/.github/workflows/gen.yml b/.github/workflows/gen.yml index 381b22bdcb..830a588365 100644 --- a/.github/workflows/gen.yml +++ b/.github/workflows/gen.yml @@ -11,22 +11,22 @@ jobs: check-codegen: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: cache: "yarn" node-version-file: ".nvmrc" - uses: actions/setup-go@v3 with: - go-version: "1.19" + go-version: "1.22" - uses: dtolnay/rust-toolchain@stable - - uses: bufbuild/buf-setup-action@v1.28.0 + - uses: bufbuild/buf-setup-action@v1.31.0 with: - version: 1.28.0 + version: 1.31.0 github_token: ${{ secrets.GITHUB_TOKEN }} - name: Install node modules diff --git a/.github/workflows/gno-lint.yml b/.github/workflows/gno-lint.yml index 608ddcbd2b..20a288ea15 100644 --- a/.github/workflows/gno-lint.yml +++ b/.github/workflows/gno-lint.yml @@ -18,7 +18,7 @@ jobs: go-version: "1.22" - name: Clean gno run: make clean-gno - + - name: Clone gno run: make clone-gno @@ -27,3 +27,16 @@ jobs: - name: Lint gno run: make lint-gno + + - name: gno mod tidy + run: make gno-mod-tidy + + - 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/.github/workflows/go.yml b/.github/workflows/go.yml index f7e7d475e3..d1a51b13be 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,11 +11,11 @@ jobs: go: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v3 with: - go-version: "1.19" + go-version: "1.22" - name: Tidy go.mod run: make go-mod-tidy diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 1e7b6e3341..208762797f 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -11,9 +11,9 @@ jobs: lint-and-build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: cache: "yarn" node-version-file: ".nvmrc" diff --git a/.github/workflows/mobile-build-android.yml b/.github/workflows/mobile-build-android.yml index 5928813bd9..f9b910175b 100644 --- a/.github/workflows/mobile-build-android.yml +++ b/.github/workflows/mobile-build-android.yml @@ -29,11 +29,11 @@ jobs: with: go-version: "1.20" - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: cache: "yarn" node-version-file: ".nvmrc" diff --git a/.github/workflows/mobile-build-ios.yml b/.github/workflows/mobile-build-ios.yml index 2820081553..9a76e9a7df 100644 --- a/.github/workflows/mobile-build-ios.yml +++ b/.github/workflows/mobile-build-ios.yml @@ -22,11 +22,11 @@ jobs: - name: Select xcode run: sudo xcode-select -s /Applications/Xcode_15.2.app - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: cache: "yarn" node-version-file: ".nvmrc" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 06fd669fa2..48bcfa9b5d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -11,7 +11,7 @@ jobs: rust: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable diff --git a/.nvmrc b/.nvmrc index d576022ca0..6569dfa4f3 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.1.0 \ No newline at end of file +20.8.1 diff --git a/Makefile b/Makefile index 60288082e2..089359f9d4 100644 --- a/Makefile +++ b/Makefile @@ -416,19 +416,37 @@ generate.internal-contracts-clients: node_modules npx tsx packages/scripts/makeTypescriptIndex $${outdir} || exit 1 ; \ done +.PHONY: install-gno +install-gno: node_modules + yarn install-gno + +.PHONY: start.gnodev-e2e +start.gnodev-e2e: + gnodev --unsafe-api --server-mode --add-account g193vp9tjhfpldvgg3gn433ayv8pn7rtfv8shyeq $$(find gno -name gno.mod -type f -exec dirname {} \;) + +.PHONY: clone-gno clone-gno: + mkdir -p gnobuild cd gnobuild && git clone https://github.com/gnolang/gno.git && cd gno && git checkout 9b114172063feaf2da4ae7ebb8263cada3ba699b cp -r ./gno/p ./gnobuild/gno/examples/gno.land/p/teritori +.PHONY: build-gno build-gno: cd gnobuild/gno/gnovm && make build +.PHONY: lint-gno lint-gno: ./gnobuild/gno/gnovm/build/gno lint ./gno/. -v +.PHONY: test-gno test-gno: ./gnobuild/gno/gnovm/build/gno test ./gno/... -v +.PHONY: gno-mod-tidy +gno-mod-tidy: + export gno=$$(pwd)/gnobuild/gno/gnovm/build/gno; \ + find gno -name gno.mod -type f | xargs -I'{}' sh -c 'cd $$(dirname {}); $$gno mod tidy' \; + +.PHONY: clean-gno clean-gno: rm -rf gnobuild - mkdir gnobuild diff --git a/assets/icons/github.svg b/assets/icons/github.svg new file mode 100644 index 0000000000..e47beb5ffd --- /dev/null +++ b/assets/icons/github.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/no-tasks.svg b/assets/icons/no-tasks.svg new file mode 100644 index 0000000000..696633adb9 --- /dev/null +++ b/assets/icons/no-tasks.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/projects-completed.svg b/assets/icons/projects-completed.svg new file mode 100644 index 0000000000..2c869a7177 --- /dev/null +++ b/assets/icons/projects-completed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/projects-inProgress.svg b/assets/icons/projects-inProgress.svg new file mode 100644 index 0000000000..5653f3d15e --- /dev/null +++ b/assets/icons/projects-inProgress.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/projects-open.svg b/assets/icons/projects-open.svg new file mode 100644 index 0000000000..0350aed0b1 --- /dev/null +++ b/assets/icons/projects-open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/projects-program.svg b/assets/icons/projects-program.svg new file mode 100644 index 0000000000..2755ef7cc8 --- /dev/null +++ b/assets/icons/projects-program.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/icons/projects-review.svg b/assets/icons/projects-review.svg new file mode 100644 index 0000000000..9651dd76bd --- /dev/null +++ b/assets/icons/projects-review.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/project-success-payment.png b/assets/project-success-payment.png new file mode 100644 index 0000000000..eed3b2534d Binary files /dev/null and b/assets/project-success-payment.png differ diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 0000000000..546918bec2 --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "cypress"; + +export default defineConfig({ + viewportWidth: 1900, + viewportHeight: 1280, + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + }, +}); diff --git a/cypress/e2e/0_preload.cy.ts b/cypress/e2e/0_preload.cy.ts new file mode 100644 index 0000000000..8a76fbfa12 --- /dev/null +++ b/cypress/e2e/0_preload.cy.ts @@ -0,0 +1,9 @@ +// this helps to get accurate test times by running first and preloading the app + +describe("Preload", () => { + it("successfully loads", () => { + cy.visit("http://localhost:8081", { + timeout: 300000, + }); + }); +}); diff --git a/cypress/e2e/lib.ts b/cypress/e2e/lib.ts new file mode 100644 index 0000000000..8b214e269c --- /dev/null +++ b/cypress/e2e/lib.ts @@ -0,0 +1,16 @@ +export const changeTestUser = (user: string) => { + cy.window().then(async (win) => { + const newUserId = await (win as any).adena.SetTestUser(user); + cy.get(`*[data-testid='selected-wallet-${newUserId}']`).should("exist"); + }); +}; + +export const changeSelectedMilestoneStatus = (newStatus: string) => { + const getStatusDropdown = () => + cy.get("*[data-testid='milestone-select-new-status']", { timeout: 20000 }); + getStatusDropdown().click(); + const statusElem = getStatusDropdown().contains(newStatus); + statusElem.click(); + statusElem.should("not.exist"); + cy.contains("Change Status").click(); +}; diff --git a/cypress/e2e/projects-contractor.cy.ts b/cypress/e2e/projects-contractor.cy.ts new file mode 100644 index 0000000000..c3bfa71968 --- /dev/null +++ b/cypress/e2e/projects-contractor.cy.ts @@ -0,0 +1,93 @@ +import { changeSelectedMilestoneStatus, changeTestUser } from "./lib"; + +describe("Contractor proposer full flow", () => { + it("works", () => { + cy.request("http://127.0.0.1:8888/reset"); + + const projectName = "Test Project"; + const milestoneName = "Test Milestone"; + const judgeAddr = "g1nyz430dsujj56ygllcaujkzagz54v0l6yusspy"; + + cy.visit("http://localhost:8081/projects?network=gno-dev", { + timeout: 300000, + }); + + cy.contains("Create a Project").click(); + + cy.contains("Connect wallet").click({ force: true }); + cy.get("div[data-testid=connect-gnotest-wallet]").click({ force: true }); + cy.contains("Connect wallet").should("not.exist"); + changeTestUser("bob"); + + // first step: basic project info + cy.get("input[placeholder='Your Grant name']").type(projectName); + cy.get("textarea[placeholder='Your Grant description']").type( + "Bli blu Bli blu Bli blu Bli blu Bli blu", + ); + cy.get( + "input[placeholder='Address of the authority that will resolve conflicts']", + ).type(judgeAddr); + + cy.get("input[type=file]").selectFile("cypress/fixtures/image.png", { + force: true, + }); + cy.get("div[data-testid=loader-full-screen]", { timeout: 10000 }).should( + "not.exist", + ); + + cy.get("input[placeholder='Add 1-5 main Grant tags using comma...']").type( + "ui,ux,frontend", + ); + cy.contains("Next").click(); + + // second step: team info + // TODO: remove the default values and type them here + cy.contains("Next").click(); + + // third step: add milestones + cy.contains("Add").click(); + cy.get("input[placeholder='⚡️ Type name here...']").type("Test Milestone"); + cy.get("textarea[placeholder='Type description here...']").type( + "Bli blu bla bleh", + ); + cy.get("input[data-testid='milestone-budget']").clear().type("42"); + cy.get("input[data-testid='milestone-duration']").clear().type("3600"); + cy.get("div[data-testid='milestone-confirm']").click(); + cy.contains("Next").click(); + + // fourth step: review and create + cy.contains("Publish this request").click(); + cy.get("div[data-testid='confirm-and-sign']").click(); + cy.get("div[data-testid='confirm-and-sign']", { timeout: 20000 }).should( + "not.exist", + ); + cy.contains("Back to Project Program").click(); + + // check that the project is present in manager as funder + cy.contains("Projects Manager").click(); + cy.contains("My projects").click(); + cy.contains("Test Project").should("exist"); + cy.contains("Projects Program").click({ force: true }); + + // make alice fund the project + cy.contains(projectName).click(); + changeTestUser("alice"); + cy.contains("Fund this project").click(); + cy.get("div[data-testid='confirm-and-sign']").click(); + cy.get("div[data-testid='confirm-and-sign']", { timeout: 20000 }).should( + "not.exist", + ); + + // switch to bob and do milestone + changeTestUser("bob"); + cy.contains(milestoneName).click(); + // TODO: test in progress state + changeSelectedMilestoneStatus("Review"); + + // switch to alice and approve completion + changeTestUser("alice"); + changeSelectedMilestoneStatus("Completed"); + + cy.get("*[data-testid='project-status-COMPLETED']").should("exist"); + }); +}); diff --git a/cypress/e2e/projects-funder.cy.ts b/cypress/e2e/projects-funder.cy.ts new file mode 100644 index 0000000000..72216449f1 --- /dev/null +++ b/cypress/e2e/projects-funder.cy.ts @@ -0,0 +1,102 @@ +import { changeSelectedMilestoneStatus, changeTestUser } from "./lib"; + +describe("Funder proposer full flow", () => { + it("works", () => { + cy.request("http://127.0.0.1:8888/reset"); + + const projectName = "Test Project"; + const milestoneName = "Test Milestone"; + const judgeAddr = "g1nyz430dsujj56ygllcaujkzagz54v0l6yusspy"; + + cy.visit("http://localhost:8081/projects?network=gno-dev", { + timeout: 300000, + }); + + cy.contains("Create a Project").click(); + + cy.contains("Connect wallet").click({ force: true }); + cy.get("div[data-testid=connect-gnotest-wallet]").click({ force: true }); + cy.contains("Connect wallet").should("not.exist"); + changeTestUser("alice"); + + // first step: basic project info + cy.contains("A funder looking for a developer").click(); + cy.get("input[placeholder='Your Grant name']").type(projectName); + cy.get("textarea[placeholder='Your Grant description']").type( + "Bli blu Bli blu Bli blu Bli blu Bli blu", + ); + cy.get( + "input[placeholder='Address of the authority that will resolve conflicts']", + ).type(judgeAddr); + + cy.get("input[type=file]").selectFile("cypress/fixtures/image.png", { + force: true, + }); + cy.get("div[data-testid=loader-full-screen]", { timeout: 10000 }).should( + "not.exist", + ); + + cy.get("input[placeholder='Add 1-5 main Grant tags using comma...']").type( + "ui,ux,frontend", + ); + cy.contains("Next").click(); + + // second step: team info + // TODO: remove the default values and type them here + cy.contains("Next").click(); + + // third step: add milestones + cy.contains("Add").click(); + cy.get("input[placeholder='⚡️ Type name here...']").type("Test Milestone"); + cy.get("textarea[placeholder='Type description here...']").type( + "Bli blu bla bleh", + ); + cy.get("input[data-testid='milestone-budget']").clear().type("42"); + cy.get("input[data-testid='milestone-duration']").clear().type("3600"); + cy.get("div[data-testid='milestone-confirm']").click(); + cy.contains("Next").click(); + + // fourth step: review and create + cy.contains("Publish this request").click(); + cy.get("div[data-testid='confirm-and-sign']").click(); + cy.get("div[data-testid='confirm-and-sign']", { timeout: 20000 }).should( + "not.exist", + ); + cy.contains("Back to Project Program").click(); + + // check that the project is present in manager as contractor + cy.contains("Projects Manager").click(); + cy.contains("Test Project").should("exist"); + cy.contains("Projects Program").click({ force: true }); + + // make bob candidate to project + cy.contains(projectName).click(); + changeTestUser("bob"); + cy.contains("Submit your candidacy as contractor").click(); + cy.get("div[data-testid='confirm-and-sign']").click(); + cy.get("div[data-testid='confirm-and-sign']", { timeout: 20000 }).should( + "not.exist", + ); + + // accept bob candidacy + changeTestUser("alice"); + cy.contains("1 candidates").click({ timeout: 10000 }); + cy.contains("Accept").click(); + // check that the project is not present anymore in candidates list + cy.contains("Accept").should("not.exist"); + cy.go("back"); + + // switch to bob and do milestone + changeTestUser("bob"); + cy.contains("Test Project").click(); + cy.contains(milestoneName).click({ force: true }); + // TODO: test in progress state + changeSelectedMilestoneStatus("Review"); + + // switch to alice and approve completion + changeTestUser("alice"); + changeSelectedMilestoneStatus("Completed"); + + cy.get("*[data-testid='project-status-COMPLETED']").should("exist"); + }); +}); diff --git a/cypress/fixtures/image.png b/cypress/fixtures/image.png new file mode 100644 index 0000000000..540c94958b Binary files /dev/null and b/cypress/fixtures/image.png differ diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts new file mode 100644 index 0000000000..95857aea4c --- /dev/null +++ b/cypress/support/commands.ts @@ -0,0 +1,37 @@ +/// +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +// +// declare global { +// namespace Cypress { +// interface Chainable { +// login(email: string, password: string): Chainable +// drag(subject: string, options?: Partial): Chainable +// dismiss(subject: string, options?: Partial): Chainable +// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable +// } +// } +// } diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts new file mode 100644 index 0000000000..6a173d6fcb --- /dev/null +++ b/cypress/support/e2e.ts @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import "./commands"; + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/gno/p/jsonutil/gno.mod b/gno/p/jsonutil/gno.mod new file mode 100644 index 0000000000..8077020ff7 --- /dev/null +++ b/gno/p/jsonutil/gno.mod @@ -0,0 +1,7 @@ +module gno.land/p/teritori/jsonutil + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/json v0.0.0-latest + gno.land/p/demo/users v0.0.0-latest +) diff --git a/gno/p/jsonutil/jsonutil.gno b/gno/p/jsonutil/jsonutil.gno new file mode 100644 index 0000000000..8bc5c05e68 --- /dev/null +++ b/gno/p/jsonutil/jsonutil.gno @@ -0,0 +1,131 @@ +package jsonutil + +import ( + "std" + "strconv" + "time" + + "gno.land/p/demo/avl" + "gno.land/p/demo/json" + "gno.land/p/demo/users" +) + +func UnionNode(variant string, value *json.Node) *json.Node { + return json.ObjectNode("", map[string]*json.Node{ + variant: value, + }) +} + +func MustUnion(value *json.Node) (string, *json.Node) { + obj := value.MustObject() + for key, value := range obj { + return key, value + } + + panic("no variant in union") +} + +func TimeNode(value time.Time) *json.Node { + j, err := value.MarshalJSON() + if err != nil { + panic(err) + } + + return json.StringNode("", string(j[1:len(j)-1])) +} + +func MustTime(value *json.Node) time.Time { + t := time.Time{} + err := t.UnmarshalJSON([]byte(value.String())) + if err != nil { + panic(err) + } + + return t +} + +func DurationNode(value time.Duration) *json.Node { + return Int64Node(value.Nanoseconds()) +} + +func MustDurationSeconds(value *json.Node) time.Duration { + return time.Duration(MustInt64(value)) * time.Second +} + +func EmptyObjectNode() *json.Node { + return json.ObjectNode("", nil) +} + +// int is always 64 bits in gno so we need a string to represent it without loss of precision in a lot of javascript environment, I wish bigint in json was more widely supported +func IntNode(value int) *json.Node { + return json.StringNode("", strconv.Itoa(value)) +} + +func MustInt(value *json.Node) int { + i, err := strconv.Atoi(value.MustString()) + if err != nil { + panic(err) + } + + return i +} + +func Uint32Node(value uint32) *json.Node { + return json.StringNode("", strconv.FormatUint(uint64(value), 10)) +} + +func MustUint32(value *json.Node) uint32 { + return uint32(MustInt(value)) +} + +func Int64Node(value int64) *json.Node { + return json.StringNode("", strconv.FormatInt(value, 10)) +} + +func MustInt64(value *json.Node) int64 { + return int64(MustInt(value)) +} + +func Uint64Node(value uint64) *json.Node { + return json.StringNode("", strconv.FormatUint(value, 10)) +} + +func MustUint64(value *json.Node) uint64 { + return uint64(MustInt(value)) // FIXME: full uint64 range support (currently limited to [-2^63, 2^63-1]) +} + +func AVLTreeNode(root *avl.Tree, transform func(elem interface{}) *json.Node) *json.Node { + if root == nil { + return EmptyObjectNode() + } + + fields := make(map[string]*json.Node) + root.Iterate("", "", func(key string, val interface{}) bool { + fields[key] = transform(val) + return false + }) + + return json.ObjectNode("", fields) +} + +func AddressNode(addr std.Address) *json.Node { + return json.StringNode("", addr.String()) +} + +func MustAddress(value *json.Node) std.Address { + addr := std.Address(value.MustString()) + if !addr.IsValid() { + panic("invalid address") + } + + return addr +} + +func AddressOrNameNode(aon users.AddressOrName) *json.Node { + return json.StringNode("", string(aon)) +} + +func MustAddressOrName(value *json.Node) users.AddressOrName { + aon := users.AddressOrName(value.MustString()) + return aon +} diff --git a/gno/p/ujson/gno.mod b/gno/p/ujson/gno.mod index c0f2d826c5..99fa7080c8 100644 --- a/gno/p/ujson/gno.mod +++ b/gno/p/ujson/gno.mod @@ -2,6 +2,6 @@ module gno.land/p/teritori/ujson require ( gno.land/p/demo/avl v0.0.0-latest - gno.land/p/teritori/utf16 v0.0.0-latest gno.land/p/demo/users v0.0.0-latest + gno.land/p/teritori/utf16 v0.0.0-latest ) diff --git a/gno/r/projects_manager/filter.gno b/gno/r/projects_manager/filter.gno new file mode 100644 index 0000000000..2913199307 --- /dev/null +++ b/gno/r/projects_manager/filter.gno @@ -0,0 +1,81 @@ +package projects_manager + +import ( + "std" + + "gno.land/p/demo/json" + "gno.land/p/demo/ufmt" + "gno.land/p/teritori/jsonutil" +) + +type Filter interface { + FromJSON(ast *json.Node) +} + +func FilterFromJSON(ast *json.Node) Filter { + if ast.IsNull() { + return nil + } + var filter Filter + key, member := jsonutil.MustUnion(ast) + switch key { + case "byCandidatesForFunder": + filter = &FilterByCandidatesForFunder{} + case "byFunder": + filter = &FilterByFunder{} + case "byContractor": + filter = &FilterByContractor{} + case "byContractorAndFunder": + filter = &FilterByContractorAndFunder{} + default: + panic(ufmt.Sprintf("invalid filter kind `%s`", key)) + } + filter.FromJSON(member) + return filter +} + +type FilterByCandidatesForFunder struct { + Funder std.Address +} + +func (f *FilterByCandidatesForFunder) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Funder = jsonutil.MustAddress(obj["funder"]) +} + +var _ Filter = &FilterByCandidatesForFunder{} + +type FilterByFunder struct { + Funder std.Address +} + +func (f *FilterByFunder) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Funder = jsonutil.MustAddress(obj["funder"]) +} + +var _ Filter = &FilterByFunder{} + +type FilterByContractor struct { + Contractor std.Address +} + +func (f *FilterByContractor) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Contractor = jsonutil.MustAddress(obj["contractor"]) +} + +var _ Filter = &FilterByContractor{} + +type FilterByContractorAndFunder struct { + Contractor std.Address + Funder std.Address +} + +func (f *FilterByContractorAndFunder) FromJSON(ast *json.Node) { + obj := ast.MustObject() + f.Contractor = jsonutil.MustAddress(obj["contractor"]) + f.Funder = jsonutil.MustAddress(obj["funder"]) +} + +var _ Filter = &FilterByContractorAndFunder{} diff --git a/gno/r/projects_manager/gno.mod b/gno/r/projects_manager/gno.mod new file mode 100644 index 0000000000..379bd441e7 --- /dev/null +++ b/gno/r/projects_manager/gno.mod @@ -0,0 +1,9 @@ +module gno.land/r/teritori/projects_manager + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/json v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/teritori/jsonutil v0.0.0-latest +) diff --git a/gno/r/projects_manager/projects_manager.gno b/gno/r/projects_manager/projects_manager.gno new file mode 100644 index 0000000000..948bf7fe23 --- /dev/null +++ b/gno/r/projects_manager/projects_manager.gno @@ -0,0 +1,941 @@ +package projects_manager + +import ( + "std" + "strconv" + "strings" + "time" + + "gno.land/p/demo/avl" + "gno.land/p/demo/json" + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" + "gno.land/p/teritori/jsonutil" +) + +type ContractStatus uint32 + +const ( + CREATED ContractStatus = 1 + ACCEPTED ContractStatus = 2 + CANCELED ContractStatus = 3 + COMPLETED ContractStatus = 5 + REJECTED ContractStatus = 6 + CONFLICT ContractStatus = 7 + ABORTED_IN_FAVOR_OF_CONTRACTOR ContractStatus = 8 + ABORTED_IN_FAVOR_OF_FUNDER ContractStatus = 9 +) + +func (x ContractStatus) String() string { + switch x { + case CREATED: + return "CREATED" + case ACCEPTED: + return "ACCEPTED" + case CANCELED: + return "CANCELED" + case COMPLETED: + return "COMPLETED" + case REJECTED: + return "REJECTED" + case CONFLICT: + return "CONFLICT" + case ABORTED_IN_FAVOR_OF_CONTRACTOR: + return "ABORTED_IN_FAVOR_OF_CONTRACTOR" + case ABORTED_IN_FAVOR_OF_FUNDER: + return "ABORTED_IN_FAVOR_OF_FUNDER" + } + return "UNKNOWN" +} + +func (x ContractStatus) ToJSON() *json.Node { + return json.StringNode("", x.String()) +} + +type ConflictOutcome uint32 + +const ( + RESUME_CONTRACT ConflictOutcome = 1 + REFUND_FUNDER ConflictOutcome = 2 + PAY_CONTRACTOR ConflictOutcome = 3 +) + +func (x ConflictOutcome) String() string { + switch x { + case RESUME_CONTRACT: + return "RESUME_CONTRACT" + case REFUND_FUNDER: + return "REFUND_FUNDER" + case PAY_CONTRACTOR: + return "PAY_CONTRACTOR" + } + return "UNKNOWN" +} + +func (x ConflictOutcome) ToJSON() *json.Node { + return json.StringNode("", x.String()) +} + +type MilestoneStatus uint32 + +const ( + MS_OPEN MilestoneStatus = 1 + MS_PROGRESS MilestoneStatus = 2 + MS_REVIEW MilestoneStatus = 3 + MS_COMPLETED MilestoneStatus = 4 +) + +func (x MilestoneStatus) String() string { + switch x { + case MS_OPEN: + return "MS_OPEN" + case MS_PROGRESS: + return "MS_PROGRESS" + case MS_REVIEW: + return "MS_REVIEW" + case MS_COMPLETED: + return "MS_COMPLETED" + } + return "UNKNOWN" +} + +func (x MilestoneStatus) ToJSON() *json.Node { + return json.StringNode("", x.String()) +} + +type MilestonePriority uint32 + +const ( + MS_PRIORITY_HIGH MilestonePriority = 1 + MS_PRIORITY_MEDIUM MilestonePriority = 2 + MS_PRIORITY_LOW MilestonePriority = 3 +) + +func (x MilestonePriority) String() string { + switch x { + case MS_PRIORITY_HIGH: + return "MS_PRIORITY_HIGH" + case MS_PRIORITY_MEDIUM: + return "MS_PRIORITY_MEDIUM" + case MS_PRIORITY_LOW: + return "MS_PRIORITY_LOW" + } + return "UNKNOWN" +} + +func MilestonePriorityFromString(s string) MilestonePriority { + switch s { + case "MS_PRIORITY_HIGH": + return MS_PRIORITY_HIGH + case "MS_PRIORITY_MEDIUM": + return MS_PRIORITY_MEDIUM + case "MS_PRIORITY_LOW": + return MS_PRIORITY_LOW + } + panic("invalid MilestonePriority") +} + +func (x MilestonePriority) ToJSON() *json.Node { + return json.StringNode("", x.String()) +} + +type Milestone struct { + id uint64 + title string + desc string + amount int64 + paid int64 + duration time.Duration // marshal as seconds + link string // milestone reference link + funded bool + priority MilestonePriority + status MilestoneStatus +} + +func (ms Milestone) ToJSON() *json.Node { + return json.ObjectNode("", map[string]*json.Node{ + "id": json.StringNode("", strconv.FormatUint(ms.id, 10)), + "title": json.StringNode("", ms.title), + "desc": json.StringNode("", ms.desc), + "amount": json.StringNode("", strconv.FormatInt(ms.amount, 10)), + "paid": json.StringNode("", strconv.FormatInt(ms.paid, 10)), + "duration": json.NumberNode("", ms.duration.Seconds()), + "link": json.StringNode("", ms.link), + "funded": json.BoolNode("", ms.funded), + "priority": ms.priority.ToJSON(), + "status": ms.status.ToJSON(), + }) +} + +type Conflict struct { + initiator std.Address + createdAt time.Time + respondedAt *time.Time + resolvedAt *time.Time + initiatorMessage string + responseMessage *string + resolutionMessage *string + outcome *ConflictOutcome +} + +func (c Conflict) ToJSON() *json.Node { + children := map[string]*json.Node{ + "initiator": json.StringNode("", c.initiator.String()), + "createdAt": json.StringNode("", c.createdAt.Format(time.RFC3339)), + "initiatorMessage": json.StringNode("", c.initiatorMessage), + } + + if c.responseMessage != nil { + children["responseMessage"] = json.StringNode("", *c.responseMessage) + } + if c.respondedAt != nil { + children["respondedAt"] = json.StringNode("", c.respondedAt.Format(time.RFC3339)) + } + if c.resolvedAt != nil { + children["resolvedAt"] = json.StringNode("", c.resolvedAt.Format(time.RFC3339)) + } + if c.resolutionMessage != nil { + children["resolutionMessage"] = json.StringNode("", *c.resolutionMessage) + } + if c.outcome != nil { + children["outcome"] = c.outcome.ToJSON() + } + + return json.ObjectNode("", children) +} + +type Contract struct { + id uint64 + sender std.Address + contractor std.Address + contractorCandidates []std.Address + funder std.Address // funder address + paymentDenom string // banker denom + metadata string // store data forforimage, tags, name, description, links for twitter/github... + status ContractStatus + expireAt time.Time + funderFeedback string + contractorFeedback string + milestones []Milestone + pausedBy string + conflictHandler string // can be a realm path or a caller address + handlerCandidate string // conflict handler candidate suggested by one party + handlerSuggestor string // the suggestor off the conflict handler candidate + createdAt time.Time + budget int64 + funded bool + rejectReason string + conflicts []Conflict +} + +func (c Contract) ToJSON() *json.Node { + candidates := make([]*json.Node, len(c.contractorCandidates)) + for i, candidate := range c.contractorCandidates { + candidates[i] = json.StringNode("", candidate.String()) + } + + milestones := make([]*json.Node, len(c.milestones)) + for i, milestone := range c.milestones { + milestones[i] = milestone.ToJSON() + } + + conflicts := make([]*json.Node, len(c.conflicts)) + for i, conflict := range c.conflicts { + conflicts[i] = conflict.ToJSON() + } + + return json.ObjectNode("", map[string]*json.Node{ + "id": json.StringNode("", strconv.FormatUint(c.id, 10)), + "sender": json.StringNode("", c.sender.String()), + "contractor": json.StringNode("", c.contractor.String()), + "contractorCandidates": json.ArrayNode("", candidates), + "funder": json.StringNode("", c.funder.String()), + "paymentDenom": json.StringNode("", c.paymentDenom), + "metadata": json.StringNode("", c.metadata), + "status": c.status.ToJSON(), + "expireAt": json.StringNode("", c.expireAt.Format(time.RFC3339)), + "funderFeedback": json.StringNode("", c.funderFeedback), + "contractorFeedback": json.StringNode("", c.contractorFeedback), + "milestones": json.ArrayNode("", milestones), + "pausedBy": json.StringNode("", c.pausedBy), + "conflictHandler": json.StringNode("", c.conflictHandler), + "handlerCandidate": json.StringNode("", c.handlerCandidate), + "handlerSuggestor": json.StringNode("", c.handlerSuggestor), + "createdAt": json.StringNode("", c.createdAt.Format(time.RFC3339)), + "budget": json.StringNode("", strconv.FormatInt(c.budget, 10)), + "funded": json.BoolNode("", c.funded), + "rejectReason": json.StringNode("", c.rejectReason), + "conflicts": json.ArrayNode("", conflicts), + }) +} + +// State +var ( + contracts []*Contract + contractsByFunder = avl.NewTree() // std.Address(funder) => contractID => *Contract + contractsByContractor = avl.NewTree() // std.Address(contractor) => contractID => *Contract + contractsByFunderAndContractor = avl.NewTree() // std.Address(funder) + std.Address(contractor) => contractID => *Contract + contractsWithCandidates = avl.NewTree() // std.Address(funder) => contractID => *Contract +) + +func setIndices(contract *Contract) { + if contract == nil { + panic("contract is nil") + } + + if contract.contractor != "" { + contractorKey := std.Address(contract.contractor).String() + byIDTree, ok := contractsByContractor.Get(contractorKey) + if !ok { + byIDTree = avl.NewTree() + contractsByContractor.Set(contractorKey, byIDTree) + } + + byIDTree.(*avl.Tree).Set(seqid.ID(contract.id).String(), contract) + } + + if contract.funder != "" { + funderKey := std.Address(contract.funder).String() + byIDTree, ok := contractsByFunder.Get(funderKey) + if !ok { + byIDTree = avl.NewTree() + contractsByFunder.Set(funderKey, byIDTree) + } + + byIDTree.(*avl.Tree).Set(seqid.ID(contract.id).String(), contract) + } + + if contract.contractor != "" && contract.funder != "" { + byIDTree, ok := contractsByFunderAndContractor.Get(std.Address(contract.funder).String() + std.Address(contract.contractor).String()) + if !ok { + byIDTree = avl.NewTree() + contractsByFunderAndContractor.Set(std.Address(contract.funder).String()+std.Address(contract.contractor).String(), byIDTree) + } + + byIDTree.(*avl.Tree).Set(seqid.ID(contract.id).String(), contract) + } +} + +func CurrentRealm() string { + return std.CurrentRealm().Addr().String() +} + +type MilestoneDefinition struct { + Title string + Desc string + Amount int64 + Duration time.Duration + Link string + Priority MilestonePriority +} + +func CreateContract( + contractor std.Address, + funder std.Address, + paymentDenom string, + metadata string, + expiryDurationSeconds uint64, + milestones []MilestoneDefinition, + conflictHandler string, +) { + if contractor != "" && !contractor.IsValid() { + panic("invalid contractor address") + } + + if funder != "" && !funder.IsValid() { + panic("invalid funder address") + } + + caller := std.PrevRealm().Addr() + if expiryDurationSeconds == 0 { + panic("invalid expiryDuration") + } + if paymentDenom == "" { + panic("empty escrow token") + } + + // For now, one of funder or contract could be empty and can be set later + if contractor == "" && funder == "" { + panic("contractor and funder cannot be both empty") + } + + if contractor != caller && funder != caller { + panic("caller should be one of contractor or funder") + } + + if len(milestones) == 0 { + panic("milestones should not be empty") + } + + mss := make([]Milestone, 0, len(milestones)) + projectBudget := int64(0) + for _, ms := range milestones { + projectBudget += ms.Amount + mss = append(mss, Milestone{ + id: uint64(len(mss)), + title: ms.Title, + desc: ms.Desc, + amount: ms.Amount, + paid: 0, + duration: ms.Duration, + link: ms.Link, + priority: ms.Priority, + status: MS_OPEN, + }) + } + + // If contract creator is funder then he needs to send all the needed fund to contract + funded := false + if caller == funder { + sent := std.GetOrigSend() + amount := sent.AmountOf(paymentDenom) + if amount != projectBudget { + panic(ufmt.Sprintf("funder `%s` should send `%d%s`, got `%d%s`", caller, projectBudget, paymentDenom, amount, paymentDenom)) + } + funded = true + } + + expiryDuration := time.Duration(expiryDurationSeconds) * time.Second + now := time.Now() + + contractId := uint64(len(contracts)) + contracts = append(contracts, &Contract{ + id: contractId, + sender: caller, + contractor: contractor, + funder: funder, + paymentDenom: paymentDenom, + metadata: metadata, + status: CREATED, + expireAt: now.Add(expiryDuration), + milestones: mss, + conflictHandler: conflictHandler, + budget: projectBudget, + createdAt: now, + funded: funded, + }) + setIndices(contracts[contractId]) +} + +func CreateContractJSON( + contractor std.Address, + funder std.Address, + paymentDenom string, + metadata string, + expiryDurationSeconds uint64, + milestonesJSON string, + conflictHandler string, +) { + ast, err := json.Unmarshal([]byte(milestonesJSON)) + if err != nil { + panic(err) + } + vals := ast.MustArray() + milestones := make([]MilestoneDefinition, 0, len(vals)) + for _, val := range vals { + obj := val.MustObject() + milestone := MilestoneDefinition{ + Title: obj["title"].MustString(), + Desc: obj["desc"].MustString(), + Amount: jsonutil.MustInt64(obj["amount"]), + Duration: jsonutil.MustDurationSeconds(obj["duration"]), + Link: obj["link"].MustString(), + Priority: MilestonePriorityFromString(obj["priority"].MustString()), + } + milestones = append(milestones, milestone) + } + CreateContract(contractor, funder, paymentDenom, metadata, expiryDurationSeconds, milestones, conflictHandler) +} + +func CancelContract(contractId uint64) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + if contract.status != CREATED { + panic("contract can only be cancelled at CREATED status") + } + + if contract.sender != caller { + panic("not authorized to cancel the contract") + } + + contracts[contractId].status = CANCELED +} + +func RejectContract(contractId uint64, rejectReason string) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + if contract.status != CREATED { + panic("contract can only be cancelled at CREATED status") + } + + if contract.sender == contract.contractor && caller != contract.funder { + // If contract creator is contractor then only funder can reject + panic("only funder can reject a request from contractor") + } else if contract.sender == contract.funder && caller != contract.contractor { + // If contract creator is funder then only contractor can reject + panic("only contractor can reject a request from funder") + } + + contracts[contractId].status = REJECTED + contracts[contractId].rejectReason = rejectReason +} + +func AcceptContract(contractId uint64) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + if contract.status != CREATED { + panic("contract can only be accepted at CREATED status") + } + + if time.Now().After(contract.expireAt) { + panic("contract already expired") + } + + if contract.sender == caller { + panic("contract sender is not able to accept the contract") + } + + if contract.funder != caller && contract.contractor != caller { + panic("only contract counterparty is allowed to accept") + } + contracts[contractId].status = ACCEPTED +} + +// Submit a funder by putting funds for specific milestones +func SubmitFunder(contractId uint64) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + + if contract.status != CREATED { + panic("can only submit candidate to a CREATED contract") + } + + if contract.funder != "" { + panic("the contract has already a funder") + } + + if caller == contract.contractor { + panic("you cannot become a funder of your requested contract") + } + + sent := std.GetOrigSend() + amount := sent.AmountOf(contract.paymentDenom) + if amount != contract.budget { + panic("wrong amount of funds sent") + } + + contracts[contractId].funded = true + contracts[contractId].status = ACCEPTED + contracts[contractId].funder = caller +} + +// Accept candidate as a contractor +func AcceptContractor(contractId uint64, contractor std.Address) { + if !contractor.IsValid() { + panic("invalid contractor address") + } + + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + + if contract.status != CREATED { + panic("can only submit candidate to a CREATED contract") + } + + if contract.contractor != "" { + panic("the contract has already a contractor") + } + + if caller != contract.funder { + panic("Only contract funder can accept contractor") + } + + candidates := contracts[contractId].contractorCandidates + for _, candidate := range candidates { + // Accept the contract if the address already submitted candidate request + if candidate == contractor { + contracts[contractId].status = ACCEPTED + } + } + + contracts[contractId].contractor = contractor + + funderKey := contract.funder.String() + byIDTreeIface, ok := contractsWithCandidates.Get(funderKey) + if !ok { + byIDTreeIface = avl.NewTree() + contractsWithCandidates.Set(funderKey, byIDTreeIface) + } + byIDTree := byIDTreeIface.(*avl.Tree) + byIDTree.Remove(seqid.ID(contract.id).String()) + if byIDTree.Size() == 0 { + contractsWithCandidates.Remove(funderKey) + } +} + +func SubmitContractorCandidate(contractId uint64) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + + if contract.status != CREATED { + panic("can only submit candidate to a CREATED contract") + } + + if contract.contractor != "" { + panic("the contract has already a contractor") + } + + if caller == contract.funder { + panic("you cannot become a contractor of your funded contract") + } + + candidates := contracts[contractId].contractorCandidates + for _, candidate := range candidates { + if candidate == caller { + panic("already a contractor candidate") + } + } + + contracts[contractId].contractorCandidates = append(candidates, caller) + + funderKey := contract.funder.String() + byIDTree, ok := contractsWithCandidates.Get(funderKey) + if !ok { + byIDTree = avl.NewTree() + contractsWithCandidates.Set(funderKey, byIDTree) + } + byIDTree.(*avl.Tree).Set(seqid.ID(contract.id).String(), contract) +} + +// Complete any milestone in review status and pay the needed amount +func CompleteMilestoneAndPay(contractId uint64, milestoneId uint64) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + if contract.funder != caller { + panic("only contract funder can pay the milestone") + } + + if contract.status != ACCEPTED { + panic("only accepted contract can be paid") + } + + milestone := contract.milestones[milestoneId] + if milestone.status != MS_REVIEW { + panic("can only complete and pay a milestone which is in review status") + } + + // Pay the milestone + unpaid := milestone.amount - milestone.paid + if unpaid > 0 { + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins( + std.CurrentRealm().Addr(), + contract.contractor, + std.Coins{std.Coin{contract.paymentDenom, int64(unpaid)}}) + contracts[contractId].milestones[milestoneId].paid += unpaid + } + + contracts[contractId].milestones[milestoneId].status = MS_COMPLETED + + // If finish all milestone then complete the contract + completedCount := 0 + for _, milestone := range contract.milestones { + if milestone.status == MS_COMPLETED { + completedCount++ + } + } + + if completedCount == len(contract.milestones) { + contracts[contractId].status = COMPLETED + } +} + +// Set milestone status +func ChangeMilestoneStatus(contractId uint64, milestoneId int, newStatus MilestoneStatus) { + if newStatus == MS_COMPLETED { + panic("use CompleteMilestoneAndPay to complete and pay the milestone") + } + + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + contract := contracts[contractId] + + caller := std.PrevRealm().Addr() + if contract.funder != caller && contract.contractor != caller { + panic("only contract participant can execute the action") + } + + if contract.status != ACCEPTED { + panic("contract is not on accepted status") + } + + if len(contract.milestones) <= milestoneId { + panic("milestone Id does not exist in contract") + } + milestone := contract.milestones[milestoneId] + + if milestone.status == MS_COMPLETED { + panic("milestone is completed") + } + + contracts[contractId].milestones[milestoneId].status = newStatus +} + +func RequestConflictResolution(contractId uint64, message string) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + if contract.funder != caller && contract.contractor != caller { + panic("only contract participants can request conflict resolution") + } + + if contract.status != ACCEPTED { + panic("conflict resolution can only be requested at ACCEPTED status") + } + + contracts[contractId].status = CONFLICT + + contracts[contractId].conflicts = append(contract.conflicts, Conflict{ + initiator: caller, + createdAt: time.Now(), + initiatorMessage: message, + }) +} + +func RespondToConflict(contractId uint64, message string) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + if contract.status != CONFLICT { + panic("conflict can only be responded at CONFLICT status") + } + + if len(contract.conflicts) == 0 { + panic("no conflict exists, this should not happen") + } + + conflictId := len(contract.conflicts) - 1 + conflict := contract.conflicts[conflictId] + + if conflict.initiator == contract.funder { + if contract.contractor != caller { + panic("only contract funder can respond to this conflict") + } + } else if conflict.initiator == contract.contractor { + if contract.funder != caller { + panic("only contract contractor can respond to this conflict") + } + } else { + panic("conflict initiator is not valid") + } + + contracts[contractId].conflicts[conflictId].responseMessage = &message + now := time.Now() + contracts[contractId].conflicts[conflictId].respondedAt = &now +} + +func ResolveConflict(contractId uint64, outcome ConflictOutcome, resolutionMessage string) { + caller := std.PrevRealm().Addr() + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + contract := contracts[contractId] + if contract.conflictHandler != caller.String() { + panic("only conflictHandler is allowed for this operation") + } + + if contract.status != CONFLICT { + panic("conflict can only be resolved at CONFLICT status") + } + + if len(contract.conflicts) == 0 { + panic("no conflict exists") + } + + conflictId := len(contract.conflicts) - 1 + + switch outcome { + case RESUME_CONTRACT: + contracts[contractId].status = ACCEPTED + case REFUND_FUNDER: + totalPaid := int64(0) + for _, milestone := range contract.milestones { + totalPaid += milestone.paid + } + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins( + std.CurrentRealm().Addr(), + contract.funder, + std.Coins{std.Coin{contract.paymentDenom, contract.budget - totalPaid}}) + contracts[contractId].status = ABORTED_IN_FAVOR_OF_FUNDER + case PAY_CONTRACTOR: + totalPaid := int64(0) + for _, milestone := range contract.milestones { + totalPaid += milestone.paid + } + banker := std.GetBanker(std.BankerTypeRealmSend) + banker.SendCoins( + std.CurrentRealm().Addr(), + contract.contractor, + std.Coins{std.Coin{contract.paymentDenom, contract.budget - totalPaid}}) + contracts[contractId].status = ABORTED_IN_FAVOR_OF_CONTRACTOR + default: + panic("invalid outcome") + } + + contracts[contractId].conflicts[conflictId].resolutionMessage = &resolutionMessage + contracts[contractId].conflicts[conflictId].outcome = &outcome + now := time.Now() + contracts[contractId].conflicts[conflictId].resolvedAt = &now +} + +func GetContractorCandidatesJSON(contractId uint64) string { + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + candidates := contracts[contractId].contractorCandidates + candidatesJSON := make([]*json.Node, len(candidates)) + for i, candidate := range candidates { + candidatesJSON[i] = json.StringNode("", candidate.String()) + } + + ret, err := json.Marshal(json.ArrayNode("", candidatesJSON)) + if err != nil { + panic(err) + } + return string(ret) +} + +func GetContracts(offset, limit int, filter Filter) []*Contract { + if offset < 0 { + offset = 0 + } + + if limit <= 0 || offset >= len(contracts) { + return nil + } + + if filter == nil { + end := offset + limit + if end > len(contracts) { + end = len(contracts) + } + return contracts[offset:end] + } + + var tree interface{} + switch f := filter.(type) { + case *FilterByCandidatesForFunder: + tree, _ = contractsWithCandidates.Get(f.Funder.String()) + case *FilterByContractorAndFunder: + tree, _ = contractsByFunderAndContractor.Get(f.Funder.String() + f.Contractor.String()) + case *FilterByContractor: + tree, _ = contractsByContractor.Get(f.Contractor.String()) + case *FilterByFunder: + tree, _ = contractsByFunder.Get(f.Funder.String()) + default: + panic("unknown filter") + } + + if tree == nil { + return nil + } + + var results []*Contract + tree.(*avl.Tree).IterateByOffset(offset, limit, func(key string, value interface{}) bool { + results = append(results, value.(*Contract)) + return false + }) + + return results +} + +func RenderContractJSON(contractId uint64) string { + if int(contractId) >= len(contracts) { + panic("invalid contract id") + } + + c := contracts[contractId] + ret, err := json.Marshal(c.ToJSON()) + if err != nil { + panic(err) + } + + return string(ret) +} + +func RenderContractsJSON(offset, limit int, filterJSON string) string { + filter := FilterFromJSON(json.Must(json.Unmarshal([]byte(filterJSON)))) + contractsRes := GetContracts(offset, limit, filter) + return renderContractsJSON(contractsRes) +} + +func renderContractsJSON(contractsRes []*Contract) string { + contractsJSON := make([]*json.Node, len(contractsRes)) + for i, c := range contractsRes { + contractsJSON[i] = c.ToJSON() + } + + ret, err := json.Marshal(json.ArrayNode("", contractsJSON)) + if err != nil { + panic(err) + } + return string(ret) +} + +func Render(path string) string { + b := strings.Builder{} + b.WriteString("# Projects Manager\n") + b.WriteString("## Overview\n") + b.WriteString("This contract is a simple project manager that allows users to create projects and manage them.\n") + b.WriteString(ufmt.Sprintf("Contracts managed: %d\n", len(contracts))) + b.WriteString("## Latest projects\n") + numContracts := 3 + if len(contracts) < 3 { + numContracts = len(contracts) + } + for i := 0; i < numContracts; i++ { + b.WriteString("```json\n") + b.WriteString(RenderContractJSON(uint64(len(contracts) - (i + 1)))) + b.WriteRune('\n') + b.WriteString("```\n") + } + return b.String() +} diff --git a/gno/r/projects_manager/projects_manager_test.gno b/gno/r/projects_manager/projects_manager_test.gno new file mode 100644 index 0000000000..f9c5d7d9dc --- /dev/null +++ b/gno/r/projects_manager/projects_manager_test.gno @@ -0,0 +1,74 @@ +package projects_manager + +import ( + "std" + "testing" + "time" + + "gno.land/p/demo/json" +) + +func TestJSONRender(t *testing.T) { + createdAt := time.Date(2021, time.August, 1, 0, 0, 0, 0, time.UTC) + duration := time.Hour * 24 * 30 + expireAt := createdAt.Add(duration) + + // golden contract + contract := Contract{ + id: 1, + sender: std.Address("sender"), + contractor: std.Address("contractor2"), + contractorCandidates: []std.Address{"contractor1", "contractor2"}, + funder: "funder", + paymentDenom: "denom", + metadata: "metadata", + status: CREATED, + expireAt: expireAt, + funderFeedback: "funderFeedback", + contractorFeedback: "contractorFeedback", + milestones: []Milestone{ + { + id: 1, + title: "title", + desc: "desc", + amount: 100, + paid: 0, + duration: duration, + link: "link", + funded: false, + priority: MS_PRIORITY_HIGH, + status: MS_OPEN, + }, + }, + pausedBy: "pausedBy", + conflictHandler: "conflictHandler", + handlerCandidate: "handlerCandidate", + handlerSuggestor: "handlerSuggestor", + createdAt: createdAt, + budget: 1000, + funded: false, + rejectReason: "rejectReason", + conflicts: []Conflict{ + { + initiator: "initiator", + createdAt: createdAt, + respondedAt: nil, + resolvedAt: nil, + initiatorMessage: "initiatorMessage", + responseMessage: nil, + resolutionMessage: nil, + outcome: nil, + }, + }, + } + + output, err := json.Marshal(contract.ToJSON()) + if err != nil { + t.Fatalf("Error marshalling contract to JSON: %s", err) + } + + expected := `{"id":"1","sender":"sender","contractor":"contractor2","contractorCandidates":["contractor1","contractor2"],"funder":"funder","paymentDenom":"denom","metadata":"metadata","status":"CREATED","expireAt":"2021-08-31T00:00:00Z","funderFeedback":"funderFeedback","contractorFeedback":"contractorFeedback","milestones":[{"id":"1","title":"title","desc":"desc","amount":"100","paid":"0","duration":2592000,"link":"link","funded":false,"priority":"MS_PRIORITY_HIGH","status":"MS_OPEN"}],"pausedBy":"pausedBy","conflictHandler":"conflictHandler","handlerCandidate":"handlerCandidate","handlerSuggestor":"handlerSuggestor","createdAt":"2021-08-01T00:00:00Z","budget":"1000","funded":false,"rejectReason":"rejectReason","conflicts":[{"initiator":"initiator","createdAt":"2021-08-01T00:00:00Z","initiatorMessage":"initiatorMessage"}]}` + if string(output) != expected { + t.Errorf("Expected output to be `%s`, got:\n`%s`", expected, string(output)) + } +} diff --git a/gno/r/social_feeds/gno.mod b/gno/r/social_feeds/gno.mod index 29da62ea3d..4da5feb0eb 100644 --- a/gno/r/social_feeds/gno.mod +++ b/gno/r/social_feeds/gno.mod @@ -2,9 +2,9 @@ module gno.land/r/teritori/social_feeds require ( gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest gno.land/p/teritori/dao_interfaces v0.0.0-latest gno.land/p/teritori/flags_index v0.0.0-latest gno.land/p/teritori/ujson v0.0.0-latest - gno.land/p/demo/testutils v0.0.0-latest - gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/go/pkg/networks/features.gen.go b/go/pkg/networks/features.gen.go index 06b1db6db7..176738c009 100644 --- a/go/pkg/networks/features.gen.go +++ b/go/pkg/networks/features.gen.go @@ -18,6 +18,7 @@ const ( FeatureTypeRiotP2E = FeatureType("RiotP2E") FeatureTypeNFTBridge = FeatureType("NFTBridge") FeatureTypeCosmWasmPremiumFeed = FeatureType("CosmWasmPremiumFeed") + FeatureTypeGnoProjectManager = FeatureType("GnoProjectManager") FeatureTypeNFTMarketplaceLeaderboard = FeatureType("NFTMarketplaceLeaderboard") FeatureTypeCosmWasmNFTsBurner = FeatureType("CosmWasmNFTsBurner") ) @@ -61,6 +62,26 @@ func (nb *NetworkBase) GetFeatureCosmWasmNFTsBurner() (*FeatureCosmWasmNFTsBurne return feature.(*FeatureCosmWasmNFTsBurner), nil } +type FeatureGnoProjectManager struct { + *FeatureBase + ProjectsManagerPkgPath string `json:"projectsManagerPkgPath"` + PaymentsDenom string `json:"paymentsDenom"` +} + +var _ Feature = &FeatureGnoProjectManager{} + +func (f FeatureGnoProjectManager) Type() FeatureType { + return FeatureTypeGnoProjectManager +} + +func (nb *NetworkBase) GetFeatureGnoProjectManager() (*FeatureGnoProjectManager, error) { + feature, err := nb.GetFeature(FeatureTypeGnoProjectManager) + if err != nil { + return nil, err + } + return feature.(*FeatureGnoProjectManager), nil +} + func UnmarshalFeature(b []byte) (Feature, error) { var base FeatureBase if err := json.Unmarshal(b, &base); err != nil { @@ -79,6 +100,12 @@ func UnmarshalFeature(b []byte) (Feature, error) { return nil, errors.Wrap(err, "failed to unmarshal feature CosmWasmNFTsBurner") } return &f, nil + case FeatureTypeGnoProjectManager: + var f FeatureGnoProjectManager + if err := json.Unmarshal(b, &f); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal feature GnoProjectManager") + } + return &f, nil } return nil, errors.Errorf("unknown feature type %s", base.Type) } diff --git a/networks.json b/networks.json index e001ce5d81..12a222e74e 100644 --- a/networks.json +++ b/networks.json @@ -10,6 +10,7 @@ "kind": "native", "denom": "uebl", "decimals": 6, + "variant": "cosmos", "displayName": "EBL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/8ball/images/8ball.svg", @@ -48,6 +49,7 @@ "kind": "native", "denom": "aacre", "decimals": 18, + "variant": "cosmos", "displayName": "ACRE", "coingeckoId": "arable-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/acrechain/images/acre.svg", @@ -57,6 +59,7 @@ "kind": "native", "denom": "erc20/0x2Cbea61fdfDFA520Ee99700F104D5b75ADf50B0c", "decimals": 18, + "variant": "cosmos", "displayName": "arUSD", "coingeckoId": "arable-usd", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/acrechain/images/arusd.svg", @@ -66,6 +69,7 @@ "kind": "native", "denom": "erc20/0xAE6D3334989a22A65228732446731438672418F2", "decimals": 18, + "variant": "cosmos", "displayName": "CNTO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/acrechain/images/cnto.svg", @@ -104,6 +108,7 @@ "kind": "native", "denom": "ubld", "decimals": 6, + "variant": "cosmos", "displayName": "BLD", "coingeckoId": "agoric", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/agoric/images/bld.svg", @@ -113,6 +118,7 @@ "kind": "native", "denom": "uist", "decimals": 6, + "variant": "cosmos", "displayName": "IST", "coingeckoId": "inter-stable-token", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/agoric/images/ist.svg", @@ -151,6 +157,7 @@ "kind": "native", "denom": "attoaioz", "decimals": 18, + "variant": "cosmos", "displayName": "AIOZ", "coingeckoId": "aioz-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/aioz/images/aioz.svg", @@ -189,6 +196,7 @@ "kind": "native", "denom": "uakt", "decimals": 6, + "variant": "cosmos", "displayName": "AKT", "coingeckoId": "akash-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/akash/images/akt.svg", @@ -227,6 +235,7 @@ "kind": "native", "denom": "uandr", "decimals": 6, + "variant": "cosmos", "displayName": "ANDR", "coingeckoId": "andromeda-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/andromeda/images/andromeda-logo.png", @@ -265,6 +274,7 @@ "kind": "native", "denom": "utia", "decimals": 6, + "variant": "cosmos", "displayName": "TIA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/celestiatestnet2/images/celestia.svg", @@ -302,6 +312,7 @@ "kind": "native", "denom": "aarch", "decimals": 18, + "variant": "cosmos", "displayName": "ARCH", "coingeckoId": "archway", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/archway/images/archway.svg", @@ -311,6 +322,7 @@ "kind": "native", "denom": "cw20:archway1fwurjg7ah4v7hhs6xsc3wutqpvmahrfhns285s0lt34tgfdhplxq6m8xg5", "decimals": 6, + "variant": "cosmos", "displayName": "ampARCH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/archway/images/amparch.png", @@ -349,6 +361,7 @@ "kind": "native", "denom": "aconst", "decimals": 18, + "variant": "cosmos", "displayName": "CONST", "coingeckoId": "not-found", "icon": "not-found", @@ -387,6 +400,7 @@ "kind": "native", "denom": "uarkeo", "decimals": 6, + "variant": "cosmos", "displayName": "ARKEO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/arkeonetworktestnet/images/arkeo.png", @@ -424,6 +438,7 @@ "kind": "native", "denom": "arkh", "decimals": 6, + "variant": "cosmos", "displayName": "ARKH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/arkh/images/arkh.svg", @@ -462,6 +477,7 @@ "kind": "native", "denom": "uart", "decimals": 18, + "variant": "cosmos", "displayName": "ART", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/artelatestnet/images/artela.png", @@ -499,6 +515,7 @@ "kind": "native", "denom": "umntl", "decimals": 6, + "variant": "cosmos", "displayName": "MNTL", "coingeckoId": "assetmantle", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/assetmantle/images/mntl.svg", @@ -537,6 +554,7 @@ "kind": "native", "denom": "ueaura", "decimals": 6, + "variant": "cosmos", "displayName": "EAURA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/aura/images/Aura-logo-2.2.svg", @@ -575,6 +593,7 @@ "kind": "native", "denom": "uaura", "decimals": 6, + "variant": "cosmos", "displayName": "AURA", "coingeckoId": "aura-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/aura/images/Aura-logo-2.2.svg", @@ -613,6 +632,7 @@ "kind": "native", "denom": "uaxl", "decimals": 6, + "variant": "cosmos", "displayName": "AXL", "coingeckoId": "axelar", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/axl.svg", @@ -622,6 +642,7 @@ "kind": "native", "denom": "uusdc", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "axlusdc", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/usdc.svg", @@ -631,6 +652,7 @@ "kind": "native", "denom": "frax-wei", "decimals": 18, + "variant": "cosmos", "displayName": "FRAX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/frax.svg", @@ -640,6 +662,7 @@ "kind": "native", "denom": "dai-wei", "decimals": 18, + "variant": "cosmos", "displayName": "DAI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/dai.svg", @@ -649,6 +672,7 @@ "kind": "native", "denom": "uusdt", "decimals": 6, + "variant": "cosmos", "displayName": "USDT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/usdt.svg", @@ -658,6 +682,7 @@ "kind": "native", "denom": "weth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/weth.png", @@ -667,6 +692,7 @@ "kind": "native", "denom": "wbtc-satoshi", "decimals": 8, + "variant": "cosmos", "displayName": "WBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/wbtc.png", @@ -676,6 +702,7 @@ "kind": "native", "denom": "aave-wei", "decimals": 18, + "variant": "cosmos", "displayName": "AAVE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/aave.svg", @@ -685,6 +712,7 @@ "kind": "native", "denom": "ape-wei", "decimals": 18, + "variant": "cosmos", "displayName": "APE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/ape.svg", @@ -694,6 +722,7 @@ "kind": "native", "denom": "axs-wei", "decimals": 18, + "variant": "cosmos", "displayName": "AXS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/axs.svg", @@ -703,6 +732,7 @@ "kind": "native", "denom": "link-wei", "decimals": 18, + "variant": "cosmos", "displayName": "LINK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/link.svg", @@ -712,6 +742,7 @@ "kind": "native", "denom": "mkr-wei", "decimals": 18, + "variant": "cosmos", "displayName": "MKR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/mkr.svg", @@ -721,6 +752,7 @@ "kind": "native", "denom": "rai-wei", "decimals": 18, + "variant": "cosmos", "displayName": "RAI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/rai.svg", @@ -730,6 +762,7 @@ "kind": "native", "denom": "shib-wei", "decimals": 18, + "variant": "cosmos", "displayName": "SHIB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/shib.svg", @@ -739,6 +772,7 @@ "kind": "native", "denom": "steth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "stETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/steth.svg", @@ -748,6 +782,7 @@ "kind": "native", "denom": "uni-wei", "decimals": 18, + "variant": "cosmos", "displayName": "UNI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/uni.svg", @@ -757,6 +792,7 @@ "kind": "native", "denom": "xcn-wei", "decimals": 18, + "variant": "cosmos", "displayName": "XCN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/xcn.svg", @@ -766,6 +802,7 @@ "kind": "native", "denom": "dot-planck", "decimals": 10, + "variant": "cosmos", "displayName": "DOT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/polkadot/images/dot.svg", @@ -775,6 +812,7 @@ "kind": "native", "denom": "wglmr-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WGLMR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/moonbeam/images/glmr.svg", @@ -784,6 +822,7 @@ "kind": "native", "denom": "wmatic-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WMATIC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/polygon/images/wmatic.svg", @@ -793,6 +832,7 @@ "kind": "native", "denom": "wbnb-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WBNB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/binancesmartchain/images/wbnb.svg", @@ -802,6 +842,7 @@ "kind": "native", "denom": "busd-wei", "decimals": 18, + "variant": "cosmos", "displayName": "BUSD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/busd.png", @@ -811,6 +852,7 @@ "kind": "native", "denom": "wavax-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WAVAX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/avalanche/images/wavax.svg", @@ -820,6 +862,7 @@ "kind": "native", "denom": "wftm-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WFTM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/fantom/images/ftm.png", @@ -829,6 +872,7 @@ "kind": "native", "denom": "polygon-uusdc", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/usdc.svg", @@ -838,6 +882,7 @@ "kind": "native", "denom": "avalanche-uusdc", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/usdc.svg", @@ -847,6 +892,7 @@ "kind": "native", "denom": "wfil-wei", "decimals": 18, + "variant": "cosmos", "displayName": "axlFIL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/filecoin/images/wfil.svg", @@ -856,6 +902,7 @@ "kind": "native", "denom": "arb-wei", "decimals": 18, + "variant": "cosmos", "displayName": "ARB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/arbitrum/images/arb.svg", @@ -865,6 +912,7 @@ "kind": "native", "denom": "pepe-wei", "decimals": 18, + "variant": "cosmos", "displayName": "PEPE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/pepe.svg", @@ -874,6 +922,7 @@ "kind": "native", "denom": "cbeth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "cbETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/cbeth.png", @@ -883,6 +932,7 @@ "kind": "native", "denom": "reth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "rETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/reth.png", @@ -892,6 +942,7 @@ "kind": "native", "denom": "sfrxeth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "sfrxETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/sfrxeth.svg", @@ -901,6 +952,7 @@ "kind": "native", "denom": "wsteth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "wstETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/wsteth.svg", @@ -910,6 +962,7 @@ "kind": "native", "denom": "yieldeth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "YieldETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/yieldeth.svg", @@ -948,6 +1001,7 @@ "kind": "native", "denom": "uaxl", "decimals": 6, + "variant": "cosmos", "displayName": "AXL", "coingeckoId": "axelar", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/axl.svg", @@ -957,6 +1011,7 @@ "kind": "native", "denom": "uausdc", "decimals": 6, + "variant": "cosmos", "displayName": "aUSDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdc.svg", @@ -966,6 +1021,7 @@ "kind": "native", "denom": "eth-wei", "decimals": 18, + "variant": "cosmos", "displayName": "axlWETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/weth.svg", @@ -975,6 +1031,7 @@ "kind": "native", "denom": "wglmr-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WDEV", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/moonbeam/images/glmr.svg", @@ -984,6 +1041,7 @@ "kind": "native", "denom": "wmatic-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WMATIC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/polygon/images/wmatic.svg", @@ -993,6 +1051,7 @@ "kind": "native", "denom": "wbnb-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WBNB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/binancesmartchain/images/wbnb.svg", @@ -1002,6 +1061,7 @@ "kind": "native", "denom": "wavax-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WAVAX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/avalanche/images/wavax.svg", @@ -1011,6 +1071,7 @@ "kind": "native", "denom": "wftm-wei", "decimals": 18, + "variant": "cosmos", "displayName": "WFTM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/fantom/images/ftm.png", @@ -1048,6 +1109,7 @@ "kind": "native", "denom": "uatom", "decimals": 6, + "variant": "cosmos", "displayName": "BBN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/babylon/images/logo.svg", @@ -1086,6 +1148,7 @@ "kind": "native", "denom": "uband", "decimals": 6, + "variant": "cosmos", "displayName": "BAND", "coingeckoId": "band-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bandchain/images/band.svg", @@ -1124,6 +1187,7 @@ "kind": "native", "denom": "ubze", "decimals": 6, + "variant": "cosmos", "displayName": "BZE", "coingeckoId": "bzedge", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/beezee/images/bze.svg", @@ -1162,6 +1226,7 @@ "kind": "native", "denom": "ubcna", "decimals": 6, + "variant": "cosmos", "displayName": "BCNA", "coingeckoId": "bitcanna", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitcanna/images/bcna.svg", @@ -1200,6 +1265,7 @@ "kind": "native", "denom": "ubcna", "decimals": 6, + "variant": "cosmos", "displayName": "BCNA", "coingeckoId": "bitcanna", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/bitcannadevnet/images/bcna.svg", @@ -1237,6 +1303,7 @@ "kind": "native", "denom": "ubcna", "decimals": 6, + "variant": "cosmos", "displayName": "BCNA", "coingeckoId": "bitcanna", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/bitcannadevnet2/images/bcna.svg", @@ -1274,6 +1341,7 @@ "kind": "native", "denom": "ubtsg", "decimals": 6, + "variant": "cosmos", "displayName": "BTSG", "coingeckoId": "bitsong", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/btsg.svg", @@ -1283,6 +1351,7 @@ "kind": "native", "denom": "ft2D8E7041556CE93E1EFD66C07C45D551A6AAAE09", "decimals": 6, + "variant": "cosmos", "displayName": "CLAY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft2D8E7041556CE93E1EFD66C07C45D551A6AAAE09.png", @@ -1292,6 +1361,7 @@ "kind": "native", "denom": "ft25B30C386CDDEBD1413D5AE1180956AE9EB3B9F7", "decimals": 6, + "variant": "cosmos", "displayName": "FASANO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft25B30C386CDDEBD1413D5AE1180956AE9EB3B9F7.png", @@ -1301,6 +1371,7 @@ "kind": "native", "denom": "ft575B10B0CEE2C164D9ED6A96313496F164A9607C", "decimals": 6, + "variant": "cosmos", "displayName": "D9X", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft575B10B0CEE2C164D9ED6A96313496F164A9607C.png", @@ -1310,6 +1381,7 @@ "kind": "native", "denom": "ft56664FC98A2CF5F4FBAC3566D1A11D891AD88305", "decimals": 6, + "variant": "cosmos", "displayName": "FONTI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft56664FC98A2CF5F4FBAC3566D1A11D891AD88305.png", @@ -1319,6 +1391,7 @@ "kind": "native", "denom": "ft52EEB0EE509AC546ED92EAC8591F731F213DDD16", "decimals": 6, + "variant": "cosmos", "displayName": "BJKS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft52EEB0EE509AC546ED92EAC8591F731F213DDD16.png", @@ -1328,6 +1401,7 @@ "kind": "native", "denom": "ftE4903ECC861CA45F2C2BC7EAB8255D2E6E87A33A", "decimals": 6, + "variant": "cosmos", "displayName": "RWNE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ftE4903ECC861CA45F2C2BC7EAB8255D2E6E87A33A.png", @@ -1337,6 +1411,7 @@ "kind": "native", "denom": "ft85AE1716C5E39EA6D64BBD7898C3899A7B500626", "decimals": 6, + "variant": "cosmos", "displayName": "ENMODA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft85AE1716C5E39EA6D64BBD7898C3899A7B500626.png", @@ -1346,6 +1421,7 @@ "kind": "native", "denom": "ft99091610CCC66F4277C66D14AF2BC4C5EE52E27A", "decimals": 6, + "variant": "cosmos", "displayName": "404DR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft99091610CCC66F4277C66D14AF2BC4C5EE52E27A.png", @@ -1355,6 +1431,7 @@ "kind": "native", "denom": "ft387C1C279D962ED80C09C1D592A92C4275FD7C5D", "decimals": 6, + "variant": "cosmos", "displayName": "N43", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft387C1C279D962ED80C09C1D592A92C4275FD7C5D.png", @@ -1364,6 +1441,7 @@ "kind": "native", "denom": "ft24C9FA4F10B0F235F4A815B15FC774E046A2B2EB", "decimals": 6, + "variant": "cosmos", "displayName": "LOBO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft24C9FA4F10B0F235F4A815B15FC774E046A2B2EB.png", @@ -1373,6 +1451,7 @@ "kind": "native", "denom": "ft7020C2A8E984EEBCBB383E91CD6FBB067BB2272B", "decimals": 6, + "variant": "cosmos", "displayName": "VIBRA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft7020C2A8E984EEBCBB383E91CD6FBB067BB2272B.png", @@ -1382,6 +1461,7 @@ "kind": "native", "denom": "ft2DD67F5D99E9A141142B48474FA7B6B3FF00A3FE", "decimals": 6, + "variant": "cosmos", "displayName": "KARINA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft2DD67F5D99E9A141142B48474FA7B6B3FF00A3FE.png", @@ -1391,6 +1471,7 @@ "kind": "native", "denom": "ft4B030260D99E3ABE2B604EA2B33BAF3C085CDA12", "decimals": 6, + "variant": "cosmos", "displayName": "TESTA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft4B030260D99E3ABE2B604EA2B33BAF3C085CDA12.png", @@ -1400,6 +1481,7 @@ "kind": "native", "denom": "ftD4B6290EDEE1EC7B97AB5A1DC6C177EFD08ADCC3", "decimals": 6, + "variant": "cosmos", "displayName": "CMQZ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ftD4B6290EDEE1EC7B97AB5A1DC6C177EFD08ADCC3.png", @@ -1409,6 +1491,7 @@ "kind": "native", "denom": "ft347B1612A2B7659913679CF6CD45B8B130C50A00", "decimals": 6, + "variant": "cosmos", "displayName": "LDON", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bitsong/images/ft347B1612A2B7659913679CF6CD45B8B130C50A00.png", @@ -1447,6 +1530,7 @@ "kind": "native", "denom": "utia", "decimals": 6, + "variant": "cosmos", "displayName": "TIA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/celestiatestnet/images/celestia.svg", @@ -1484,6 +1568,7 @@ "kind": "native", "denom": "ubnt", "decimals": 6, + "variant": "cosmos", "displayName": "BLZ", "coingeckoId": "bluzelle", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bluzelle/images/bluzelle.svg", @@ -1493,6 +1578,7 @@ "kind": "native", "denom": "uelt", "decimals": 6, + "variant": "cosmos", "displayName": "ELT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bluzelle/images/elt.png", @@ -1502,6 +1588,7 @@ "kind": "native", "denom": "ug4", "decimals": 6, + "variant": "cosmos", "displayName": "G4", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bluzelle/images/g4.png", @@ -1540,6 +1627,7 @@ "kind": "native", "denom": "boot", "decimals": 6, + "variant": "cosmos", "displayName": "BOOT", "coingeckoId": "bostrom", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bostrom/images/boot.svg", @@ -1549,6 +1637,7 @@ "kind": "native", "denom": "hydrogen", "decimals": 6, + "variant": "cosmos", "displayName": "HYDROGEN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bostrom/images/hydrogen.svg", @@ -1558,6 +1647,7 @@ "kind": "native", "denom": "milliampere", "decimals": 3, + "variant": "cosmos", "displayName": "A", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bostrom/images/ampere.svg", @@ -1567,6 +1657,7 @@ "kind": "native", "denom": "millivolt", "decimals": 3, + "variant": "cosmos", "displayName": "V", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bostrom/images/volt.svg", @@ -1576,6 +1667,7 @@ "kind": "native", "denom": "tocyb", "decimals": 6, + "variant": "cosmos", "displayName": "TOCYB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/bostrom/images/tocyb.svg", @@ -1614,6 +1706,7 @@ "kind": "native", "denom": "acanto", "decimals": 18, + "variant": "cosmos", "displayName": "CANTO", "coingeckoId": "canto", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/canto/images/canto.svg", @@ -1652,6 +1745,7 @@ "kind": "native", "denom": "swth", "decimals": 8, + "variant": "cosmos", "displayName": "SWTH", "coingeckoId": "switcheo", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/carbon/images/swth.svg", @@ -1661,6 +1755,7 @@ "kind": "native", "denom": "usc", "decimals": 8, + "variant": "cosmos", "displayName": "USC", "coingeckoId": "carbon-usd", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/carbon/images/usc.svg", @@ -1670,6 +1765,7 @@ "kind": "native", "denom": "bnb.1.6.773edb", "decimals": 18, + "variant": "cosmos", "displayName": "BNB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/binancesmartchain/images/bnb.svg", @@ -1679,6 +1775,7 @@ "kind": "native", "denom": "bneo.1.14.e2e5f6", "decimals": 8, + "variant": "cosmos", "displayName": "bNEO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/carbon/images/bneo.svg", @@ -1688,6 +1785,7 @@ "kind": "native", "denom": "busd.1.6.754a80", "decimals": 18, + "variant": "cosmos", "displayName": "BUSD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/busd.png", @@ -1697,6 +1795,7 @@ "kind": "native", "denom": "cglp.1.19.1698d3", "decimals": 18, + "variant": "cosmos", "displayName": "CGLP", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/carbon/images/cglp.svg", @@ -1706,6 +1805,7 @@ "kind": "native", "denom": "cgt/1", "decimals": 18, + "variant": "cosmos", "displayName": "USD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/carbon/images/carbon-grouped-usd.svg", @@ -1715,6 +1815,7 @@ "kind": "native", "denom": "eth.1.19.c3b805", "decimals": 18, + "variant": "cosmos", "displayName": "ETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/eth.svg", @@ -1724,6 +1825,7 @@ "kind": "native", "denom": "eth.1.2.942d87", "decimals": 18, + "variant": "cosmos", "displayName": "ETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/eth.svg", @@ -1733,6 +1835,7 @@ "kind": "native", "denom": "usdc.1.2.343151", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdc.svg", @@ -1742,6 +1845,7 @@ "kind": "native", "denom": "usdc.1.6.53ff75", "decimals": 18, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdc.svg", @@ -1751,6 +1855,7 @@ "kind": "native", "denom": "zil.1.18.1a4a06", "decimals": 12, + "variant": "cosmos", "displayName": "ZIL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/carbon/images/zil.svg", @@ -1789,6 +1894,7 @@ "kind": "native", "denom": "aCC", "decimals": 18, + "variant": "cosmos", "displayName": "TCC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/cascadiatestnet/images/cascadia.png", @@ -1827,6 +1933,7 @@ "kind": "native", "denom": "utia", "decimals": 6, + "variant": "cosmos", "displayName": "TIA", "coingeckoId": "celestia", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/celestia/images/celestia.svg", @@ -1865,6 +1972,7 @@ "kind": "native", "denom": "ucrbrus", "decimals": 6, + "variant": "cosmos", "displayName": "CRBRUS", "coingeckoId": "cerberus-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/cerberus/images/crbrus.svg", @@ -1903,6 +2011,7 @@ "kind": "native", "denom": "uc4e", "decimals": 6, + "variant": "cosmos", "displayName": "C4E", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chain4energy/images/c4e.png", @@ -1941,6 +2050,7 @@ "kind": "native", "denom": "uc4e", "decimals": 6, + "variant": "cosmos", "displayName": "C4E", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/chain4energytestnet/images/c4e.png", @@ -1979,6 +2089,7 @@ "kind": "native", "denom": "ncheq", "decimals": 9, + "variant": "cosmos", "displayName": "CHEQ", "coingeckoId": "cheqd-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/cheqd/images/cheq.svg", @@ -2017,6 +2128,7 @@ "kind": "native", "denom": "ncheq", "decimals": 9, + "variant": "cosmos", "displayName": "CHEQ", "coingeckoId": "cheqd-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/cheqdtestnet/images/cheq.svg", @@ -2055,6 +2167,7 @@ "kind": "native", "denom": "uhuahua", "decimals": 6, + "variant": "cosmos", "displayName": "HUAHUA", "coingeckoId": "chihuahua-token", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/huahua.svg", @@ -2064,6 +2177,7 @@ "kind": "native", "denom": "cw20:chihuahua1yl8z39ugle8s02fpwkhh293509q5xcpalmdzc4amvchz8nkexrmsy95gef", "decimals": 6, + "variant": "cosmos", "displayName": "PUPPY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/puppyhuahua_logo.png", @@ -2073,6 +2187,7 @@ "kind": "native", "denom": "factory/chihuahua1x4q2vkrz4dfgd9hcw0p5m2f2nuv2uqmt9xr8k2/achihuahuawifhat", "decimals": 6, + "variant": "cosmos", "displayName": "BADDOG", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/baddog.png", @@ -2082,6 +2197,7 @@ "kind": "native", "denom": "factory/chihuahua13jawsn574rf3f0u5rhu7e8n6sayx5gkw3eddhp/uwoof", "decimals": 6, + "variant": "cosmos", "displayName": "WOOF", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/woof.png", @@ -2091,6 +2207,7 @@ "kind": "native", "denom": "factory/chihuahua13jawsn574rf3f0u5rhu7e8n6sayx5gkw3eddhp/utacos", "decimals": 6, + "variant": "cosmos", "displayName": "TACOS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/tacos.png", @@ -2100,6 +2217,7 @@ "kind": "native", "denom": "factory/chihuahua13jawsn574rf3f0u5rhu7e8n6sayx5gkw3eddhp/uweed", "decimals": 6, + "variant": "cosmos", "displayName": "WEED", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/weed.png", @@ -2109,6 +2227,7 @@ "kind": "native", "denom": "factory/chihuahua13jawsn574rf3f0u5rhu7e8n6sayx5gkw3eddhp/ubdog", "decimals": 6, + "variant": "cosmos", "displayName": "BDOG", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/bdog.png", @@ -2118,6 +2237,7 @@ "kind": "native", "denom": "factory/chihuahua13jawsn574rf3f0u5rhu7e8n6sayx5gkw3eddhp/ucorso", "decimals": 6, + "variant": "cosmos", "displayName": "CORSO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chihuahua/images/corso.png", @@ -2156,6 +2276,7 @@ "kind": "native", "denom": "ucmba", "decimals": 6, + "variant": "cosmos", "displayName": "CMBA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chimba/images/chimba-blue.svg", @@ -2194,6 +2315,7 @@ "kind": "native", "denom": "ucmba", "decimals": 6, + "variant": "cosmos", "displayName": "CMBA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chimba/images/chimba-blue.svg", @@ -2231,6 +2353,7 @@ "kind": "native", "denom": "ucht", "decimals": 6, + "variant": "cosmos", "displayName": "CHT", "coingeckoId": "cht", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chronicnetwork/images/cht.png", @@ -2240,6 +2363,7 @@ "kind": "native", "denom": "ucgas", "decimals": 6, + "variant": "cosmos", "displayName": "CGAS", "coingeckoId": "cgas", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/chronicnetwork/images/cgas.png", @@ -2277,6 +2401,7 @@ "kind": "native", "denom": "ucmdx", "decimals": 6, + "variant": "cosmos", "displayName": "CMDX", "coingeckoId": "comdex", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/comdex/images/cmdx.svg", @@ -2286,6 +2411,7 @@ "kind": "native", "denom": "uharbor", "decimals": 6, + "variant": "cosmos", "displayName": "HARBOR", "coingeckoId": "harbor-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/comdex/images/harbor.svg", @@ -2295,6 +2421,7 @@ "kind": "native", "denom": "ucmst", "decimals": 6, + "variant": "cosmos", "displayName": "CMST", "coingeckoId": "composite", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/comdex/images/cmst.svg", @@ -2333,6 +2460,7 @@ "kind": "native", "denom": "ucommercio", "decimals": 6, + "variant": "cosmos", "displayName": "COM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/commercionetwork/images/com.svg", @@ -2342,6 +2470,7 @@ "kind": "native", "denom": "uccc", "decimals": 6, + "variant": "cosmos", "displayName": "CCC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/commercionetwork/images/ccc.svg", @@ -2380,6 +2509,7 @@ "kind": "native", "denom": "ppica", "decimals": 12, + "variant": "cosmos", "displayName": "PICA", "coingeckoId": "picasso", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/composable/images/pica.svg", @@ -2418,6 +2548,7 @@ "kind": "native", "denom": "ppica", "decimals": 12, + "variant": "cosmos", "displayName": "PICA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/composable/images/composable.svg", @@ -2455,6 +2586,7 @@ "kind": "native", "denom": "acvnt", "decimals": 18, + "variant": "cosmos", "displayName": "CVN", "coingeckoId": "consciousdao", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/conscious/images/cvn.svg", @@ -2493,6 +2625,7 @@ "kind": "native", "denom": "uccat", "decimals": 6, + "variant": "cosmos", "displayName": "CCAT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/coolcattestnet/images/coolcat.svg", @@ -2530,6 +2663,7 @@ "kind": "native", "denom": "utestcore", "decimals": 6, + "variant": "cosmos", "displayName": "TESTCORE", "coingeckoId": "coreum", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/coreum/images/coreum.svg", @@ -2569,6 +2703,7 @@ "denom": "uatom", "displayName": "ATOM", "decimals": 6, + "variant": "cosmos", "coingeckoId": "cosmos", "icon": "cosmos-hub-circle.svg", "kind": "native", @@ -2614,6 +2749,7 @@ "denom": "uatom", "displayName": "ATOM", "decimals": 6, + "variant": "cosmos", "coingeckoId": "cosmos", "icon": "cosmos-hub-circle.svg", "kind": "native", @@ -2654,6 +2790,7 @@ "kind": "native", "denom": "umlg", "decimals": 6, + "variant": "cosmos", "displayName": "MLG", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/cosmwasmtestnet/images/cosmwasm.svg", @@ -2663,6 +2800,7 @@ "kind": "native", "denom": "uand", "decimals": 6, + "variant": "cosmos", "displayName": "AND", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/cosmwasmtestnet/images/cosmwasm.svg", @@ -2700,6 +2838,7 @@ "kind": "native", "denom": "ucgas", "decimals": 6, + "variant": "cosmos", "displayName": "CGAS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/coss/images/cgas.svg", @@ -2709,6 +2848,7 @@ "kind": "native", "denom": "ucoss", "decimals": 6, + "variant": "cosmos", "displayName": "COSS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/coss/images/coss.svg", @@ -2747,6 +2887,7 @@ "kind": "native", "denom": "ucgas", "decimals": 6, + "variant": "cosmos", "displayName": "cgas", "coingeckoId": "not-found", "icon": "not-found", @@ -2784,6 +2925,7 @@ "kind": "native", "denom": "ucre", "decimals": 6, + "variant": "cosmos", "displayName": "CRE", "coingeckoId": "crescent-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/crescent/images/cre.svg", @@ -2793,6 +2935,7 @@ "kind": "native", "denom": "ubcre", "decimals": 6, + "variant": "cosmos", "displayName": "bCRE", "coingeckoId": "liquid-staking-crescent", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/crescent/images/bcre.svg", @@ -2831,6 +2974,7 @@ "kind": "native", "denom": "basecro", "decimals": 18, + "variant": "cosmos", "displayName": "CRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/cronos/images/cro.svg", @@ -2869,6 +3013,7 @@ "kind": "native", "denom": "basecro", "decimals": 8, + "variant": "cosmos", "displayName": "CRO", "coingeckoId": "crypto-com-chain", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/cronos/images/cro.svg", @@ -2907,6 +3052,7 @@ "kind": "native", "denom": "acudos", "decimals": 18, + "variant": "cosmos", "displayName": "CUDOS", "coingeckoId": "cudos", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/cudos/images/cudos.svg", @@ -2945,6 +3091,7 @@ "kind": "native", "denom": "acudos", "decimals": 18, + "variant": "cosmos", "displayName": "CUDOS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/cudostestnet/images/cudos.svg", @@ -2982,6 +3129,7 @@ "kind": "native", "denom": "udear", "decimals": 6, + "variant": "cosmos", "displayName": "DEAR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/deardogetestnet/images/deardoge.png", @@ -3019,6 +3167,7 @@ "kind": "native", "denom": "udec", "decimals": 6, + "variant": "cosmos", "displayName": "DEC", "coingeckoId": "decentr", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/decentr/images/dec.svg", @@ -3057,6 +3206,7 @@ "kind": "native", "denom": "udsm", "decimals": 6, + "variant": "cosmos", "displayName": "DSM", "coingeckoId": "desmos", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/desmos/images/dsm.svg", @@ -3095,6 +3245,7 @@ "kind": "native", "denom": "udaric", "decimals": 6, + "variant": "cosmos", "displayName": "DARIC", "coingeckoId": "desmos", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/desmos/images/dsm.svg", @@ -3133,6 +3284,7 @@ "kind": "native", "denom": "uself", "decimals": 6, + "variant": "cosmos", "displayName": "SELF", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/selfchaindevnet/images/selfchain.png", @@ -3170,6 +3322,7 @@ "kind": "native", "denom": "udig", "decimals": 6, + "variant": "cosmos", "displayName": "DIG", "coingeckoId": "dig-chain", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/dig/images/dig.png", @@ -3208,6 +3361,7 @@ "kind": "native", "denom": "peaka", "decimals": 18, + "variant": "cosmos", "displayName": "DORA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/doravota/images/dora.svg", @@ -3245,6 +3399,7 @@ "kind": "native", "denom": "peaka", "decimals": 18, + "variant": "cosmos", "displayName": "DORA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/doravotatestnet/images/doravota.png", @@ -3282,6 +3437,7 @@ "kind": "native", "denom": "peaka", "decimals": 18, + "variant": "cosmos", "displayName": "DORA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/doravotatestnet/images/doravota.png", @@ -3319,6 +3475,7 @@ "kind": "native", "denom": "adydx", "decimals": 18, + "variant": "cosmos", "displayName": "DYDX", "coingeckoId": "dydx", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/dydx/images/dydx.svg", @@ -3357,6 +3514,7 @@ "kind": "native", "denom": "adydx", "decimals": 18, + "variant": "cosmos", "displayName": "DYDX", "coingeckoId": "dydx", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/dydx/images/dydx.svg", @@ -3395,6 +3553,7 @@ "kind": "native", "denom": "dys", "decimals": 6, + "variant": "cosmos", "displayName": "DYS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/dyson/images/dys.svg", @@ -3433,6 +3592,7 @@ "kind": "native", "denom": "ungm", "decimals": 6, + "variant": "cosmos", "displayName": "NGM", "coingeckoId": "e-money", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/emoney/images/ngm.svg", @@ -3442,6 +3602,7 @@ "kind": "native", "denom": "eeur", "decimals": 6, + "variant": "cosmos", "displayName": "EEUR", "coingeckoId": "e-money-eur", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/emoney/images/eeur.svg", @@ -3451,6 +3612,7 @@ "kind": "native", "denom": "echf", "decimals": 6, + "variant": "cosmos", "displayName": "ECHF", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/emoney/images/echf.svg", @@ -3460,6 +3622,7 @@ "kind": "native", "denom": "enok", "decimals": 6, + "variant": "cosmos", "displayName": "ENOK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/emoney/images/enok.svg", @@ -3469,6 +3632,7 @@ "kind": "native", "denom": "edkk", "decimals": 6, + "variant": "cosmos", "displayName": "EDKK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/emoney/images/edkk.svg", @@ -3478,6 +3642,7 @@ "kind": "native", "denom": "esek", "decimals": 6, + "variant": "cosmos", "displayName": "ESEK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/emoney/images/esek.svg", @@ -3516,6 +3681,7 @@ "kind": "native", "denom": "aechelon", "decimals": 18, + "variant": "cosmos", "displayName": "ECH", "coingeckoId": "echelon", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/echelon/images/ech.svg", @@ -3554,6 +3720,7 @@ "kind": "native", "denom": "uelys", "decimals": 6, + "variant": "cosmos", "displayName": "ELYS", "coingeckoId": "elys", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/elystestnet/images/elys.png", @@ -3563,6 +3730,7 @@ "kind": "native", "denom": "ueden", "decimals": 6, + "variant": "cosmos", "displayName": "EDEN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/elystestnet/images/eden.png", @@ -3600,6 +3768,7 @@ "kind": "native", "denom": "umpwr", "decimals": 6, + "variant": "cosmos", "displayName": "MPWR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/empowertestnet/images/mpwr.png", @@ -3637,6 +3806,7 @@ "kind": "native", "denom": "umpwr", "decimals": 6, + "variant": "cosmos", "displayName": "MPWR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/empowerchain/images/mpwr.svg", @@ -3674,6 +3844,7 @@ "kind": "native", "denom": "uentry", "decimals": 6, + "variant": "cosmos", "displayName": "ENTRY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/entrypointtestnet/images/entry.svg", @@ -3717,6 +3888,7 @@ "denom": "0x0000000000000000000000000000000000000000", "displayName": "ETH", "decimals": 18, + "variant": "ethereum", "coingeckoId": "ethereum", "icon": "ethereum-circle.svg", "kind": "native", @@ -3762,6 +3934,7 @@ "denom": "0x0000000000000000000000000000000000000000", "displayName": "GoerliETH", "decimals": 18, + "variant": "ethereum", "coingeckoId": "ethereum", "icon": "ethereum-circle.svg", "kind": "native", @@ -3802,6 +3975,7 @@ "kind": "native", "denom": "aRYT", "decimals": 18, + "variant": "cosmos", "displayName": "RYT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/ethos/images/ethos.svg", @@ -3840,6 +4014,7 @@ "kind": "native", "denom": "aevmos", "decimals": 18, + "variant": "cosmos", "displayName": "EVMOS", "coingeckoId": "evmos", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/evmos/images/evmos.svg", @@ -3849,6 +4024,7 @@ "kind": "native", "denom": "erc20/0x655ecB57432CC1370f65e5dc2309588b71b473A9", "decimals": 18, + "variant": "cosmos", "displayName": "NEOK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/evmos/images/neok.svg", @@ -3887,6 +4063,7 @@ "kind": "native", "denom": "atevmos", "decimals": 18, + "variant": "cosmos", "displayName": "TEVMOS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/evmostestnet/images/evmos.svg", @@ -3924,6 +4101,7 @@ "kind": "native", "denom": "FX", "decimals": 18, + "variant": "cosmos", "displayName": "FX", "coingeckoId": "fx-coin", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/fxcore/images/fx.svg", @@ -3933,6 +4111,7 @@ "kind": "native", "denom": "eth0x0FD10b9899882a6f2fcb5c371E17e70FdEe00C38", "decimals": 18, + "variant": "cosmos", "displayName": "PUNDIX", "coingeckoId": "pundi-x-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/pundix/images/pundi-x-token-logo.svg", @@ -3970,6 +4149,7 @@ "kind": "native", "denom": "afet", "decimals": 18, + "variant": "cosmos", "displayName": "FET", "coingeckoId": "fetch-ai", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/fetchhub/images/fet.svg", @@ -3979,6 +4159,7 @@ "kind": "native", "denom": "nanomobx", "decimals": 9, + "variant": "cosmos", "displayName": "MOBX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/fetchhub/images/mobx.svg", @@ -4017,6 +4198,7 @@ "kind": "native", "denom": "atestfet", "decimals": 6, + "variant": "cosmos", "displayName": "FET", "coingeckoId": "fetch-ai", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/fetchhub/images/fet.svg", @@ -4054,6 +4236,7 @@ "kind": "native", "denom": "ufct", "decimals": 6, + "variant": "cosmos", "displayName": "FCT", "coingeckoId": "firmachain", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/firmachain/images/fct.svg", @@ -4092,6 +4275,7 @@ "kind": "native", "denom": "ufury", "decimals": 6, + "variant": "cosmos", "displayName": "FURY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/furya/images/ufury.svg", @@ -4130,6 +4314,7 @@ "kind": "native", "denom": "uglx", "decimals": 6, + "variant": "cosmos", "displayName": "GLX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/galaxy/images/glx.svg", @@ -4168,6 +4353,7 @@ "kind": "native", "denom": "el1", "decimals": 18, + "variant": "cosmos", "displayName": "L1", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/genesisl1/images/l1.svg", @@ -4206,6 +4392,7 @@ "kind": "native", "denom": "ulore", "decimals": 6, + "variant": "cosmos", "displayName": "LORE", "coingeckoId": "gitopia", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/gitopia/images/lore.svg", @@ -4244,6 +4431,7 @@ "kind": "native", "denom": "utlore", "decimals": 6, + "variant": "cosmos", "displayName": "TLORE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/gitopiatestnet/images/gitopia.png", @@ -4278,13 +4466,32 @@ "features": [ "Organizations", "SocialFeed", - "UPP" + "UPP", + "GnoProjectManager" + ], + "featureObjects": [ + { + "type": "GnoProjectManager", + "projectsManagerPkgPath": "gno.land/r/teritori/projects_manager", + "paymentsDenom": "ugnot" + } ], "currencies": [ { "denom": "ugnot", "displayName": "GNOT", "decimals": 6, + "variant": "gno", + "coingeckoId": "gno", + "icon": "gno.svg", + "kind": "native", + "color": "#232800" + }, + { + "denom": "gno.land/r/demo/tori20", + "displayName": "TORI", + "decimals": 6, + "variant": "grc20", "coingeckoId": "gno", "icon": "gno.svg", "kind": "native", @@ -4294,23 +4501,23 @@ "stakeCurrency": "ugnot", "idPrefix": "gnodev", "chainId": "dev", - "endpoint": "http://127.0.0.1:36657/http://127.0.0.1:26657", + "endpoint": "http://127.0.0.1:26657", "txExplorer": "https://gnoscan.io/transactions/details?txhash=$hash", "accountExplorer": "https://gnoscan.io/accounts/$address", "contractExplorer": "https://gnoscan.io/realms/details?path=$address", "testnet": true, - "backendEndpoint": "http://localhost:9090", + "backendEndpoint": "https://dapp-backend.testnet.teritori.com", "vaultContractAddress": "", - "daoRegistryPkgPath": "gno.land/r/demo/teritori/dao_registry", - "socialFeedsPkgPath": "gno.land/r/demo/teritori/social_feeds", - "socialFeedsDAOPkgPath": "gno.land/r/demo/teritori/social_feeds_dao", + "daoRegistryPkgPath": "gno.land/r/teritori/dao_registry", + "socialFeedsPkgPath": "gno.land/r/teritori/social_feeds", + "socialFeedsDAOPkgPath": "gno.land/r/teritori/social_feeds_dao", "nameServiceContractAddress": "gno.land/r/demo/users", - "modboardsPkgPath": "gno.land/r/demo/teritori/modboards", - "groupsPkgPath": "gno.land/r/demo/teritori/groups", - "votingGroupPkgPath": "gno.land/p/demo/teritori/dao_voting_group", - "daoProposalSinglePkgPath": "gno.land/p/demo/teritori/dao_proposal_single", - "daoInterfacesPkgPath": "gno.land/p/demo/teritori/dao_interfaces", - "daoCorePkgPath": "gno.land/p/demo/teritori/dao_core", + "modboardsPkgPath": "gno.land/r/teritori/modboards", + "groupsPkgPath": "gno.land/r/teritori/groups", + "votingGroupPkgPath": "gno.land/p/teritori/dao_voting_group", + "daoProposalSinglePkgPath": "gno.land/p/teritori/dao_proposal_single", + "daoInterfacesPkgPath": "gno.land/p/teritori/dao_interfaces", + "daoCorePkgPath": "gno.land/p/teritori/dao_core", "nameServiceDefaultImage": "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", "gnowebURL": "http://127.0.0.1:8888" }, @@ -4332,7 +4539,8 @@ "coingeckoId": "gno", "icon": "gno.svg", "kind": "native", - "color": "#232800" + "color": "#232800", + "variant": "gno" } ], "stakeCurrency": "ugnot", @@ -4348,14 +4556,14 @@ "vaultContractAddress": "", "nameServiceContractAddress": "gno.land/r/demo/users", "nameServiceDefaultImage": "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", - "daoRegistryPkgPath": "gno.land/r/demo/teritori/dao_registry", - "socialFeedsPkgPath": "gno.land/r/demo/teritori/social_feeds", - "socialFeedsDAOPkgPath": "gno.land/r/demo/teritori/social_feeds_dao", - "groupsPkgPath": "gno.land/r/demo/teritori/groups", - "votingGroupPkgPath": "gno.land/p/demo/teritori/dao_voting_group", - "daoProposalSinglePkgPath": "gno.land/p/demo/teritori/dao_proposal_single", - "daoInterfacesPkgPath": "gno.land/p/demo/teritori/dao_interfaces", - "daoCorePkgPath": "gno.land/p/demo/teritori/dao_core" + "daoRegistryPkgPath": "gno.land/r/teritori/dao_registry", + "socialFeedsPkgPath": "gno.land/r/teritori/social_feeds", + "socialFeedsDAOPkgPath": "gno.land/r/teritori/social_feeds_dao", + "groupsPkgPath": "gno.land/r/teritori/groups", + "votingGroupPkgPath": "gno.land/p/teritori/dao_voting_group", + "daoProposalSinglePkgPath": "gno.land/p/teritori/dao_proposal_single", + "daoInterfacesPkgPath": "gno.land/p/teritori/dao_interfaces", + "daoCorePkgPath": "gno.land/p/teritori/dao_core" }, { "id": "gno-teritori", @@ -4365,13 +4573,22 @@ "features": [ "Organizations", "SocialFeed", - "UPP" + "UPP", + "GnoProjectManager" + ], + "featureObjects": [ + { + "type": "GnoProjectManager", + "projectsManagerPkgPath": "gno.land/r/teritori/escrow", + "paymentsDenom": "ugnot" + } ], "currencies": [ { "denom": "ugnot", "displayName": "GNOT", "decimals": 6, + "variant": "gno", "coingeckoId": "gno", "icon": "gno.svg", "kind": "native", @@ -4390,15 +4607,15 @@ "vaultContractAddress": "", "nameServiceContractAddress": "gno.land/r/demo/users", "nameServiceDefaultImage": "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", - "daoRegistryPkgPath": "gno.land/r/demo/teritori/dao_registry_v4", - "socialFeedsPkgPath": "gno.land/r/demo/teritori/social_feeds_v4", - "socialFeedsDAOPkgPath": "gno.land/r/demo/teritori/social_feeds_dao_v2", - "modboardsPkgPath": "gno.land/r/demo/teritori/modboards_v4", - "groupsPkgPath": "gno.land/r/demo/teritori/groups_v4", - "votingGroupPkgPath": "gno.land/p/demo/teritori/dao_voting_group_v2", - "daoProposalSinglePkgPath": "gno.land/p/demo/teritori/dao_proposal_single_v4", - "daoInterfacesPkgPath": "gno.land/p/demo/teritori/dao_interfaces_v5", - "daoCorePkgPath": "gno.land/p/demo/teritori/dao_core_v4", + "daoRegistryPkgPath": "gno.land/r/teritori/dao_registry_v4", + "socialFeedsPkgPath": "gno.land/r/teritori/social_feeds_v4", + "socialFeedsDAOPkgPath": "gno.land/r/teritori/social_feeds_dao_v2", + "modboardsPkgPath": "gno.land/r/teritori/modboards_v4", + "groupsPkgPath": "gno.land/r/teritori/groups_v4", + "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", + "daoCorePkgPath": "gno.land/p/teritori/dao_core_v4", "gnowebURL": "https://testnet.gno.teritori.com", "faucetURL": "https://testnet.gno.teritori.com:5050/?toaddr=$addr" }, @@ -4413,6 +4630,7 @@ "denom": "ugnot", "displayName": "GNOT", "decimals": 6, + "variant": "gno", "coingeckoId": "gno", "icon": "gno.svg", "kind": "native", @@ -4450,7 +4668,8 @@ "coingeckoId": "gno", "icon": "gno.svg", "kind": "native", - "color": "#232800" + "color": "#232800", + "variant": "gno" } ], "stakeCurrency": "ugnot", @@ -4481,6 +4700,7 @@ "kind": "native", "denom": "ugraviton", "decimals": 6, + "variant": "cosmos", "displayName": "GRAV", "coingeckoId": "graviton", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/gravitybridge/images/grav.svg", @@ -4490,6 +4710,7 @@ "kind": "native", "denom": "gravity0xfB5c6815cA3AC72Ce9F5006869AE67f18bF77006", "decimals": 18, + "variant": "cosmos", "displayName": "PSTAKE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/pstake.svg", @@ -4499,6 +4720,7 @@ "kind": "native", "denom": "gravity0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "decimals": 18, + "variant": "cosmos", "displayName": "WETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/weth.svg", @@ -4508,6 +4730,7 @@ "kind": "native", "denom": "gravity0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdc.svg", @@ -4517,6 +4740,7 @@ "kind": "native", "denom": "gravity0xdAC17F958D2ee523a2206206994597C13D831ec7", "decimals": 6, + "variant": "cosmos", "displayName": "USDT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdt.svg", @@ -4526,6 +4750,7 @@ "kind": "native", "denom": "gravity0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", "decimals": 8, + "variant": "cosmos", "displayName": "WBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/wbtc.svg", @@ -4535,6 +4760,7 @@ "kind": "native", "denom": "gravity0x6B175474E89094C44Da98b954EedeAC495271d0F", "decimals": 18, + "variant": "cosmos", "displayName": "DAI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/dai.svg", @@ -4544,6 +4770,7 @@ "kind": "native", "denom": "gravity0x83F20F44975D03b1b09e64809B757c47f942BEeA", "decimals": 18, + "variant": "cosmos", "displayName": "sDAI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/sdai.svg", @@ -4553,6 +4780,7 @@ "kind": "native", "denom": "gravity0x2F109021aFe75B949429fe30523Ee7C0D5B27207", "decimals": 18, + "variant": "cosmos", "displayName": "OCC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/occamfi.png", @@ -4562,6 +4790,7 @@ "kind": "native", "denom": "gravity0x60e683C6514Edd5F758A55b6f393BeBBAfaA8d5e", "decimals": 8, + "variant": "cosmos", "displayName": "PAGE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/page.svg", @@ -4600,6 +4829,7 @@ "kind": "native", "denom": "aISLM", "decimals": 18, + "variant": "cosmos", "displayName": "ISLM", "coingeckoId": "islamic-coin", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/haqq/images/islm.svg", @@ -4638,6 +4868,7 @@ "kind": "native", "denom": "ufury", "decimals": 6, + "variant": "cosmos", "displayName": "FURY", "coingeckoId": "fanfury", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/highbury/images/fury.svg", @@ -4647,6 +4878,7 @@ "kind": "native", "denom": "jinx", "decimals": 6, + "variant": "cosmos", "displayName": "JINX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/highbury/images/jinx.svg", @@ -4656,6 +4888,7 @@ "kind": "native", "denom": "jinxy", "decimals": 6, + "variant": "cosmos", "displayName": "JINXy", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/highbury/images/jinxy.svg", @@ -4694,6 +4927,7 @@ "kind": "native", "denom": "uheart", "decimals": 6, + "variant": "cosmos", "displayName": "HEART", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/humanstestnet/images/humans.png", @@ -4731,6 +4965,7 @@ "kind": "native", "denom": "aheart", "decimals": 18, + "variant": "cosmos", "displayName": "HEART", "coingeckoId": "humans-ai", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/humans/images/humans_small_light.svg", @@ -4769,6 +5004,7 @@ "kind": "native", "denom": "uhid", "decimals": 6, + "variant": "cosmos", "displayName": "HID", "coingeckoId": "hypersign-identity-token", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/hypersigntestnet/images/hypersign.png", @@ -4806,6 +5042,7 @@ "kind": "native", "denom": "idep", "decimals": 6, + "variant": "cosmos", "displayName": "IDEP", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/idep/images/idep.svg", @@ -4844,6 +5081,7 @@ "kind": "native", "denom": "aimv", "decimals": 18, + "variant": "cosmos", "displayName": "IMV", "coingeckoId": "imv", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/imversed/images/imversed.svg", @@ -4882,6 +5120,7 @@ "kind": "native", "denom": "nimv", "decimals": 6, + "variant": "cosmos", "displayName": "IMV", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/imversedtestnet/images/imversed.svg", @@ -4919,6 +5158,7 @@ "kind": "native", "denom": "inj", "decimals": 18, + "variant": "cosmos", "displayName": "INJ", "coingeckoId": "injective-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/injective/images/inj.svg", @@ -4928,6 +5168,7 @@ "kind": "native", "denom": "factory/inj1cdwt8g7nxgtg2k4fn8sj363mh9ahkw2qt0vrnc/ampINJ", "decimals": 6, + "variant": "cosmos", "displayName": "ampINJ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/injective/images/ampinj.png", @@ -4937,6 +5178,7 @@ "kind": "native", "denom": "factory/inj14lf8xm6fcvlggpa7guxzjqwjmtr24gnvf56hvz/autism", "decimals": 6, + "variant": "cosmos", "displayName": "AUTISM", "coingeckoId": "autism", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/injective/images/autism.png", @@ -4946,6 +5188,7 @@ "kind": "native", "denom": "factory/inj1xtel2knkt8hmc9dnzpjz6kdmacgcfmlv5f308w/ninja", "decimals": 6, + "variant": "cosmos", "displayName": "NINJA", "coingeckoId": "dog-wif-nuchucks", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/injective/images/ninja.png", @@ -4955,6 +5198,7 @@ "kind": "native", "denom": "factory/inj1rmjzj9fn47kdmfk4f3z39qr6czexxe0yjyc546/WGMI", "decimals": 6, + "variant": "cosmos", "displayName": "WGMI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/injective/images/wgmi.png", @@ -4993,6 +5237,7 @@ "kind": "native", "denom": "inj", "decimals": 18, + "variant": "cosmos", "displayName": "INJ", "coingeckoId": "injective-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/injective/images/inj.svg", @@ -5030,6 +5275,7 @@ "kind": "native", "denom": "uiris", "decimals": 6, + "variant": "cosmos", "displayName": "IRIS", "coingeckoId": "iris-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/irisnet/images/iris.svg", @@ -5068,6 +5314,7 @@ "kind": "native", "denom": "uixo", "decimals": 6, + "variant": "cosmos", "displayName": "IXO", "coingeckoId": "ixo", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/impacthub/images/ixo.svg", @@ -5106,6 +5353,7 @@ "kind": "native", "denom": "uixo", "decimals": 6, + "variant": "cosmos", "displayName": "IXO", "coingeckoId": "ixo", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/impacthubdevnet/images/ixo.svg", @@ -5143,6 +5391,7 @@ "kind": "native", "denom": "uixo", "decimals": 6, + "variant": "cosmos", "displayName": "IXO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/impacthub/images/ixo.svg", @@ -5181,6 +5430,7 @@ "kind": "native", "denom": "ujkl", "decimals": 6, + "variant": "cosmos", "displayName": "JKL", "coingeckoId": "jackal-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/jackal/images/jkl.svg", @@ -5219,6 +5469,7 @@ "kind": "native", "denom": "ujkl", "decimals": 6, + "variant": "cosmos", "displayName": "JKL", "coingeckoId": "jackal", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/jackaltestnet/images/jkl.svg", @@ -5256,6 +5507,7 @@ "kind": "native", "denom": "ujuno", "decimals": 6, + "variant": "cosmos", "displayName": "JUNO", "coingeckoId": "juno-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/juno.svg", @@ -5265,6 +5517,7 @@ "kind": "native", "denom": "cw20:juno168ctmpyppk90d34p3jjy658zf5a5l3w8wk35wht6ccqj4mr0yv8s4j5awr", "decimals": 6, + "variant": "cosmos", "displayName": "NETA", "coingeckoId": "neta", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/neta.svg", @@ -5274,6 +5527,7 @@ "kind": "native", "denom": "cw20:juno1g2g7ucurum66d42g8k5twk34yegdq8c82858gz0tq2fc75zy7khssgnhjl", "decimals": 3, + "variant": "cosmos", "displayName": "MARBLE", "coingeckoId": "marble", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/marble.svg", @@ -5283,6 +5537,7 @@ "kind": "native", "denom": "cw20:juno1re3x67ppxap48ygndmrc7har2cnc7tcxtm9nplcas4v0gc3wnmvs3s807z", "decimals": 6, + "variant": "cosmos", "displayName": "HOPE", "coingeckoId": "hope-galaxy", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/hope.svg", @@ -5292,6 +5547,7 @@ "kind": "native", "denom": "cw20:juno1r4pzw8f9z0sypct5l9j906d47z998ulwvhvqe5xdwgy8wf84583sxwh0pa", "decimals": 6, + "variant": "cosmos", "displayName": "RAC", "coingeckoId": "racoon", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/rac.svg", @@ -5301,6 +5557,7 @@ "kind": "native", "denom": "cw20:juno1y9rf7ql6ffwkv02hsgd4yruz23pn4w97p75e2slsnkm0mnamhzysvqnxaq", "decimals": 6, + "variant": "cosmos", "displayName": "BLOCK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/block.svg", @@ -5310,6 +5567,7 @@ "kind": "native", "denom": "cw20:juno1tdjwrqmnztn2j3sj2ln9xnyps5hs48q3ddwjrz7jpv6mskappjys5czd49", "decimals": 6, + "variant": "cosmos", "displayName": "DHK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/dhk.svg", @@ -5319,6 +5577,7 @@ "kind": "native", "denom": "cw20:juno15u3dt79t6sxxa3x3kpkhzsy56edaa5a66wvt3kxmukqjz2sx0hes5sn38g", "decimals": 6, + "variant": "cosmos", "displayName": "RAW", "coingeckoId": "junoswap-raw-dao", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/raw.svg", @@ -5328,6 +5587,7 @@ "kind": "native", "denom": "cw20:juno17wzaxtfdw5em7lc94yed4ylgjme63eh73lm3lutp2rhcxttyvpwsypjm4w", "decimals": 6, + "variant": "cosmos", "displayName": "ASVT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/asvt.png", @@ -5337,6 +5597,7 @@ "kind": "native", "denom": "cw20:juno1ur4jx0sxchdevahep7fwq28yk4tqsrhshdtylz46yka3uf6kky5qllqp4k", "decimals": 6, + "variant": "cosmos", "displayName": "HNS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/hns.svg", @@ -5346,6 +5607,7 @@ "kind": "native", "denom": "cw20:juno1n7n7d5088qlzlj37e9mgmkhx6dfgtvt02hqxq66lcap4dxnzdhwqfmgng3", "decimals": 6, + "variant": "cosmos", "displayName": "JOE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/joe.png", @@ -5355,6 +5617,7 @@ "kind": "native", "denom": "cw20:juno1sfwye65qxcfsc837gu5qcprcz7w49gkv3wnat04764ld76hy3arqs779tr", "decimals": 6, + "variant": "cosmos", "displayName": "DLA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/dla.svg", @@ -5364,6 +5627,7 @@ "kind": "native", "denom": "cw20:juno1j0a9ymgngasfn3l5me8qpd53l5zlm9wurfdk7r65s5mg6tkxal3qpgf5se", "decimals": 6, + "variant": "cosmos", "displayName": "GLTO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/glto.svg", @@ -5373,6 +5637,7 @@ "kind": "native", "denom": "cw20:juno1gz8cf86zr4vw9cjcyyv432vgdaecvr9n254d3uwwkx9rermekddsxzageh", "decimals": 6, + "variant": "cosmos", "displayName": "GKEY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/gkey.svg", @@ -5382,6 +5647,7 @@ "kind": "native", "denom": "cw20:juno1t46z6hg8vvsena7sue0vg6w85ljar3cundplkre9sz0skeqkap9sxyyy6m", "decimals": 6, + "variant": "cosmos", "displayName": "HOLE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/hole.svg", @@ -5391,6 +5657,7 @@ "kind": "native", "denom": "cw20:juno1dd0k0um5rqncfueza62w9sentdfh3ec4nw4aq4lk5hkjl63vljqscth9gv", "decimals": 6, + "variant": "cosmos", "displayName": "SEJUNO", "coingeckoId": "stakeeasy-juno-derivative", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/sejuno.svg", @@ -5400,6 +5667,7 @@ "kind": "native", "denom": "cw20:juno1wwnhkagvcd3tjz6f8vsdsw5plqnw8qy2aj3rrhqr2axvktzv9q2qz8jxn3", "decimals": 6, + "variant": "cosmos", "displayName": "BJUNO", "coingeckoId": "stakeeasy-bjuno", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/bjuno.svg", @@ -5409,6 +5677,7 @@ "kind": "native", "denom": "cw20:juno159q8t5g02744lxq8lfmcn6f78qqulq9wn3y9w7lxjgkz4e0a6kvsfvapse", "decimals": 6, + "variant": "cosmos", "displayName": "SOLAR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/solar.svg", @@ -5418,6 +5687,7 @@ "kind": "native", "denom": "cw20:juno19rqljkh95gh40s7qdx40ksx3zq5tm4qsmsrdz9smw668x9zdr3lqtg33mf", "decimals": 6, + "variant": "cosmos", "displayName": "SEASY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/seasy.svg", @@ -5427,6 +5697,7 @@ "kind": "native", "denom": "cw20:juno1p8x807f6h222ur0vssqy3qk6mcpa40gw2pchquz5atl935t7kvyq894ne3", "decimals": 6, + "variant": "cosmos", "displayName": "MUSE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/muse.png", @@ -5436,6 +5707,7 @@ "kind": "native", "denom": "cw20:juno1qsrercqegvs4ye0yqg93knv73ye5dc3prqwd6jcdcuj8ggp6w0us66deup", "decimals": 6, + "variant": "cosmos", "displayName": "LOOP", "coingeckoId": "loop", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/loop.png", @@ -5445,6 +5717,7 @@ "kind": "native", "denom": "cw20:juno1cltgm8v842gu54srmejewghnd6uqa26lzkpa635wzra9m9xuudkqa2gtcz", "decimals": 6, + "variant": "cosmos", "displayName": "FURY", "coingeckoId": "fanfury", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/fanfury.png", @@ -5454,6 +5727,7 @@ "kind": "native", "denom": "cw20:juno1rws84uz7969aaa7pej303udhlkt3j9ca0l3egpcae98jwak9quzq8szn2l", "decimals": 6, + "variant": "cosmos", "displayName": "PHMN", "coingeckoId": "posthuman", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/phmn.svg", @@ -5463,6 +5737,7 @@ "kind": "native", "denom": "cw20:juno1u45shlp0q4gcckvsj06ss4xuvsu0z24a0d0vr9ce6r24pht4e5xq7q995n", "decimals": 6, + "variant": "cosmos", "displayName": "HOPERS", "coingeckoId": "hopers-io ", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/hopers.svg", @@ -5472,6 +5747,7 @@ "kind": "native", "denom": "cw20:juno1g647t78y2ulqlm3lss8rs3d0spzd0teuwhdvnqn92tr79yltk9dq2h24za", "decimals": 6, + "variant": "cosmos", "displayName": "RED", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/red.png", @@ -5481,6 +5757,7 @@ "kind": "native", "denom": "cw20:juno14q8kk464fafql2fwmlsgvgcdl6h2csqpzv4hr025fmcvgjahpess32k0j7", "decimals": 6, + "variant": "cosmos", "displayName": "BLUE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/blue.png", @@ -5490,6 +5767,7 @@ "kind": "native", "denom": "cw20:juno1mkw83sv6c7sjdvsaplrzc8yaes9l42p4mhy0ssuxjnyzl87c9eps7ce3m9", "decimals": 6, + "variant": "cosmos", "displayName": "WYND", "coingeckoId": "wynd", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/wynd.svg", @@ -5499,6 +5777,7 @@ "kind": "native", "denom": "juno1s2dp05rspeuzzpzyzdchk262szehrtfpz847uvf98cnwh53ulx4qg20qwj", "decimals": 6, + "variant": "cosmos", "displayName": "BANANA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/banana.png", @@ -5508,6 +5787,7 @@ "kind": "native", "denom": "cw20:juno1qmlchtmjpvu0cr7u0tad2pq8838h6farrrjzp39eqa9xswg7teussrswlq", "decimals": 6, + "variant": "cosmos", "displayName": "NRIDE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/nride.svg", @@ -5517,6 +5797,7 @@ "kind": "native", "denom": "cw20:juno14lycavan8gvpjn97aapzvwmsj8kyrvf644p05r0hu79namyj3ens87650k", "decimals": 6, + "variant": "cosmos", "displayName": "SGNL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/sgnl.png", @@ -5526,6 +5807,7 @@ "kind": "native", "denom": "cw20:juno1zkwveux7y6fmsr88atf3cyffx96p0c96qr8tgcsj7vfnhx7sal3s3zu3ps", "decimals": 6, + "variant": "cosmos", "displayName": "JAPE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/jape.png", @@ -5535,6 +5817,7 @@ "kind": "native", "denom": "cw20:juno12wxyvtqe76x2a5jj6ckp2hfq8v32m6rvyyxwwufl2tksqvkt7whqczv6pa", "decimals": 6, + "variant": "cosmos", "displayName": "CATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/catom.png", @@ -5544,6 +5827,7 @@ "kind": "native", "denom": "cw20:juno1g0wuyu2f49ncf94r65278puxzclf5arse9f3kvffxyv4se4vgdmsk4dvqz", "decimals": 6, + "variant": "cosmos", "displayName": "HOWL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/howl.png", @@ -5553,6 +5837,7 @@ "kind": "native", "denom": "cw20:juno1u8cr3hcjvfkzxcaacv9q75uw9hwjmn8pucc93pmy6yvkzz79kh3qncca8x", "decimals": 6, + "variant": "cosmos", "displayName": "FOX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/fox.png", @@ -5562,6 +5847,7 @@ "kind": "native", "denom": "cw20:juno1xekkh27punj0uxruv3gvuydyt856fax0nu750xns99t2qcxp7xmsqwhfma", "decimals": 6, + "variant": "cosmos", "displayName": "GRDN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/guardian.png", @@ -5571,6 +5857,7 @@ "kind": "native", "denom": "cw20:juno166heaxlyntd33a5euh4rrz26svhean4klzw594esmd02l4atan6sazy2my", "decimals": 6, + "variant": "cosmos", "displayName": "MNPU", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/mnpu.svg", @@ -5580,6 +5867,7 @@ "kind": "native", "denom": "cw20:juno10gthz5ufgrpuk5cscve2f0hjp56wgp90psqxcrqlg4m9mcu9dh8q4864xy", "decimals": 6, + "variant": "cosmos", "displayName": "KLEO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/kleomedes.png", @@ -5589,6 +5877,7 @@ "kind": "native", "denom": "cw20:juno1qqwf3lkfjhp77yja7gmg3y95pda0e5xctqrdhf3wvwdd79flagvqfgrgxp", "decimals": 6, + "variant": "cosmos", "displayName": "SKOJ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/sikoba.svg", @@ -5598,6 +5887,7 @@ "kind": "native", "denom": "cw20:juno1x5qt47rw84c4k6xvvywtrd40p8gxjt8wnmlahlqg07qevah3f8lqwxfs7z", "decimals": 6, + "variant": "cosmos", "displayName": "SHIBAC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/shibacosmos.png", @@ -5607,6 +5897,7 @@ "kind": "native", "denom": "cw20:juno1ngww7zxak55fql42wmyqrr4rhzpne24hhs4p3w4cwhcdgqgr3hxsmzl9zg", "decimals": 6, + "variant": "cosmos", "displayName": "CLST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/celestims.png", @@ -5616,6 +5907,7 @@ "kind": "native", "denom": "cw20:juno1m4h8q4p305wgy7vkux0w6e5ylhqll3s6pmadhxkhqtuwd5wlxhxs8xklsw", "decimals": 6, + "variant": "cosmos", "displayName": "WATR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/watr.png", @@ -5625,6 +5917,7 @@ "kind": "native", "denom": "cw20:juno1ju8k8sqwsqu5k6umrypmtyqu2wqcpnrkf4w4mntvl0javt4nma7s8lzgss", "decimals": 6, + "variant": "cosmos", "displayName": "CASA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/casa.png", @@ -5634,6 +5927,7 @@ "kind": "native", "denom": "cw20:juno1j4ux0f6gt7e82z7jdpm25v4g2gts880ap64rdwa49989wzhd0dfqed6vqm", "decimals": 6, + "variant": "cosmos", "displayName": "SUMMIT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/summit.png", @@ -5643,6 +5937,7 @@ "kind": "native", "denom": "cw20:juno13ca2g36ng6etcfhr9qxx352uw2n5e92np54thfkm3w3nzlhsgvwsjaqlyq", "decimals": 6, + "variant": "cosmos", "displayName": "MANNA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/manna.png", @@ -5652,6 +5947,7 @@ "kind": "native", "denom": "cw20:juno12etxwkxvms0uy9ak8g3pyq6a53myukufdnx82pakzmjmpm77a0ksr9gs5v", "decimals": 6, + "variant": "cosmos", "displayName": "EMPWR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/empwr.png", @@ -5661,6 +5957,7 @@ "kind": "native", "denom": "cw20:juno1525fuspletvzykpgr2atxpymu9le4mghd7qq4a4u23uwqzc2f3fq7fmafd", "decimals": 6, + "variant": "cosmos", "displayName": "MIDDLE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/middle.png", @@ -5670,6 +5967,7 @@ "kind": "native", "denom": "cw20:juno17703kcxtsg37hryxnestejyyycuv5yyvnghp2e7w0kqvafnnyetsgzq62w", "decimals": 6, + "variant": "cosmos", "displayName": "SUNSET", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/sunset.png", @@ -5679,6 +5977,7 @@ "kind": "native", "denom": "cw20:juno1uu3rxu7w7fpfj4sl4xpxppgymk57mzdzn6kg7492jdxh5dwk7d2qq9429e", "decimals": 6, + "variant": "cosmos", "displayName": "TREE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/livingtree.png", @@ -5688,6 +5987,7 @@ "kind": "native", "denom": "cw20:juno1jwdy7v4egw36pd84aeks3ww6n8k7zhsumd4ac8q5lts83ppxueus4626e8", "decimals": 6, + "variant": "cosmos", "displayName": "INVDRS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/invdrs.png", @@ -5697,6 +5997,7 @@ "kind": "native", "denom": "cw20:juno1jrr0tuuzxrrwcg6hgeqhw5wqpck2y55734e7zcrp745aardlp0qqg8jz06", "decimals": 6, + "variant": "cosmos", "displayName": "APEMOS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/apemos.png", @@ -5706,6 +6007,7 @@ "kind": "native", "denom": "cw20:juno1ytymtllllsp3hfmndvcp802p2xmy5s8m59ufel8xv9ahyxyfs4hs4kd4je", "decimals": 6, + "variant": "cosmos", "displayName": "OSDOGE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/osdoge.png", @@ -5715,6 +6017,7 @@ "kind": "native", "denom": "cw20:juno1k2ruzzvvwwtwny6gq6kcwyfhkzahaunp685wmz4hafplduekj98q9hgs6d", "decimals": 6, + "variant": "cosmos", "displayName": "DOGA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/doga.png", @@ -5724,6 +6027,7 @@ "kind": "native", "denom": "cw20:juno1zqrj3ta4u7ylv0wqzd8t8q3jrr9rdmn43zuzp9zemeunecnhy8fss778g7", "decimals": 6, + "variant": "cosmos", "displayName": "PEPE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/pepe.png", @@ -5733,6 +6037,7 @@ "kind": "native", "denom": "cw20:juno1f5datjdse3mdgrapwuzs3prl7pvxxht48ns6calnn0t77v2s9l8s0qu488", "decimals": 6, + "variant": "cosmos", "displayName": "CATMOS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/catmos.png", @@ -5742,6 +6047,7 @@ "kind": "native", "denom": "cw20:juno1dyyf7pxeassxvftf570krv7fdf5r8e4r04mp99h0mllsqzp3rs4q7y8yqg", "decimals": 6, + "variant": "cosmos", "displayName": "SPACER", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/spacer.png", @@ -5751,6 +6057,7 @@ "kind": "native", "denom": "cw20:juno1dpany8c0lj526lsa02sldv7shzvnw5dt5ues72rk35hd69rrydxqeraz8l", "decimals": 9, + "variant": "cosmos", "displayName": "LIGHT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/light.png", @@ -5760,6 +6067,7 @@ "kind": "native", "denom": "cw20:juno1llg7q2d5dqlrqzh5dxv8c7kzzjszld34s5vktqmlmaaxqjssz43sxyhq0d", "decimals": 6, + "variant": "cosmos", "displayName": "MILE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/mille.png", @@ -5769,6 +6077,7 @@ "kind": "native", "denom": "cw20:juno1lpvx3mv2a6ddzfjc7zzz2v2cm5gqgqf0hx67hc5p5qwn7hz4cdjsnznhu8", "decimals": 6, + "variant": "cosmos", "displayName": "VOID", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/void.png", @@ -5778,6 +6087,7 @@ "kind": "native", "denom": "cw20:juno10vgf2u03ufcf25tspgn05l7j3tfg0j63ljgpffy98t697m5r5hmqaw95ux", "decimals": 6, + "variant": "cosmos", "displayName": "SLCA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/silica.png", @@ -5787,6 +6097,7 @@ "kind": "native", "denom": "cw20:juno1epxnvge53c4hkcmqzlxryw5fp7eae2utyk6ehjcfpwajwp48km3sgxsh9k", "decimals": 6, + "variant": "cosmos", "displayName": "PEPEC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/pepec.png", @@ -5796,6 +6107,7 @@ "kind": "native", "denom": "cw20:juno15au4k2jgwd0jnchy0fkg3lm00fpt7jt0j2duuzradn2q7sega2dszyn5pp", "decimals": 6, + "variant": "cosmos", "displayName": "PLTN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/pltn.png", @@ -5805,6 +6117,7 @@ "kind": "native", "denom": "cw20:juno12mcwmd6wqhledkjsurlfqtc8j0pedvxlcxw3gs4kh2qf808ehehsen8nmw", "decimals": 6, + "variant": "cosmos", "displayName": "YFD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/yfd.svg", @@ -5814,6 +6127,7 @@ "kind": "native", "denom": "factory/juno1qly4zcmzr2gyxtze5yt9chv2srczwwunppxjfh/NEXX", "decimals": 6, + "variant": "cosmos", "displayName": "NEXX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/nexx.png", @@ -5823,6 +6137,7 @@ "kind": "native", "denom": "factory/juno1u805lv20qc6jy7c3ttre7nct6uyl20pfky5r7e/DGL", "decimals": 6, + "variant": "cosmos", "displayName": "DGL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/dgl.png", @@ -5832,6 +6147,7 @@ "kind": "native", "denom": "cw20:juno1a0khag6cfzu5lrwazmyndjgvlsuk7g4vn9jd8ceym8f4jf6v2l9q6d348a", "decimals": 6, + "variant": "cosmos", "displayName": "ampJUNO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/ampjuno.svg", @@ -5841,6 +6157,7 @@ "kind": "native", "denom": "cw20:juno14fz92ehqt37e096xr95kmy8nc0kz803uezxtg4fwx7agjjma86sqm8mg3h", "decimals": 8, + "variant": "cosmos", "displayName": "BITS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/bits.png", @@ -5850,6 +6167,7 @@ "kind": "native", "denom": "cw20:juno13epyeat7ef0k7q6kllmyvc8zpfd9xm7cqjrgtk0qkgrk7n5mjfmq8979jw", "decimals": 6, + "variant": "cosmos", "displayName": "POIL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/poil.png", @@ -5859,6 +6177,7 @@ "kind": "native", "denom": "factory/juno195asgku87kxgu48s447z0ryhsyn5rl3yzvfw0d/uhava", "decimals": 6, + "variant": "cosmos", "displayName": "HAVA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/juno/images/hava.png", @@ -5897,6 +6216,7 @@ "kind": "native", "denom": "ujunox", "decimals": 6, + "variant": "cosmos", "displayName": "JUNOX", "coingeckoId": "juno-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/juno.svg", @@ -5906,6 +6226,7 @@ "kind": "native", "denom": "factory/juno12klaltyqvg2j6v034jwdxrk5n4242ttse4sdpt/NEXX", "decimals": 6, + "variant": "cosmos", "displayName": "NEXX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/junotestnet/images/nexx.png", @@ -5943,6 +6264,7 @@ "kind": "native", "denom": "ukava", "decimals": 6, + "variant": "cosmos", "displayName": "KAVA", "coingeckoId": "kava", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kava/images/kava.svg", @@ -5952,6 +6274,7 @@ "kind": "native", "denom": "hard", "decimals": 6, + "variant": "cosmos", "displayName": "HARD", "coingeckoId": "kava-lend", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kava/images/hard.svg", @@ -5961,6 +6284,7 @@ "kind": "native", "denom": "swp", "decimals": 6, + "variant": "cosmos", "displayName": "SWP", "coingeckoId": "kava-swap", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kava/images/swp.svg", @@ -5970,6 +6294,7 @@ "kind": "native", "denom": "usdx", "decimals": 6, + "variant": "cosmos", "displayName": "USDX", "coingeckoId": "usdx", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kava/images/usdx.svg", @@ -5979,6 +6304,7 @@ "kind": "native", "denom": "erc20/tether/usdt", "decimals": 6, + "variant": "cosmos", "displayName": "USDT", "coingeckoId": "tether", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdt.svg", @@ -6017,6 +6343,7 @@ "kind": "native", "denom": "uxki", "decimals": 6, + "variant": "cosmos", "displayName": "XKI", "coingeckoId": "ki", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kichain/images/xki.svg", @@ -6026,6 +6353,7 @@ "kind": "native", "denom": "cw20:ki1dt3lk455ed360pna38fkhqn0p8y44qndsr77qu73ghyaz2zv4whq83mwdy", "decimals": 6, + "variant": "cosmos", "displayName": "LVN", "coingeckoId": "lvn", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kichain/images/lvn.png", @@ -6064,6 +6392,7 @@ "kind": "native", "denom": "utki", "decimals": 6, + "variant": "cosmos", "displayName": "TKI", "coingeckoId": "ki", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kichain/images/xki.svg", @@ -6101,6 +6430,7 @@ "kind": "native", "denom": "udarc", "decimals": 6, + "variant": "cosmos", "displayName": "DARC", "coingeckoId": "darcmatter-coin", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/konstellation/images/darc.svg", @@ -6139,6 +6469,7 @@ "kind": "native", "denom": "ukuji", "decimals": 6, + "variant": "cosmos", "displayName": "KUJI", "coingeckoId": "kujira", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/kuji.svg", @@ -6148,6 +6479,7 @@ "kind": "native", "denom": "factory/kujira1qk00h5atutpsv900x202pxx42npjr9thg58dnqpa72f2p7m2luase444a7/uusk", "decimals": 6, + "variant": "cosmos", "displayName": "USK", "coingeckoId": "usk", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/usk.svg", @@ -6157,6 +6489,7 @@ "kind": "native", "denom": "factory/kujira1m96ucsfpt2yy72w09z2rxjdj38y5qd8lqx5jtggnejmdua2ynpnsxyvjex/urcpt", "decimals": 6, + "variant": "cosmos", "displayName": "qcKUJI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/qckuji.svg", @@ -6166,6 +6499,7 @@ "kind": "native", "denom": "factory/kujira1n3fr5f56r2ce0s37wdvwrk98yhhq3unnxgcqus8nzsfxvllk0yxquurqty/ampKUJI", "decimals": 6, + "variant": "cosmos", "displayName": "ampKUJI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/ampkuji.svg", @@ -6175,6 +6509,7 @@ "kind": "native", "denom": "factory/kujira1643jxg8wasy5cfcn7xm8rd742yeazcksqlg4d7/umnta", "decimals": 6, + "variant": "cosmos", "displayName": "MNTA", "coingeckoId": "mantadao", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/mnta.svg", @@ -6184,6 +6519,7 @@ "kind": "native", "denom": "factory/kujira1qzu3up50auxhqyzfq56znuj8n38q2ra7daaf9ef7vg8gu66jh4fqd2wd2y/urcpt", "decimals": 6, + "variant": "cosmos", "displayName": "qcMNTA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/qcmnta.svg", @@ -6193,6 +6529,7 @@ "kind": "native", "denom": "factory/kujira175yatpvkpgw07w0chhzuks3zrrae9z9g2y6r7u5pzqesyau4x9eqqyv0rr/ampMNTA", "decimals": 6, + "variant": "cosmos", "displayName": "ampMNTA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/ampmnta.png", @@ -6202,6 +6539,7 @@ "kind": "native", "denom": "factory/kujira12cjjeytrqcj25uv349thltcygnp9k0kukpct0e/uwink", "decimals": 6, + "variant": "cosmos", "displayName": "WINK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/wink.svg", @@ -6211,6 +6549,7 @@ "kind": "native", "denom": "factory/kujira1slueerjz7mx9jp45u3y0rjwtvhcf8a68wf2mjt/ublend", "decimals": 6, + "variant": "cosmos", "displayName": "BLEND", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/blend.svg", @@ -6220,6 +6559,7 @@ "kind": "native", "denom": "factory/kujira1sc6a0347cc5q3k890jj0pf3ylx2s38rh4sza4t/ufuzn", "decimals": 6, + "variant": "cosmos", "displayName": "FUZN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/fuzion.svg", @@ -6229,6 +6569,7 @@ "kind": "native", "denom": "factory/kujira1sc6a0347cc5q3k890jj0pf3ylx2s38rh4sza4t/urfuzn", "decimals": 6, + "variant": "cosmos", "displayName": "rFUZN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/fuzionr.png", @@ -6238,6 +6579,7 @@ "kind": "native", "denom": "factory/kujira1sc6a0347cc5q3k890jj0pf3ylx2s38rh4sza4t/uyfuzn", "decimals": 6, + "variant": "cosmos", "displayName": "yFUZN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/fuziony.svg", @@ -6247,6 +6589,7 @@ "kind": "native", "denom": "factory/kujira1l04ged98c7a7s9tllu62ld09ztylwf442qgm4thfgmadrvngeumsz4zrh2/urcpt", "decimals": 6, + "variant": "cosmos", "displayName": "qcFUZN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/qcfuzn.svg", @@ -6256,6 +6599,7 @@ "kind": "native", "denom": "factory/kujira1aaudpfr9y23lt9d45hrmskphpdfaq9ajxd3ukh/unstk", "decimals": 6, + "variant": "cosmos", "displayName": "NSTK", "coingeckoId": "unstake-fi", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/nstk.svg", @@ -6265,6 +6609,7 @@ "kind": "native", "denom": "factory/kujira1swkuyt08z74n5jl7zr6hx0ru5sa2yev5v896p6/local", "decimals": 6, + "variant": "cosmos", "displayName": "LOCAL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/local.png", @@ -6274,6 +6619,7 @@ "kind": "native", "denom": "factory/kujira166ysf07ze5suazfzj0r05tv8amk2yn8zvsfuu7/uplnk", "decimals": 6, + "variant": "cosmos", "displayName": "PLNK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kujira/images/PLNK_drk.png", @@ -6312,6 +6658,7 @@ "kind": "native", "denom": "ukuji", "decimals": 6, + "variant": "cosmos", "displayName": "KUJI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/kujiratestnet/images/kuji.png", @@ -6321,6 +6668,7 @@ "kind": "native", "denom": "factory/kujira16qpvzhmawvsm8mcj4hdvtz25dadatdhhgw79xa/FUZN", "decimals": 6, + "variant": "cosmos", "displayName": "FUZN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/kujiratestnet/images/fuzn.png", @@ -6358,6 +6706,7 @@ "kind": "native", "denom": "ukyve", "decimals": 6, + "variant": "cosmos", "displayName": "KYVE", "coingeckoId": "kyve-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kyve/images/kyve-token.svg", @@ -6396,6 +6745,7 @@ "kind": "native", "denom": "tkyve", "decimals": 6, + "variant": "cosmos", "displayName": "KYVE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/kyvetestnet/images/kyve.svg", @@ -6433,6 +6783,7 @@ "kind": "native", "denom": "tkyve", "decimals": 9, + "variant": "cosmos", "displayName": "KYVE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/kyvedevnet/images/kyve.svg", @@ -6470,6 +6821,7 @@ "kind": "native", "denom": "ulamb", "decimals": 18, + "variant": "cosmos", "displayName": "LAMB", "coingeckoId": "lambda", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/lambda/images/lambda.svg", @@ -6508,6 +6860,7 @@ "kind": "native", "denom": "ulava", "decimals": 6, + "variant": "cosmos", "displayName": "LAVA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/lavatestnet/images/lava-icon.svg", @@ -6546,6 +6899,7 @@ "kind": "native", "denom": "nanolike", "decimals": 9, + "variant": "cosmos", "displayName": "LIKE", "coingeckoId": "likecoin", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/likecoin/images/like.svg", @@ -6584,6 +6938,7 @@ "kind": "native", "denom": "nanoekil", "decimals": 9, + "variant": "cosmos", "displayName": "EKIL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/likecoin/images/like.svg", @@ -6622,6 +6977,7 @@ "kind": "native", "denom": "aLYT", "decimals": 18, + "variant": "cosmos", "displayName": "LYT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/logos/images/logos.svg", @@ -6660,6 +7016,7 @@ "kind": "native", "denom": "ulyl", "decimals": 6, + "variant": "cosmos", "displayName": "LYL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/loyal/images/lyl.svg", @@ -6698,6 +7055,7 @@ "kind": "native", "denom": "ulum", "decimals": 6, + "variant": "cosmos", "displayName": "LUM", "coingeckoId": "lum-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/lumnetwork/images/lum.svg", @@ -6736,6 +7094,7 @@ "kind": "native", "denom": "ulumen", "decimals": 6, + "variant": "cosmos", "displayName": "LUMEN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/lumenx/images/lumen.svg", @@ -6774,6 +7133,7 @@ "kind": "native", "denom": "ulumen", "decimals": 6, + "variant": "cosmos", "displayName": "LUMEN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/lumenxtestnet/images/lumen.svg", @@ -6811,6 +7171,7 @@ "kind": "native", "denom": "umars", "decimals": 6, + "variant": "cosmos", "displayName": "MARS", "coingeckoId": "mars-protocol-a7fcbcfb-fd61-4017-92f0-7ee9f9cc6da3", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/mars/images/mars-token.svg", @@ -6849,6 +7210,7 @@ "kind": "native", "denom": "umars", "decimals": 6, + "variant": "cosmos", "displayName": "MARS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/mars/images/mars-token.svg", @@ -6887,6 +7249,7 @@ "kind": "native", "denom": "cacao", "decimals": 10, + "variant": "cosmos", "displayName": "CACAO", "coingeckoId": "cacao", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/mayachain/images/cacao.svg", @@ -6896,6 +7259,7 @@ "kind": "native", "denom": "maya", "decimals": 4, + "variant": "cosmos", "displayName": "MAYA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/mayachain/images/maya.svg", @@ -6933,6 +7297,7 @@ "kind": "native", "denom": "umedas", "decimals": 6, + "variant": "cosmos", "displayName": "MEDAS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/medasdigital/images/medas.svg", @@ -6971,6 +7336,7 @@ "kind": "native", "denom": "umed", "decimals": 6, + "variant": "cosmos", "displayName": "MED", "coingeckoId": "medibloc", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/panacea/images/med.svg", @@ -7009,6 +7375,7 @@ "kind": "native", "denom": "umeme", "decimals": 6, + "variant": "cosmos", "displayName": "MEME", "coingeckoId": "meme-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/meme/images/meme.svg", @@ -7047,6 +7414,7 @@ "kind": "native", "denom": "utick", "decimals": 6, + "variant": "cosmos", "displayName": "TICK", "coingeckoId": "microtick", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/microtick/images/tick.svg", @@ -7085,6 +7453,7 @@ "kind": "native", "denom": "uwhale", "decimals": 6, + "variant": "cosmos", "displayName": "WHALE", "coingeckoId": "white-whale", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/white-whale.svg", @@ -7094,6 +7463,7 @@ "kind": "native", "denom": "factory/migaloo1436kxs0w2es6xlqpp9rd35e3d0cjnw4sv8j3a7483sgks29jqwgshqdky4/ampWHALE", "decimals": 6, + "variant": "cosmos", "displayName": "ampWHALE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/ampwhale.svg", @@ -7103,6 +7473,7 @@ "kind": "native", "denom": "factory/migaloo1mf6ptkssddfmxvhdx0ech0k03ktp6kf9yk59renau2gvht3nq2gqdhts4u/boneWhale", "decimals": 6, + "variant": "cosmos", "displayName": "bWHALE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/bWHALE.svg", @@ -7112,6 +7483,7 @@ "kind": "native", "denom": "factory/migaloo18a9m9stu3dyvewwcq9qmp85euxqcvln5mefync/fable", "decimals": 6, + "variant": "cosmos", "displayName": "FABLE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/fable.svg", @@ -7121,6 +7493,7 @@ "kind": "native", "denom": "factory/migaloo1eqntnl6tzcj9h86psg4y4h6hh05g2h9nj8e09l/urac", "decimals": 6, + "variant": "cosmos", "displayName": "RAC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/rac.svg", @@ -7130,6 +7503,7 @@ "kind": "native", "denom": "factory/migaloo1erul6xyq0gk6ws98ncj7lnq9l4jn4gnnu9we73gdz78yyl2lr7qqrvcgup/ash", "decimals": 6, + "variant": "cosmos", "displayName": "ASH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/ash.svg", @@ -7139,6 +7513,7 @@ "kind": "native", "denom": "cw20:migaloo10nucfm2zqgzqmy7y7ls398t58pjt9cwjsvpy88y2nvamtl34rgmqt5em2v", "decimals": 6, + "variant": "cosmos", "displayName": "mUSDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/mUSDC.svg", @@ -7148,6 +7523,7 @@ "kind": "native", "denom": "factory/migaloo1etlu2h30tjvv8rfa4fwdc43c92f6ul5w9acxzk/uguppy", "decimals": 6, + "variant": "cosmos", "displayName": "GUPPY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/guppy.png", @@ -7157,6 +7533,7 @@ "kind": "native", "denom": "factory/migaloo1t862qdu9mj5hr3j727247acypym3ej47axu22rrapm4tqlcpuseqltxwq5/ophir", "decimals": 6, + "variant": "cosmos", "displayName": "OPHIR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/ophir.png", @@ -7195,6 +7572,7 @@ "kind": "native", "denom": "uwhale", "decimals": 6, + "variant": "cosmos", "displayName": "WHALE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/migaloo/images/white-whale.svg", @@ -7233,6 +7611,7 @@ "kind": "native", "denom": "umis", "decimals": 6, + "variant": "cosmos", "displayName": "MIS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/mises/images/mises.png", @@ -7271,6 +7650,7 @@ "kind": "native", "denom": "utia", "decimals": 6, + "variant": "cosmos", "displayName": "TIA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/celestiatestnet3/images/celestia.svg", @@ -7308,6 +7688,7 @@ "kind": "native", "denom": "umun", "decimals": 6, + "variant": "cosmos", "displayName": "MUN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/mun/images/mun.svg", @@ -7346,6 +7727,7 @@ "kind": "native", "denom": "aMYT", "decimals": 18, + "variant": "cosmos", "displayName": "MYT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/mythos/images/mythos.svg", @@ -7384,6 +7766,7 @@ "kind": "native", "denom": "untrn", "decimals": 6, + "variant": "cosmos", "displayName": "NTRN", "coingeckoId": "neutron-3", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/ntrn.svg", @@ -7393,6 +7776,7 @@ "kind": "native", "denom": "factory/neutron14henrqx9y328fjrdvz6l6d92r0t7g5hk86q5nd/uastropepe", "decimals": 6, + "variant": "cosmos", "displayName": "ASTROPEPE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/astropepe.png", @@ -7402,6 +7786,7 @@ "kind": "native", "denom": "factory/neutron1ug740qrkquxzrk2hh29qrlx3sktkfml3je7juusc2te7xmvsscns0n2wry/wstETH", "decimals": 18, + "variant": "cosmos", "displayName": "wstETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/wsteth.svg", @@ -7411,6 +7796,7 @@ "kind": "native", "denom": "factory/neutron1p8d89wvxyjcnawmgw72klknr3lg9gwwl6ypxda/newt", "decimals": 6, + "variant": "cosmos", "displayName": "NEWT", "coingeckoId": "newt", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/newt.png", @@ -7420,6 +7806,7 @@ "kind": "native", "denom": "factory/neutron1tklm6cvr2wxg8k65t8gh5ewslnzdfd5fsk0w3f/corgi", "decimals": 6, + "variant": "cosmos", "displayName": "CORGI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/babycorgi.png", @@ -7429,6 +7816,7 @@ "kind": "native", "denom": "factory/neutron170v88vrtnedesyfytuku257cggxc79rd7lwt7q/ucircus", "decimals": 6, + "variant": "cosmos", "displayName": "CIRCUS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/circus.png", @@ -7438,6 +7826,7 @@ "kind": "native", "denom": "factory/neutron108x7vp9zv22d6wxrs9as8dshd3pd5vsga463yd/JIMMY", "decimals": 6, + "variant": "cosmos", "displayName": "JIMMY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/jimmy.png", @@ -7447,6 +7836,7 @@ "kind": "native", "denom": "factory/neutron143wp6g8paqasnuuey6zyapucknwy9rhnld8hkr/bad", "decimals": 6, + "variant": "cosmos", "displayName": "BAD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/bad.png", @@ -7456,6 +7846,7 @@ "kind": "native", "denom": "neutron1fjzg7fmv770hsvahqm0nwnu6grs3rjnd2wa6fvm9unv6vedkzekqpw44qj", "decimals": 6, + "variant": "cosmos", "displayName": "BTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/bitcosmos.png", @@ -7465,6 +7856,7 @@ "kind": "native", "denom": "neutron12h09p8hq5y4xpsmcuxxzsn9juef4f6jvekp8yefc6xnlwm6uumnsdk29wf", "decimals": 6, + "variant": "cosmos", "displayName": "WTF", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/WTF.png", @@ -7474,6 +7866,7 @@ "kind": "native", "denom": "factory/neutron1t5qrjtyryh8gzt800qr5vylhh2f8cmx4wmz9mc/ugoddard", "decimals": 6, + "variant": "cosmos", "displayName": "GODRD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/goddardntrn.png", @@ -7483,6 +7876,7 @@ "kind": "native", "denom": "factory/neutron154gg0wtm2v4h9ur8xg32ep64e8ef0g5twlsgvfeajqwghdryvyqsqhgk8e/APOLLO", "decimals": 6, + "variant": "cosmos", "displayName": "APOLLO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/apollo.svg", @@ -7492,6 +7886,7 @@ "kind": "native", "denom": "factory/neutron1ume2n42r5j0660gegrr28fzdze7aqf7r5cd9y6/newtroll", "decimals": 6, + "variant": "cosmos", "displayName": "NTRL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/newtroll.svg", @@ -7501,6 +7896,7 @@ "kind": "native", "denom": "factory/neutron1t24nc7whl77relnu3taxyg3p66pjyuk82png2y/uretro", "decimals": 6, + "variant": "cosmos", "displayName": "RETRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/neutron/images/retro.svg", @@ -7539,6 +7935,7 @@ "kind": "native", "denom": "untrn", "decimals": 6, + "variant": "cosmos", "displayName": "NTRN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/neutrontestnet/images/neutron.svg", @@ -7577,6 +7974,7 @@ "kind": "native", "denom": "unibi", "decimals": 6, + "variant": "cosmos", "displayName": "NIBI", "coingeckoId": "nibi", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nibiru/images/nibiru.svg", @@ -7615,6 +8013,7 @@ "kind": "native", "denom": "ustake", "decimals": 6, + "variant": "cosmos", "displayName": "STAKE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/noble/images/stake.svg", @@ -7624,6 +8023,7 @@ "kind": "native", "denom": "ufrienzies", "decimals": 6, + "variant": "cosmos", "displayName": "FRNZ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/noble/images/frnz.svg", @@ -7633,6 +8033,7 @@ "kind": "native", "denom": "uusdc", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "usd-coin", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/noble/images/USDCoin.svg", @@ -7671,6 +8072,7 @@ "kind": "native", "denom": "ustake", "decimals": 6, + "variant": "cosmos", "displayName": "STAKE", "coingeckoId": "not-found", "icon": "not-found", @@ -7680,6 +8082,7 @@ "kind": "native", "denom": "ulove", "decimals": 6, + "variant": "cosmos", "displayName": "LOVE", "coingeckoId": "not-found", "icon": "not-found", @@ -7689,6 +8092,7 @@ "kind": "native", "denom": "uusdc", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdc.svg", @@ -7698,6 +8102,7 @@ "kind": "native", "denom": "uusdlr", "decimals": 6, + "variant": "cosmos", "displayName": "USDLR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/nobletestnet/images/usdlr.svg", @@ -7735,6 +8140,7 @@ "kind": "native", "denom": "unois", "decimals": 6, + "variant": "cosmos", "displayName": "NOIS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nois/images/nois.svg", @@ -7773,6 +8179,7 @@ "kind": "native", "denom": "unois", "decimals": 6, + "variant": "cosmos", "displayName": "NOIS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nois/images/nois.svg", @@ -7811,6 +8218,7 @@ "kind": "native", "denom": "unls", "decimals": 6, + "variant": "cosmos", "displayName": "NLS", "coingeckoId": "nolus", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nolus/images/nolus.svg", @@ -7849,6 +8257,7 @@ "kind": "native", "denom": "unls", "decimals": 6, + "variant": "cosmos", "displayName": "NLS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/nolustestnet/images/nolus.svg", @@ -7887,6 +8296,7 @@ "kind": "native", "denom": "unom", "decimals": 6, + "variant": "cosmos", "displayName": "NOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nomic/images/nom.svg", @@ -7896,6 +8306,7 @@ "kind": "native", "denom": "usat", "decimals": 14, + "variant": "cosmos", "displayName": "nBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nomic/images/nbtc.svg", @@ -7934,6 +8345,7 @@ "kind": "native", "denom": "unyx", "decimals": 6, + "variant": "cosmos", "displayName": "NYX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nyx/images/nyx.png", @@ -7943,6 +8355,7 @@ "kind": "native", "denom": "unym", "decimals": 6, + "variant": "cosmos", "displayName": "NYM", "coingeckoId": "nym", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/nyx/images/nym.png", @@ -7981,6 +8394,7 @@ "kind": "native", "denom": "uocta", "decimals": 6, + "variant": "cosmos", "displayName": "OCTA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/octa/images/octa.svg", @@ -8019,6 +8433,7 @@ "kind": "native", "denom": "loki", "decimals": 6, + "variant": "cosmos", "displayName": "ODIN", "coingeckoId": "odin-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/odin/images/odin.svg", @@ -8028,6 +8443,7 @@ "kind": "native", "denom": "mGeo", "decimals": 6, + "variant": "cosmos", "displayName": "GEO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/odin/images/geo.svg", @@ -8037,6 +8453,7 @@ "kind": "native", "denom": "mO9W", "decimals": 6, + "variant": "cosmos", "displayName": "O9W", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/odin/images/o9w.svg", @@ -8075,6 +8492,7 @@ "kind": "native", "denom": "wei", "decimals": 18, + "variant": "cosmos", "displayName": "OKT", "coingeckoId": "oec-token", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/okexchain/images/okc.png", @@ -8113,6 +8531,7 @@ "kind": "native", "denom": "uknow", "decimals": 6, + "variant": "cosmos", "displayName": "KNOW", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/okp4testnet/images/okp4.png", @@ -8151,6 +8570,7 @@ "kind": "native", "denom": "uflix", "decimals": 6, + "variant": "cosmos", "displayName": "FLIX", "coingeckoId": "omniflix-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/omniflixhub/images/flix.svg", @@ -8189,6 +8609,7 @@ "kind": "native", "denom": "anom", "decimals": 18, + "variant": "cosmos", "displayName": "NOM", "coingeckoId": "onomy-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/onomy/images/nom.svg", @@ -8227,6 +8648,7 @@ "kind": "native", "denom": "orai", "decimals": 6, + "variant": "cosmos", "displayName": "ORAI", "coingeckoId": "oraichain-token", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/oraichain/images/orai-white.svg", @@ -8270,6 +8692,7 @@ "denom": "uosmo", "displayName": "OSMO", "decimals": 6, + "variant": "cosmos", "coingeckoId": "osmosis", "icon": "osmosis-circle.svg", "kind": "native", @@ -8346,6 +8769,7 @@ "denom": "uosmo", "displayName": "OSMO", "decimals": 6, + "variant": "cosmos", "coingeckoId": "osmosis", "icon": "osmosis-circle.svg", "kind": "native", @@ -8406,6 +8830,7 @@ "kind": "native", "denom": "uosmo", "decimals": 6, + "variant": "cosmos", "displayName": "OSMO", "coingeckoId": "osmosis", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/osmosis/images/osmo.svg", @@ -8415,6 +8840,7 @@ "kind": "native", "denom": "uion", "decimals": 6, + "variant": "cosmos", "displayName": "ION", "coingeckoId": "ion", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/osmosis/images/ion.svg", @@ -8453,6 +8879,7 @@ "kind": "native", "denom": "upasg", "decimals": 6, + "variant": "cosmos", "displayName": "PASG", "coingeckoId": "passage", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/passage/images/pasg.png", @@ -8491,6 +8918,7 @@ "kind": "native", "denom": "upasg", "decimals": 6, + "variant": "cosmos", "displayName": "PASG", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/passage/images/pasg.png", @@ -8529,6 +8957,7 @@ "kind": "native", "denom": "uxprt", "decimals": 6, + "variant": "cosmos", "displayName": "XPRT", "coingeckoId": "persistence", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/xprt.svg", @@ -8538,6 +8967,7 @@ "kind": "native", "denom": "stk/uatom", "decimals": 6, + "variant": "cosmos", "displayName": "stkATOM", "coingeckoId": "stkatom", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/stkatom.svg", @@ -8547,6 +8977,7 @@ "kind": "native", "denom": "stk/uosmo", "decimals": 6, + "variant": "cosmos", "displayName": "stkOSMO", "coingeckoId": "pstake-staked-osmo", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/stkosmo.svg", @@ -8585,6 +9016,7 @@ "kind": "native", "denom": "uxprt", "decimals": 6, + "variant": "cosmos", "displayName": "XPRT", "coingeckoId": "persistence", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/xprt.svg", @@ -8594,6 +9026,7 @@ "kind": "native", "denom": "stk/uatom", "decimals": 6, + "variant": "cosmos", "displayName": "stkATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/stkatom.svg", @@ -8631,6 +9064,7 @@ "kind": "native", "denom": "uxprt", "decimals": 6, + "variant": "cosmos", "displayName": "XPRT", "coingeckoId": "persistence", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/xprt.svg", @@ -8640,6 +9074,7 @@ "kind": "native", "denom": "stk/uatom", "decimals": 6, + "variant": "cosmos", "displayName": "stkATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/stkatom.svg", @@ -8649,6 +9084,7 @@ "kind": "native", "denom": "stk/uosmo", "decimals": 6, + "variant": "cosmos", "displayName": "stkOSMO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/stkosmo.svg", @@ -8658,6 +9094,7 @@ "kind": "native", "denom": "stk/adv4tnt", "decimals": 18, + "variant": "cosmos", "displayName": "stkDV4TNT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/persistence/images/stkdv4tnt.svg", @@ -8695,6 +9132,7 @@ "kind": "native", "denom": "aplanq", "decimals": 18, + "variant": "cosmos", "displayName": "PLQ", "coingeckoId": "planq", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/planq/images/planq.svg", @@ -8733,6 +9171,7 @@ "kind": "native", "denom": "apoint", "decimals": 18, + "variant": "cosmos", "displayName": "POINT", "coingeckoId": "point-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/point/images/point-logo.svg", @@ -8773,6 +9212,7 @@ "denom": "0x0000000000000000000000000000000000000000", "displayName": "MATIC", "decimals": 18, + "variant": "ethereum", "coingeckoId": "matic", "icon": "polygon.svg", "kind": "native", @@ -8815,6 +9255,7 @@ "denom": "0x0000000000000000000000000000000000000000", "displayName": "MATIC", "decimals": 18, + "variant": "ethereum", "coingeckoId": "matic", "icon": "polygon.svg", "kind": "native", @@ -8853,6 +9294,7 @@ "kind": "native", "denom": "nhash", "decimals": 9, + "variant": "cosmos", "displayName": "HASH", "coingeckoId": "provenance-blockchain", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/provenance/images/prov.svg", @@ -8891,6 +9333,7 @@ "kind": "native", "denom": "upryzm", "decimals": 6, + "variant": "cosmos", "displayName": "PRYZM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/pryzmtestnet/images/pryzm.svg", @@ -8929,6 +9372,7 @@ "kind": "native", "denom": "bsc0x29a63F4B209C29B4DC47f06FFA896F32667DAD2C", "decimals": 18, + "variant": "cosmos", "displayName": "PURSE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/pundix/images/purse-token-logo.svg", @@ -8967,6 +9411,7 @@ "kind": "native", "denom": "uqsr", "decimals": 6, + "variant": "cosmos", "displayName": "QSR", "coingeckoId": "quasar-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quasar/images/quasar.png", @@ -9005,6 +9450,7 @@ "kind": "native", "denom": "uqsr", "decimals": 6, + "variant": "cosmos", "displayName": "QSR", "coingeckoId": "not-found", "icon": "not-found", @@ -9014,6 +9460,7 @@ "kind": "native", "denom": "uay", "decimals": 6, + "variant": "cosmos", "displayName": "AYY", "coingeckoId": "not-found", "icon": "not-found", @@ -9023,6 +9470,7 @@ "kind": "native", "denom": "oro", "decimals": 6, + "variant": "cosmos", "displayName": "ORO", "coingeckoId": "not-found", "icon": "not-found", @@ -9061,6 +9509,7 @@ "kind": "native", "denom": "uqck", "decimals": 6, + "variant": "cosmos", "displayName": "QCK", "coingeckoId": "quicksilver", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quicksilver/images/qck.png", @@ -9070,6 +9519,7 @@ "kind": "native", "denom": "uqstars", "decimals": 6, + "variant": "cosmos", "displayName": "qSTARS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quicksilver/images/qstars.svg", @@ -9079,6 +9529,7 @@ "kind": "native", "denom": "uqatom", "decimals": 6, + "variant": "cosmos", "displayName": "qATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quicksilver/images/qatom.svg", @@ -9088,6 +9539,7 @@ "kind": "native", "denom": "uqregen", "decimals": 6, + "variant": "cosmos", "displayName": "qREGEN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quicksilver/images/qregen.svg", @@ -9097,6 +9549,7 @@ "kind": "native", "denom": "uqosmo", "decimals": 6, + "variant": "cosmos", "displayName": "qOSMO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quicksilver/images/qosmo.svg", @@ -9106,6 +9559,7 @@ "kind": "native", "denom": "uqsomm", "decimals": 6, + "variant": "cosmos", "displayName": "qSOMM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quicksilver/images/qsomm.svg", @@ -9144,6 +9598,7 @@ "kind": "native", "denom": "uqck", "decimals": 6, + "variant": "cosmos", "displayName": "QCK", "coingeckoId": "quicksilver", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/quicksilver/images/qck.png", @@ -9182,6 +9637,7 @@ "kind": "native", "denom": "uqwoyn", "decimals": 6, + "variant": "cosmos", "displayName": "QWOYN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/qwoyn/images/qwoyn.png", @@ -9220,6 +9676,7 @@ "kind": "native", "denom": "uqwoyn", "decimals": 6, + "variant": "cosmos", "displayName": "QWOYN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/qwoyntestnet/images/qwoyn.png", @@ -9258,6 +9715,7 @@ "kind": "native", "denom": "ario", "decimals": 18, + "variant": "cosmos", "displayName": "RIO", "coingeckoId": "realio-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/realio/images/rio.svg", @@ -9267,6 +9725,7 @@ "kind": "native", "denom": "arst", "decimals": 18, + "variant": "cosmos", "displayName": "RST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/realio/images/rst.png", @@ -9305,6 +9764,7 @@ "kind": "native", "denom": "arebus", "decimals": 18, + "variant": "cosmos", "displayName": "REBUS", "coingeckoId": "rebus", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/rebus/images/rebus.svg", @@ -9343,6 +9803,7 @@ "kind": "native", "denom": "uregen", "decimals": 6, + "variant": "cosmos", "displayName": "REGEN", "coingeckoId": "regen", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/regen/images/regen.svg", @@ -9352,6 +9813,7 @@ "kind": "native", "denom": "eco.uC.NCT", "decimals": 6, + "variant": "cosmos", "displayName": "NCT", "coingeckoId": "toucan-protocol-nature-carbon-tonne", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/regen/images/nct.svg", @@ -9390,6 +9852,7 @@ "kind": "native", "denom": "uatom", "decimals": 6, + "variant": "cosmos", "displayName": "ATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/cosmoshub/images/atom.svg", @@ -9427,6 +9890,7 @@ "kind": "native", "denom": "uatolo", "decimals": 6, + "variant": "cosmos", "displayName": "ATOLO", "coingeckoId": "rizon", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/rizon/images/atolo.svg", @@ -9469,7 +9933,8 @@ "displayName": "SAGA", "coingeckoId": "saga-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/saga/images/saga.svg", - "color": "TODO" + "color": "TODO", + "variant": "cosmos" } ], "features": [], @@ -9504,6 +9969,7 @@ "kind": "native", "denom": "utsaga", "decimals": 6, + "variant": "cosmos", "displayName": "TSAGA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/saga/images/saga.svg", @@ -9545,7 +10011,8 @@ "displayName": "TSAGA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/saga/images/saga.svg", - "color": "TODO" + "color": "TODO", + "variant": "cosmos" } ], "features": [], @@ -9580,6 +10047,7 @@ "kind": "native", "denom": "uakt", "decimals": 6, + "variant": "cosmos", "displayName": "AKT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/akash/images/akt.svg", @@ -9618,6 +10086,7 @@ "kind": "native", "denom": "uscrt", "decimals": 6, + "variant": "cosmos", "displayName": "SCRT", "coingeckoId": "secret", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/scrt.svg", @@ -9627,6 +10096,7 @@ "kind": "native", "denom": "cw20:secret1rgm2m5t530tdzyd99775n6vzumxa5luxcllml4", "decimals": 18, + "variant": "cosmos", "displayName": "SIENNA", "coingeckoId": "sienna", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/sienna.svg", @@ -9636,6 +10106,7 @@ "kind": "native", "denom": "cw20:secret1qfql357amn448duf5gvp9gr48sxx9tsnhupu3d", "decimals": 8, + "variant": "cosmos", "displayName": "SHD(old)", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/shdold.svg", @@ -9645,6 +10116,7 @@ "kind": "native", "denom": "cw20:secret153wu605vvp934xhd4k9dtd640zsep5jkesstdm", "decimals": 8, + "variant": "cosmos", "displayName": "SHD", "coingeckoId": "shade-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/shd.svg", @@ -9654,6 +10126,7 @@ "kind": "native", "denom": "cw20:secret1fl449muk5yq8dlad7a22nje4p5d2pnsgymhjfd", "decimals": 6, + "variant": "cosmos", "displayName": "SILK", "coingeckoId": "silk-bcec1136-561c-4706-a42c-8b67d0d7f7d2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/silk.svg", @@ -9663,6 +10136,7 @@ "kind": "native", "denom": "cw20:secret1k6u0cy4feepm6pehnz804zmwakuwdapm69tuc4", "decimals": 6, + "variant": "cosmos", "displayName": "stkd-SCRT", "coingeckoId": "stkd-scrt", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/stkd-scrt.svg", @@ -9672,6 +10146,7 @@ "kind": "native", "denom": "cw20:secret1yxcexylwyxlq58umhgsjgstgcg2a0ytfy4d9lt", "decimals": 6, + "variant": "cosmos", "displayName": "BUTT", "coingeckoId": "buttcoin-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/butt.svg", @@ -9681,6 +10156,7 @@ "kind": "native", "denom": "cw20:secret12rcvz0umvk875kd6a803txhtlu7y0pnd73kcej", "decimals": 6, + "variant": "cosmos", "displayName": "ALTER", "coingeckoId": "alter", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/alter.svg", @@ -9690,6 +10166,7 @@ "kind": "native", "denom": "cw20:secret1s09x2xvfd2lp2skgzm29w2xtena7s8fq98v852", "decimals": 6, + "variant": "cosmos", "displayName": "AMBER", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/amber.svg", @@ -9699,6 +10176,7 @@ "kind": "native", "denom": "cw20:secret197dvnt9yjxwn8sjdlx05f7zuk27lsdxtfnwxse", "decimals": 6, + "variant": "cosmos", "displayName": "SHILL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/shill.svg", @@ -9737,6 +10215,7 @@ "kind": "native", "denom": "uscrt", "decimals": 6, + "variant": "cosmos", "displayName": "SCRT", "coingeckoId": "secret", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/scrt.svg", @@ -9774,6 +10253,7 @@ "kind": "native", "denom": "uscrt", "decimals": 6, + "variant": "cosmos", "displayName": "SCRT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/secretnetwork/images/scrt.svg", @@ -9811,6 +10291,7 @@ "kind": "native", "denom": "usei", "decimals": 6, + "variant": "cosmos", "displayName": "SEI", "coingeckoId": "sei-network", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/sei.svg", @@ -9820,6 +10301,7 @@ "kind": "native", "denom": "factory/sei1thgp6wamxwqt7rthfkeehktmq0ujh5kspluw6w/OIN", "decimals": 6, + "variant": "cosmos", "displayName": "OIN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/oin.png", @@ -9829,6 +10311,7 @@ "kind": "native", "denom": "factory/sei1x2fgaaqecvk8kwuqkjqcj27clw5p5g99uawdzy9sc4rku8avumcq3cky4k/ampSEI", "decimals": 6, + "variant": "cosmos", "displayName": "ampSEI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/ampsei.svg", @@ -9838,6 +10321,7 @@ "kind": "native", "denom": "factory/sei1fl8pg59wfsgw2wp4aruk38zqccfnc2g8ptrm24/popeye", "decimals": 6, + "variant": "cosmos", "displayName": "POPEYE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/popeye.png", @@ -9847,6 +10331,7 @@ "kind": "native", "denom": "factory/sei1ta5rkr6y2qlkj7px8w2cvear7m2822q4f4ea0m/sensei", "decimals": 6, + "variant": "cosmos", "displayName": "SENSEI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sei/images/SenseiDog.png", @@ -9885,6 +10370,7 @@ "kind": "native", "denom": "usei", "decimals": 6, + "variant": "cosmos", "displayName": "SEI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/seitestnet/images/sei.png", @@ -9923,6 +10409,7 @@ "kind": "native", "denom": "usei", "decimals": 6, + "variant": "cosmos", "displayName": "SEI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/seidevnet3/images/sei.png", @@ -9960,6 +10447,7 @@ "kind": "native", "denom": "udvpn", "decimals": 6, + "variant": "cosmos", "displayName": "DVPN", "coingeckoId": "sentinel", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sentinel/images/dvpn.svg", @@ -9998,6 +10486,7 @@ "kind": "native", "denom": "usge", "decimals": 6, + "variant": "cosmos", "displayName": "SGE", "coingeckoId": "six-sigma", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sge/images/sge.svg", @@ -10036,6 +10525,7 @@ "kind": "native", "denom": "usge", "decimals": 6, + "variant": "cosmos", "displayName": "SGE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sgetestnet/images/sge.png", @@ -10074,6 +10564,7 @@ "kind": "native", "denom": "nshr", "decimals": 9, + "variant": "cosmos", "displayName": "SHR", "coingeckoId": "shareledger", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/shareledger/images/token.svg", @@ -10112,6 +10603,7 @@ "kind": "native", "denom": "uctk", "decimals": 6, + "variant": "cosmos", "displayName": "CTK", "coingeckoId": "certik", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/shentu/images/ctk.svg", @@ -10150,6 +10642,7 @@ "kind": "native", "denom": "rowan", "decimals": 18, + "variant": "cosmos", "displayName": "ROWAN", "coingeckoId": "sifchain", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sifchain/images/rowan.svg", @@ -10188,6 +10681,7 @@ "kind": "native", "denom": "usix", "decimals": 6, + "variant": "cosmos", "displayName": "six", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/sixtestnet/images/six.png", @@ -10225,6 +10719,7 @@ "kind": "native", "denom": "usix", "decimals": 6, + "variant": "cosmos", "displayName": "SIX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/six/images/six.png", @@ -10262,6 +10757,7 @@ "kind": "native", "denom": "usomm", "decimals": 6, + "variant": "cosmos", "displayName": "SOMM", "coingeckoId": "sommelier", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/sommelier/images/somm.svg", @@ -10300,6 +10796,7 @@ "kind": "native", "denom": "usource", "decimals": 6, + "variant": "cosmos", "displayName": "SOURCE", "coingeckoId": "source", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/source/images/source.svg", @@ -10338,6 +10835,7 @@ "kind": "native", "denom": "usource", "decimals": 6, + "variant": "cosmos", "displayName": "SOURCE", "coingeckoId": "source-protocol", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/sourcetestnet/images/source.svg", @@ -10375,6 +10873,7 @@ "kind": "native", "denom": "ufis", "decimals": 6, + "variant": "cosmos", "displayName": "FIS", "coingeckoId": "stafi", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stafihub/images/fis.svg", @@ -10384,6 +10883,7 @@ "kind": "native", "denom": "uratom", "decimals": 6, + "variant": "cosmos", "displayName": "rATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stafihub/images/ratom.svg", @@ -10393,6 +10893,7 @@ "kind": "native", "denom": "uriris", "decimals": 6, + "variant": "cosmos", "displayName": "rIRIS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stafihub/images/riris.svg", @@ -10402,6 +10903,7 @@ "kind": "native", "denom": "urhuahua", "decimals": 6, + "variant": "cosmos", "displayName": "rHUAHUA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stafihub/images/rhuahua.svg", @@ -10411,6 +10913,7 @@ "kind": "native", "denom": "urswth", "decimals": 8, + "variant": "cosmos", "displayName": "rSWTH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stafihub/images/rswth.svg", @@ -10449,6 +10952,7 @@ "kind": "native", "denom": "ustars", "decimals": 6, + "variant": "cosmos", "displayName": "STARS", "coingeckoId": "stargaze", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/stars.svg", @@ -10458,6 +10962,7 @@ "kind": "native", "denom": "factory/stars16da2uus9zrsy83h23ur42v3lglg5rmyrpqnju4/dust", "decimals": 6, + "variant": "cosmos", "displayName": "STRDST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/dust.svg", @@ -10467,6 +10972,7 @@ "kind": "native", "denom": "factory/stars16da2uus9zrsy83h23ur42v3lglg5rmyrpqnju4/mGAZE", "decimals": 6, + "variant": "cosmos", "displayName": "GAZE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/gaze.svg", @@ -10476,6 +10982,7 @@ "kind": "native", "denom": "factory/stars16da2uus9zrsy83h23ur42v3lglg5rmyrpqnju4/uBRNCH", "decimals": 6, + "variant": "cosmos", "displayName": "BRNCH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/brnch.svg", @@ -10485,6 +10992,7 @@ "kind": "native", "denom": "factory/stars16da2uus9zrsy83h23ur42v3lglg5rmyrpqnju4/uOHH", "decimals": 6, + "variant": "cosmos", "displayName": "OHH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/ohh.svg", @@ -10494,6 +11002,7 @@ "kind": "native", "denom": "factory/stars1xx5976njvxpl9n4v8huvff6cudhx7yuu8e7rt4/usneaky", "decimals": 6, + "variant": "cosmos", "displayName": "SNEAKY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/sneaky.svg", @@ -10532,6 +11041,7 @@ "kind": "native", "denom": "ustars", "decimals": 6, + "variant": "cosmos", "displayName": "STARS", "coingeckoId": "stargaze", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stargaze/images/stars.png", @@ -10569,6 +11079,7 @@ "kind": "native", "denom": "uiov", "decimals": 6, + "variant": "cosmos", "displayName": "IOV", "coingeckoId": "starname", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/starname/images/iov.svg", @@ -10607,6 +11118,7 @@ "kind": "native", "denom": "ustate", "decimals": 6, + "variant": "cosmos", "displayName": "STATE", "coingeckoId": "not-found", "icon": "not-found", @@ -10644,6 +11156,7 @@ "kind": "native", "denom": "wei", "decimals": 18, + "variant": "cosmos", "displayName": "STOS", "coingeckoId": "stratos", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stratos/images/stratos.svg", @@ -10682,6 +11195,7 @@ "kind": "native", "denom": "ustrd", "decimals": 6, + "variant": "cosmos", "displayName": "STRD", "coingeckoId": "stride", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/strd.svg", @@ -10691,6 +11205,7 @@ "kind": "native", "denom": "stuatom", "decimals": 6, + "variant": "cosmos", "displayName": "stATOM", "coingeckoId": "stride-staked-atom", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/statom.svg", @@ -10700,6 +11215,7 @@ "kind": "native", "denom": "stustars", "decimals": 6, + "variant": "cosmos", "displayName": "stSTARS", "coingeckoId": "stride-staked-stars", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/ststars.svg", @@ -10709,6 +11225,7 @@ "kind": "native", "denom": "stuosmo", "decimals": 6, + "variant": "cosmos", "displayName": "stOSMO", "coingeckoId": "stride-staked-osmo", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stosmo.svg", @@ -10718,6 +11235,7 @@ "kind": "native", "denom": "stujuno", "decimals": 6, + "variant": "cosmos", "displayName": "stJUNO", "coingeckoId": "stride-staked-juno", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stjuno.svg", @@ -10727,6 +11245,7 @@ "kind": "native", "denom": "stuluna", "decimals": 6, + "variant": "cosmos", "displayName": "stLUNA", "coingeckoId": "stride-staked-luna", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stluna.svg", @@ -10736,6 +11255,7 @@ "kind": "native", "denom": "stinj", "decimals": 18, + "variant": "cosmos", "displayName": "stINJ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stinj.svg", @@ -10745,6 +11265,7 @@ "kind": "native", "denom": "staevmos", "decimals": 18, + "variant": "cosmos", "displayName": "stEVMOS", "coingeckoId": "stride-staked-evmos", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stevmos.svg", @@ -10754,6 +11275,7 @@ "kind": "native", "denom": "stuumee", "decimals": 6, + "variant": "cosmos", "displayName": "stUMEE", "coingeckoId": "stride-staked-umee", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stumee.svg", @@ -10763,6 +11285,7 @@ "kind": "native", "denom": "stucmdx", "decimals": 6, + "variant": "cosmos", "displayName": "stCMDX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stcmdx.svg", @@ -10772,6 +11295,7 @@ "kind": "native", "denom": "stusomm", "decimals": 6, + "variant": "cosmos", "displayName": "stSOMM", "coingeckoId": "stride-staked-sommelier", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/stsomm.svg", @@ -10810,6 +11334,7 @@ "kind": "native", "denom": "ustrd", "decimals": 6, + "variant": "cosmos", "displayName": "STRD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/stride/images/strd.svg", @@ -10847,6 +11372,7 @@ "kind": "native", "denom": "utprl", "decimals": 6, + "variant": "cosmos", "displayName": "TPRL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/temporaltestnet/images/temporal.png", @@ -10884,6 +11410,7 @@ "kind": "native", "denom": "atenet", "decimals": 18, + "variant": "cosmos", "displayName": "TENET", "coingeckoId": "tenet-1b000f7b-59cb-4e06-89ce-d62b32d362b9", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/tenet/images/tenet.svg", @@ -10942,6 +11469,7 @@ { "denom": "utori", "displayName": "TORI", + "variant": "cosmos", "decimals": 6, "coingeckoId": "teritori", "icon": "teritori-circle.svg", @@ -11030,6 +11558,7 @@ { "denom": "utori", "displayName": "TORI", + "variant": "cosmos", "decimals": 6, "coingeckoId": "teritori", "icon": "icons/networks/teritori-circle.svg", @@ -11120,6 +11649,7 @@ { "denom": "utori", "displayName": "TORI", + "variant": "cosmos", "decimals": 6, "coingeckoId": "teritori", "icon": "teritori-circle.svg", @@ -11198,6 +11728,7 @@ "kind": "native", "denom": "uterpx", "decimals": 6, + "variant": "cosmos", "displayName": "TERPX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terpnetwork/images/terp.png", @@ -11207,6 +11738,7 @@ "kind": "native", "denom": "uthiolx", "decimals": 6, + "variant": "cosmos", "displayName": "THIOLX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terpnetwork/images/thiol.png", @@ -11244,6 +11776,7 @@ "kind": "native", "denom": "uluna", "decimals": 6, + "variant": "cosmos", "displayName": "LUNA", "coingeckoId": "terra-luna-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/luna.svg", @@ -11253,6 +11786,7 @@ "kind": "native", "denom": "cw20:terra1nsuqsk6kh58ulczatwev87ttq2z6r3pusulg9r24mfj2fvtzd4uq3exn26", "decimals": 6, + "variant": "cosmos", "displayName": "ASTRO", "coingeckoId": "astroport-fi", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/astro.png", @@ -11262,6 +11796,7 @@ "kind": "native", "denom": "cw20:terra1spkm49wd9dqkranhrks4cupecl3rtgeqqljq3qrvrrts2ev2gw6sy5vz3k", "decimals": 6, + "variant": "cosmos", "displayName": "DINHEIROS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/Dinheiros.png", @@ -11271,6 +11806,7 @@ "kind": "native", "denom": "cw20:terra1sdglum2dt4f3fmq7jrt2phf2tegmnudc7qqqqujkpqcm9ujuxxkqakv5u8", "decimals": 6, + "variant": "cosmos", "displayName": "REIS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/Reis.png", @@ -11280,6 +11816,7 @@ "kind": "native", "denom": "cw20:terra1qj5hs3e86qn4vm9dvtgtlkdp550r0rayk9wpay44mfw3gn3tr8nq5jw3dg", "decimals": 6, + "variant": "cosmos", "displayName": "ESCUDOS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/Escudos.png", @@ -11289,6 +11826,7 @@ "kind": "native", "denom": "cw20:terra1cmf8ytutcwrjrv08zskj9phuh46a3w3nkjax7en4hxezsrdr58lqvzy05q", "decimals": 6, + "variant": "cosmos", "displayName": "ALEM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/Alem.png", @@ -11298,6 +11836,7 @@ "kind": "native", "denom": "cw20:terra1ecgazyd0waaj3g7l9cmy5gulhxkps2gmxu9ghducvuypjq68mq2s5lvsct", "decimals": 6, + "variant": "cosmos", "displayName": "ampLUNA", "coingeckoId": "eris-amplified-luna", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/ampluna.svg", @@ -11307,6 +11846,7 @@ "kind": "native", "denom": "cw20:terra1lxx40s29qvkrcj8fsa3yzyehy7w50umdvvnls2r830rys6lu2zns63eelv", "decimals": 6, + "variant": "cosmos", "displayName": "ROAR", "coingeckoId": "lion-dao", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/roar.png", @@ -11316,6 +11856,7 @@ "kind": "native", "denom": "cw20:terra1ynvsz80w9xmhdxucv96gkwpxlwvjgsq75xh2f3pf825c4wfmkfxskq6pqv", "decimals": 6, + "variant": "cosmos", "displayName": "GEM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/gem.png", @@ -11325,6 +11866,7 @@ "kind": "native", "denom": "cw20:terra1lalvk0r6nhruel7fvzdppk3tup3mh5j4d4eadrqzfhle4zrf52as58hh9t", "decimals": 6, + "variant": "cosmos", "displayName": "CUB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/cub.png", @@ -11334,6 +11876,7 @@ "kind": "native", "denom": "cw20:terra1gwrz9xzhqsygyr5asrgyq3pu0ewpn00mv2zenu86yvx2nlwpe8lqppv584", "decimals": 6, + "variant": "cosmos", "displayName": "BLUE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/blue.png", @@ -11343,6 +11886,7 @@ "kind": "native", "denom": "cw20:terra10se906awphtccf4vd83m0ulpmpt9v4msuakmpy0pwvmtxmup3kdq25rayn", "decimals": 10, + "variant": "cosmos", "displayName": "xxx", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/xxx3.png", @@ -11352,6 +11896,7 @@ "kind": "native", "denom": "cw20:terra17aj4ty4sz4yhgm08na8drc0v03v2jwr3waxcqrwhajj729zhl7zqnpc0ml", "decimals": 6, + "variant": "cosmos", "displayName": "bLUNA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/boneluna.png", @@ -11361,6 +11906,7 @@ "kind": "native", "denom": "cw20:terra1xp9hrhthzddnl7j5du83gqqr4wmdjm5t0guzg9jp6jwrtpukwfjsjgy4f3", "decimals": 6, + "variant": "cosmos", "displayName": "SAYVE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/sayve.svg", @@ -11370,6 +11916,7 @@ "kind": "native", "denom": "factory/terra1j35ta0llaxcf55auv2cjqau5a7aee6g8fz7md7my7005cvh23jfsaw83dy/ampWHALEt", "decimals": 6, + "variant": "cosmos", "displayName": "ampWHALEt", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/ampwhalet.svg", @@ -11379,6 +11926,7 @@ "kind": "native", "denom": "factory/terra10j3zrymfrkta2pxe0gklc79gu06tqyuy8c3kh6tqdsrrprsjqkrqzfl4df/boneWHALEt", "decimals": 6, + "variant": "cosmos", "displayName": "boneWHALEt", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/bonewhalet.svg", @@ -11388,6 +11936,7 @@ "kind": "native", "denom": "factory/terra1vklefn7n6cchn0u962w3gaszr4vf52wjvd4y95t2sydwpmpdtszsqvk9wy/ampROAR", "decimals": 6, + "variant": "cosmos", "displayName": "ampROAR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/amproar.png", @@ -11397,6 +11946,7 @@ "kind": "native", "denom": "cw20:terra1e0efrrrj8d55pflme3dmtyuj7klzcef5cfmz6r2jyqz77kk2jz3qa6drg3", "decimals": 18, + "variant": "cosmos", "displayName": "NICO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/nicodao.png", @@ -11406,6 +11956,7 @@ "kind": "native", "denom": "cw20:terra1rc6ssp5rym7a0hg29xpj4cc9e67tl56kg5jyzgl9qrhfxxc2ugvsnrkala", "decimals": 6, + "variant": "cosmos", "displayName": "SEAS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/SEAS.png", @@ -11415,6 +11966,7 @@ "kind": "native", "denom": "cw20:terra18vp5s0r7keezm35hdxsgw8zgfnyn8wejdkk893ag2kqncgpqxhjqwjpc0v", "decimals": 6, + "variant": "cosmos", "displayName": "BITZ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/BITZ.png", @@ -11424,6 +11976,7 @@ "kind": "native", "denom": "cw20:terra13s5pxw5j2p4ssvzwvxd8l7h30vke8vjgtng75vqgv6p9vddfk3hskfka0l", "decimals": 6, + "variant": "cosmos", "displayName": "SEUL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/seul.png", @@ -11433,6 +11986,7 @@ "kind": "native", "denom": "cw20:terra1q328gl40az3cf9x67cgudn8e8w2az9vsmhtkwsgdu7a43rhy5caqc82yr5", "decimals": 6, + "variant": "cosmos", "displayName": "xSEUL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/xseul.png", @@ -11442,6 +11996,7 @@ "kind": "native", "denom": "cw20:terra1c77xqv746m7ghxayrge79dxr4kcezev8g6cnrfled4f3n4ufj0vs5gz28s", "decimals": 6, + "variant": "cosmos", "displayName": "ITO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/I.png", @@ -11451,6 +12006,7 @@ "kind": "native", "denom": "cw20:terra1aa7stl3fytvave9xtcexgv0kne4k7ks068dcljkrfj37hy8q270sjadav8", "decimals": 6, + "variant": "cosmos", "displayName": "ARMANI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/armani.png", @@ -11460,6 +12016,7 @@ "kind": "native", "denom": "cw20:terra1cl273523kmr2uwjhhznq54je69mted2u3ljffm8kp2ap4z3drdksftwqun", "decimals": 6, + "variant": "cosmos", "displayName": "DROGO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/drogo.png", @@ -11469,6 +12026,7 @@ "kind": "native", "denom": "cw20:terra1w8xk6rtu40st6lvl3yv7ynw5urm2n686u9cchvrzltmnktzwdesqcwy0nu", "decimals": 6, + "variant": "cosmos", "displayName": "ADO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/ADO.png", @@ -11507,6 +12065,7 @@ "kind": "native", "denom": "uluna", "decimals": 6, + "variant": "cosmos", "displayName": "LUNA", "coingeckoId": "terra-luna-2", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra2/images/luna.svg", @@ -11544,6 +12103,7 @@ "kind": "native", "denom": "uluna", "decimals": 6, + "variant": "cosmos", "displayName": "LUNC", "coingeckoId": "terra-luna", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luna.svg", @@ -11553,6 +12113,7 @@ "kind": "native", "denom": "uusd", "decimals": 6, + "variant": "cosmos", "displayName": "USTC", "coingeckoId": "terrausd", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/ust.svg", @@ -11562,6 +12123,7 @@ "kind": "native", "denom": "ukrw", "decimals": 6, + "variant": "cosmos", "displayName": "KRTC", "coingeckoId": "terrakrw", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/krt.svg", @@ -11571,6 +12133,7 @@ "kind": "native", "denom": "cw20:terra1php5m8a6qd68z02t3zpw4jv2pj4vgw4wz0t8mz", "decimals": 6, + "variant": "cosmos", "displayName": "WHALE", "coingeckoId": "white-whale", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/whale.png", @@ -11580,6 +12143,7 @@ "kind": "native", "denom": "cw20:terra1kc87mu460fwkqte29rquh4hc20m54fxwtsx7gp", "decimals": 6, + "variant": "cosmos", "displayName": "bLuna", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/bluna.png", @@ -11589,6 +12153,7 @@ "kind": "native", "denom": "cw20:terra1dzhzukyezv0etz22ud940z7adyv7xgcjkahuun", "decimals": 6, + "variant": "cosmos", "displayName": "bETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/beth.png", @@ -11598,6 +12163,7 @@ "kind": "native", "denom": "cw20:terra1hzh9vpxhsk8253se0vv5jj6etdvxu3nv8z07zu", "decimals": 6, + "variant": "cosmos", "displayName": "aUST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/aust.png", @@ -11607,6 +12173,7 @@ "kind": "native", "denom": "cw20:terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", "decimals": 6, + "variant": "cosmos", "displayName": "ANC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/anc.png", @@ -11616,6 +12183,7 @@ "kind": "native", "denom": "cw20:terra15gwkyepfc6xgca5t5zefzwy42uts8l2m4g40k6", "decimals": 6, + "variant": "cosmos", "displayName": "MIR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mir.png", @@ -11625,6 +12193,7 @@ "kind": "native", "denom": "cw20:terra1vxtwu4ehgzz77mnfwrntyrmgl64qjs75mpwqaz", "decimals": 6, + "variant": "cosmos", "displayName": "mAAPL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/maapl.png", @@ -11634,6 +12203,7 @@ "kind": "native", "denom": "cw20:terra1g4x2pzmkc9z3mseewxf758rllg08z3797xly0n", "decimals": 6, + "variant": "cosmos", "displayName": "mABNB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mabnb.png", @@ -11643,6 +12213,7 @@ "kind": "native", "denom": "cw20:terra1qelfthdanju7wavc5tq0k5r0rhsyzyyrsn09qy", "decimals": 6, + "variant": "cosmos", "displayName": "mAMC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mamc.png", @@ -11652,6 +12223,7 @@ "kind": "native", "denom": "cw20:terra18ej5nsuu867fkx4tuy2aglpvqjrkcrjjslap3z", "decimals": 6, + "variant": "cosmos", "displayName": "mAMD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mamd.png", @@ -11661,6 +12233,7 @@ "kind": "native", "denom": "cw20:terra165nd2qmrtszehcfrntlplzern7zl4ahtlhd5t2", "decimals": 6, + "variant": "cosmos", "displayName": "mAMZN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mamzn.png", @@ -11670,6 +12243,7 @@ "kind": "native", "denom": "cw20:terra1qqfx5jph0rsmkur2zgzyqnfucra45rtjae5vh6", "decimals": 6, + "variant": "cosmos", "displayName": "mARKK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/markk.png", @@ -11679,6 +12253,7 @@ "kind": "native", "denom": "cw20:terra1w7zgkcyt7y4zpct9dw8mw362ywvdlydnum2awa", "decimals": 6, + "variant": "cosmos", "displayName": "mBABA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mbaba.png", @@ -11688,6 +12263,7 @@ "kind": "native", "denom": "cw20:terra1rhhvx8nzfrx5fufkuft06q5marfkucdqwq5sjw", "decimals": 6, + "variant": "cosmos", "displayName": "mBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mbtc.png", @@ -11697,6 +12273,7 @@ "kind": "native", "denom": "cw20:terra18wayjpyq28gd970qzgjfmsjj7dmgdk039duhph", "decimals": 6, + "variant": "cosmos", "displayName": "mCOIN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mcoin.png", @@ -11706,6 +12283,7 @@ "kind": "native", "denom": "cw20:terra149755r3y0rve30e209awkhn5cxgkn5c8ju9pm5", "decimals": 6, + "variant": "cosmos", "displayName": "mDIS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mdis.png", @@ -11715,6 +12293,7 @@ "kind": "native", "denom": "cw20:terra19ya4jpvjvvtggepvmmj6ftmwly3p7way0tt08r", "decimals": 6, + "variant": "cosmos", "displayName": "mDOT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mdot.png", @@ -11724,6 +12303,7 @@ "kind": "native", "denom": "cw20:terra1dk3g53js3034x4v5c3vavhj2738une880yu6kx", "decimals": 6, + "variant": "cosmos", "displayName": "mETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/meth.png", @@ -11733,6 +12313,7 @@ "kind": "native", "denom": "cw20:terra1mqsjugsugfprn3cvgxsrr8akkvdxv2pzc74us7", "decimals": 6, + "variant": "cosmos", "displayName": "mFB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mfb.png", @@ -11742,6 +12323,7 @@ "kind": "native", "denom": "cw20:terra1l5lrxtwd98ylfy09fn866au6dp76gu8ywnudls", "decimals": 6, + "variant": "cosmos", "displayName": "mGLXY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mglxy.png", @@ -11751,6 +12333,7 @@ "kind": "native", "denom": "cw20:terra1m6j6j9gw728n82k78s0j9kq8l5p6ne0xcc820p", "decimals": 6, + "variant": "cosmos", "displayName": "mGME", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mgme.png", @@ -11760,6 +12343,7 @@ "kind": "native", "denom": "cw20:terra1h8arz2k547uvmpxctuwush3jzc8fun4s96qgwt", "decimals": 6, + "variant": "cosmos", "displayName": "mGOOGL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mgoogl.png", @@ -11769,6 +12353,7 @@ "kind": "native", "denom": "cw20:terra137drsu8gce5thf6jr5mxlfghw36rpljt3zj73v", "decimals": 6, + "variant": "cosmos", "displayName": "mGS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mgs.png", @@ -11778,6 +12363,7 @@ "kind": "native", "denom": "cw20:terra18yqdfzfhnguerz9du5mnvxsh5kxlknqhcxzjfr", "decimals": 6, + "variant": "cosmos", "displayName": "mHOOD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mhood.png", @@ -11787,6 +12373,7 @@ "kind": "native", "denom": "cw20:terra10h7ry7apm55h4ez502dqdv9gr53juu85nkd4aq", "decimals": 6, + "variant": "cosmos", "displayName": "mIAU", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/miau.png", @@ -11796,6 +12383,7 @@ "kind": "native", "denom": "cw20:terra15hp9pr8y4qsvqvxf3m4xeptlk7l8h60634gqec", "decimals": 6, + "variant": "cosmos", "displayName": "mIAU", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/miau.png", @@ -11805,6 +12393,7 @@ "kind": "native", "denom": "cw20:terra1ptdxmj3xmmljzx02nr4auwfuelmj0cnkh8egs2", "decimals": 6, + "variant": "cosmos", "displayName": "mJNJ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mjnj.png", @@ -11814,6 +12403,7 @@ "kind": "native", "denom": "cw20:terra1qsnj5gvq8rgs7yws8x5u02gwd5wvtu4tks0hjm", "decimals": 6, + "variant": "cosmos", "displayName": "mKO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mko.png", @@ -11823,6 +12413,7 @@ "kind": "native", "denom": "cw20:terra1227ppwxxj3jxz8cfgq00jgnxqcny7ryenvkwj6", "decimals": 6, + "variant": "cosmos", "displayName": "mMSFT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mmsft.png", @@ -11832,6 +12423,7 @@ "kind": "native", "denom": "cw20:terra1jsxngqasf2zynj5kyh0tgq9mj3zksa5gk35j4k", "decimals": 6, + "variant": "cosmos", "displayName": "mNFLX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mnflx.png", @@ -11841,6 +12433,7 @@ "kind": "native", "denom": "cw20:terra1dj2cj02zak0nvwy3uj9r9dhhxhdwxnw6psse6p", "decimals": 6, + "variant": "cosmos", "displayName": "mNIO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mnio.png", @@ -11850,6 +12443,7 @@ "kind": "native", "denom": "cw20:terra17ana8hvzea0q7w367dm0dw48sxwql39qekpt7g", "decimals": 6, + "variant": "cosmos", "displayName": "mNKE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mnke.png", @@ -11859,6 +12453,7 @@ "kind": "native", "denom": "cw20:terra1drsjzvzej4h4qlehcfwclxg4w5l3h5tuvd3jd8", "decimals": 6, + "variant": "cosmos", "displayName": "mNVDA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mnvda.png", @@ -11868,6 +12463,7 @@ "kind": "native", "denom": "cw20:terra1rh2907984nudl7vh56qjdtvv7947z4dujj92sx", "decimals": 6, + "variant": "cosmos", "displayName": "mPYPL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mpypl.png", @@ -11877,6 +12473,7 @@ "kind": "native", "denom": "cw20:terra1csk6tc7pdmpr782w527hwhez6gfv632tyf72cp", "decimals": 6, + "variant": "cosmos", "displayName": "mQQQ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mqqq.png", @@ -11886,6 +12483,7 @@ "kind": "native", "denom": "cw20:terra1246zy658dfgtausf0c4a6ly8sc2e285q4kxqga", "decimals": 6, + "variant": "cosmos", "displayName": "mSBUX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/msbux.png", @@ -11895,6 +12493,7 @@ "kind": "native", "denom": "cw20:terra1kscs6uhrqwy6rx5kuw5lwpuqvm3t6j2d6uf2lp", "decimals": 6, + "variant": "cosmos", "displayName": "mSLV", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mslv.png", @@ -11904,6 +12503,7 @@ "kind": "native", "denom": "cw20:terra1aa00lpfexyycedfg5k2p60l9djcmw0ue5l8fhc", "decimals": 6, + "variant": "cosmos", "displayName": "mSPY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mspy.png", @@ -11913,6 +12513,7 @@ "kind": "native", "denom": "cw20:terra1u43zu5amjlsgty5j64445fr9yglhm53m576ugh", "decimals": 6, + "variant": "cosmos", "displayName": "mSQ", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/msq.png", @@ -11922,6 +12523,7 @@ "kind": "native", "denom": "cw20:terra14y5affaarufk3uscy2vr6pe6w6zqf2wpjzn5sh", "decimals": 6, + "variant": "cosmos", "displayName": "mTSLA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mtsla.png", @@ -11931,6 +12533,7 @@ "kind": "native", "denom": "cw20:terra1cc3enj9qgchlrj34cnzhwuclc4vl2z3jl7tkqg", "decimals": 6, + "variant": "cosmos", "displayName": "mTWTR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mtwtr.png", @@ -11940,6 +12543,7 @@ "kind": "native", "denom": "cw20:terra1lvmx8fsagy70tv0fhmfzdw9h6s3sy4prz38ugf", "decimals": 6, + "variant": "cosmos", "displayName": "mUSO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/muso.png", @@ -11949,6 +12553,7 @@ "kind": "native", "denom": "cw20:terra19cmt6vzvhnnnfsmccaaxzy2uaj06zjktu6yzjx", "decimals": 6, + "variant": "cosmos", "displayName": "mVIXY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mvixy.png", @@ -11958,6 +12563,7 @@ "kind": "native", "denom": "cw20:terra1zp3a6q6q4953cz376906g5qfmxnlg77hx3te45", "decimals": 6, + "variant": "cosmos", "displayName": "mVIXY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mvixy.png", @@ -11967,6 +12573,7 @@ "kind": "native", "denom": "cw20:terra1ez46kxtulsdv07538fh5ra5xj8l68mu8eg24vr", "decimals": 6, + "variant": "cosmos", "displayName": "LOTA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/lota.png", @@ -11976,6 +12583,7 @@ "kind": "native", "denom": "cw20:terra17jnhankdfl8vyzj6vejt7ag8uz0cjc9crkl2h7", "decimals": 6, + "variant": "cosmos", "displayName": "DPH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/dph.png", @@ -11985,6 +12593,7 @@ "kind": "native", "denom": "cw20:terra1kcthelkax4j9x8d3ny6sdag0qmxxynl3qtcrpy", "decimals": 6, + "variant": "cosmos", "displayName": "MINE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mine.png", @@ -11994,6 +12603,7 @@ "kind": "native", "denom": "cw20:terra1zsaswh926ey8qa5x4vj93kzzlfnef0pstuca0y", "decimals": 6, + "variant": "cosmos", "displayName": "bPsiDP-24m", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/bpsidp-24m.png", @@ -12003,6 +12613,7 @@ "kind": "native", "denom": "cw20:terra1s5eczhe0h0jutf46re52x5z4r03c8hupacxmdr", "decimals": 6, + "variant": "cosmos", "displayName": "SPEC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/spec.png", @@ -12012,6 +12623,7 @@ "kind": "native", "denom": "cw20:terra1nef5jf6c7js9x6gkntlehgywvjlpytm7pcgkn4", "decimals": 6, + "variant": "cosmos", "displayName": "LOOP", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/loop.png", @@ -12021,6 +12633,7 @@ "kind": "native", "denom": "cw20:terra1jx4lmmke2srcvpjeereetc9hgegp4g5j0p9r2q", "decimals": 6, + "variant": "cosmos", "displayName": "LOOPR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/loopr.png", @@ -12030,6 +12643,7 @@ "kind": "native", "denom": "cw20:terra13xujxcrc9dqft4p9a8ls0w3j0xnzm6y2uvve8n", "decimals": 6, + "variant": "cosmos", "displayName": "STT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/stt.png", @@ -12039,6 +12653,7 @@ "kind": "native", "denom": "cw20:terra19djkaepjjswucys4npd5ltaxgsntl7jf0xz7w6", "decimals": 6, + "variant": "cosmos", "displayName": "TWD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/twd.png", @@ -12048,6 +12663,7 @@ "kind": "native", "denom": "cw20:terra1kvjscdgwuvwc6uzm4rqfjl6nlmuhj28tequlnc", "decimals": 6, + "variant": "cosmos", "displayName": "XTRA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/xtra.png", @@ -12057,6 +12673,7 @@ "kind": "native", "denom": "cw20:terra1vtr50tw0pgqpes34zqu60n554p9x4950wk8f63", "decimals": 6, + "variant": "cosmos", "displayName": "MIAW", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/miaw.png", @@ -12066,6 +12683,7 @@ "kind": "native", "denom": "cw20:terra12897djskt9rge8dtmm86w654g7kzckkd698608", "decimals": 6, + "variant": "cosmos", "displayName": "Psi", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/psi.png", @@ -12075,6 +12693,7 @@ "kind": "native", "denom": "cw20:terra10f2mt82kjnkxqj2gepgwl637u2w4ue2z5nhz5j", "decimals": 6, + "variant": "cosmos", "displayName": "nLuna", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/nluna.svg", @@ -12084,6 +12703,7 @@ "kind": "native", "denom": "cw20:terra178v546c407pdnx5rer3hu8s2c0fc924k74ymnn", "decimals": 6, + "variant": "cosmos", "displayName": "nETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/neth.svg", @@ -12093,6 +12713,7 @@ "kind": "native", "denom": "cw20:terra1u553zk43jd4rwzc53qrdrq4jc2p8rextyq09dj", "decimals": 6, + "variant": "cosmos", "displayName": "cnLuna", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cnluna.svg", @@ -12102,6 +12723,7 @@ "kind": "native", "denom": "cw20:terra1nagqpmyw55yjphea4rhntlfv87ugmeaj8ym756", "decimals": 6, + "variant": "cosmos", "displayName": "cnETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cneth.svg", @@ -12111,6 +12733,7 @@ "kind": "native", "denom": "cw20:terra13k62n0285wj8ug0ngcgpf7dgnkzqeu279tz636", "decimals": 6, + "variant": "cosmos", "displayName": "nAVAX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/navax.svg", @@ -12120,6 +12743,7 @@ "kind": "native", "denom": "cw20:terra1jtdc6zpf95tvh9peuaxwp3v0yqszcnwl8j5ade", "decimals": 6, + "variant": "cosmos", "displayName": "nATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/natom.svg", @@ -12129,6 +12753,7 @@ "kind": "native", "denom": "cw20:terra1dy9kmlm4anr92e42mrkjwzyvfqwz66un00rwr5", "decimals": 6, + "variant": "cosmos", "displayName": "VKR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/vkr.png", @@ -12138,6 +12763,7 @@ "kind": "native", "denom": "cw20:terra1mddcdx0ujx89f38gu7zspk2r2ffdl5enyz2u03", "decimals": 8, + "variant": "cosmos", "displayName": "ORION", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/orion.png", @@ -12147,6 +12773,7 @@ "kind": "native", "denom": "cw20:terra1r5506ckw5tfr3z52jwlek8vg9sn3yflrqrzfsc", "decimals": 6, + "variant": "cosmos", "displayName": "TLAND", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tland.png", @@ -12156,6 +12783,7 @@ "kind": "native", "denom": "cw20:terra1w0p5zre38ecdy3ez8efd5h9fvgum5s206xknrg", "decimals": 6, + "variant": "cosmos", "displayName": "vUST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/vust.png", @@ -12165,6 +12793,7 @@ "kind": "native", "denom": "cw20:terra14tl83xcwqjy0ken9peu4pjjuu755lrry2uy25r", "decimals": 8, + "variant": "cosmos", "displayName": "ETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/eth.png", @@ -12174,6 +12803,7 @@ "kind": "native", "denom": "cw20:terra1aa7upykmmqqc63l924l5qfap8mrmx5rfdm0v55", "decimals": 8, + "variant": "cosmos", "displayName": "WBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/wbtc.png", @@ -12183,6 +12813,7 @@ "kind": "native", "denom": "cw20:terra190tqwgqx7s8qrknz6kckct7v607cu068gfujpk", "decimals": 8, + "variant": "cosmos", "displayName": "SOL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sol.png", @@ -12192,6 +12823,7 @@ "kind": "native", "denom": "cw20:terra1dfasranqm4uyaz72r960umxy0w8t6zewqlnkuq", "decimals": 8, + "variant": "cosmos", "displayName": "MATICet", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/maticet.png", @@ -12201,6 +12833,7 @@ "kind": "native", "denom": "cw20:terra1cetg5wruw2wsdjp7j46rj44xdel00z006e9yg8", "decimals": 8, + "variant": "cosmos", "displayName": "BNB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/bnb.png", @@ -12210,6 +12843,7 @@ "kind": "native", "denom": "cw20:terra1xvqlpjl2dxyel9qrp6qvtrg04xe3jh9cyxc6av", "decimals": 8, + "variant": "cosmos", "displayName": "CAKE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cake.png", @@ -12219,6 +12853,7 @@ "kind": "native", "denom": "cw20:terra12dfv3f0e6m22z6cnhfn3nxk2en3z3zeqy6ctym", "decimals": 8, + "variant": "cosmos", "displayName": "LINK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/link.png", @@ -12228,6 +12863,7 @@ "kind": "native", "denom": "cw20:terra1csvuzlf92nyemu6tv25h0l79etpe8hz3h5vn4a", "decimals": 8, + "variant": "cosmos", "displayName": "SUSHI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sushi.png", @@ -12237,6 +12873,7 @@ "kind": "native", "denom": "cw20:terra1wyxkuy5jq545fn7xfn3enpvs5zg9f9dghf6gxf", "decimals": 8, + "variant": "cosmos", "displayName": "UNI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/uni.png", @@ -12246,6 +12883,7 @@ "kind": "native", "denom": "cw20:terra1ce06wkrdm4vl6t0hvc0g86rsy27pu8yadg3dva", "decimals": 6, + "variant": "cosmos", "displayName": "USDTet", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdtet.png", @@ -12255,6 +12893,7 @@ "kind": "native", "denom": "cw20:terra1pepwcav40nvj3kh60qqgrk8k07ydmc00xyat06", "decimals": 6, + "variant": "cosmos", "displayName": "USDCet", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdcet.png", @@ -12264,6 +12903,7 @@ "kind": "native", "denom": "cw20:terra1xfsdgcemqwxp4hhnyk4rle6wr22sseq7j07dnn", "decimals": 6, + "variant": "cosmos", "displayName": "KUJI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/kuji.png", @@ -12273,6 +12913,7 @@ "kind": "native", "denom": "cw20:terra188w26t95tf4dz77raftme8p75rggatxjxfeknw", "decimals": 6, + "variant": "cosmos", "displayName": "sKUJI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/skuji.png", @@ -12282,6 +12923,7 @@ "kind": "native", "denom": "cw20:terra133chr09wu8sakfte5v7vd8qzq9vghtkv4tn0ur", "decimals": 8, + "variant": "cosmos", "displayName": "wstETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/wsteth.png", @@ -12291,6 +12933,7 @@ "kind": "native", "denom": "cw20:terra1t9ul45l7m6jw6sxgvnp8e5hj8xzkjsg82g84ap", "decimals": 8, + "variant": "cosmos", "displayName": "wstSOL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/wstsol.png", @@ -12300,6 +12943,7 @@ "kind": "native", "denom": "cw20:terra1c3xd5s2j3ejx2d94tvcjfkrdeu6rmz48ghzznj", "decimals": 8, + "variant": "cosmos", "displayName": "wsbSOL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/wsbsol.svg", @@ -12309,6 +12953,7 @@ "kind": "native", "denom": "cw20:terra1jxypgnfa07j6w92wazzyskhreq2ey2a5crgt6z", "decimals": 8, + "variant": "cosmos", "displayName": "LDO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/ldo.png", @@ -12318,6 +12963,7 @@ "kind": "native", "denom": "cw20:terra1u5szg038ur9kzuular3cae8hq6q5rk5u27tuvz", "decimals": 8, + "variant": "cosmos", "displayName": "webETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/webeth.svg", @@ -12327,6 +12973,7 @@ "kind": "native", "denom": "cw20:terra1yg3j2s986nyp5z7r2lvt0hx3r0lnd7kwvwwtsc", "decimals": 6, + "variant": "cosmos", "displayName": "stLuna", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/stluna.png", @@ -12336,6 +12983,7 @@ "kind": "native", "denom": "cw20:terra169edevav3pdrtjcx35j6pvzuv54aevewar4nlh", "decimals": 8, + "variant": "cosmos", "displayName": "XDEFI", "coingeckoId": "not-found", "icon": "not-found", @@ -12345,6 +12993,7 @@ "kind": "native", "denom": "cw20:terra193c42lfwmlkasvcw22l9qqzc5q2dx208tkd7wl", "decimals": 6, + "variant": "cosmos", "displayName": "BTL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/btl.png", @@ -12354,6 +13003,7 @@ "kind": "native", "denom": "cw20:terra17y9qkl8dfkeg4py7n0g5407emqnemc3yqk5rup", "decimals": 6, + "variant": "cosmos", "displayName": "LunaX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/lunax.png", @@ -12363,6 +13013,7 @@ "kind": "native", "denom": "cw20:terra1m3tdguf59xq3pa2twk5fjte5g6szj5y9x5npy7", "decimals": 6, + "variant": "cosmos", "displayName": "LUNI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luni.png", @@ -12372,6 +13023,7 @@ "kind": "native", "denom": "cw20:terra13awdgcx40tz5uygkgm79dytez3x87rpg4uhnvu", "decimals": 6, + "variant": "cosmos", "displayName": "PLY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/ply.png", @@ -12381,6 +13033,7 @@ "kind": "native", "denom": "cw20:terra1u2k0nkenw0p25ljsr4ksh7rxm65y466vkdewwj", "decimals": 6, + "variant": "cosmos", "displayName": "TFLOKI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tfloki.png", @@ -12390,6 +13043,7 @@ "kind": "native", "denom": "cw20:terra1a8k3jyv3wf6k3zngza5h6srrxcckdf7zv90p6u", "decimals": 6, + "variant": "cosmos", "displayName": "TFTIC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tftic.png", @@ -12399,6 +13053,7 @@ "kind": "native", "denom": "cw20:terra1xt9fgu7965kgvunnjts9zkprd8986kcc444q86", "decimals": 6, + "variant": "cosmos", "displayName": "TFTICII", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tfticii.png", @@ -12408,6 +13063,7 @@ "kind": "native", "denom": "cw20:terra1vte2xv7dr8sfnrnwdf9arcyprqgr0hty5ads28", "decimals": 6, + "variant": "cosmos", "displayName": "TFTICIII", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tfticiii.png", @@ -12417,6 +13073,7 @@ "kind": "native", "denom": "cw20:terra1hmxxq0y8h79f3228vs0czc4uz5jdgjt0appp26", "decimals": 6, + "variant": "cosmos", "displayName": "MOON", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/moon.png", @@ -12426,6 +13083,7 @@ "kind": "native", "denom": "cw20:terra1xj49zyqrwpv5k928jwfpfy2ha668nwdgkwlrg3", "decimals": 6, + "variant": "cosmos", "displayName": "ASTRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/astro.png", @@ -12435,6 +13093,7 @@ "kind": "native", "denom": "cw20:terra14lpnyzc9z4g3ugr4lhm8s4nle0tq8vcltkhzh7", "decimals": 6, + "variant": "cosmos", "displayName": "xASTRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/xastro.png", @@ -12444,6 +13103,7 @@ "kind": "native", "denom": "cw20:terra1w8kvd6cqpsthupsk4l0clwnmek4l3zr7c84kwq", "decimals": 6, + "variant": "cosmos", "displayName": "HALO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/halo.png", @@ -12453,6 +13113,7 @@ "kind": "native", "denom": "cw20:terra1kdfsdm3c4reun9j3m4mk3nmyw4a4ns7mj24q3j", "decimals": 6, + "variant": "cosmos", "displayName": "PUG", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/pug.png", @@ -12462,6 +13123,7 @@ "kind": "native", "denom": "cw20:terra1hnezwjqlhzawcrfysczcxs6xqxu2jawn729kkf", "decimals": 6, + "variant": "cosmos", "displayName": "ORNE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/orne.png", @@ -12471,6 +13133,7 @@ "kind": "native", "denom": "cw20:terra14vz4v8adanzph278xyeggll4tfww7teh0xtw2y", "decimals": 6, + "variant": "cosmos", "displayName": "TNS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tns.png", @@ -12480,6 +13143,7 @@ "kind": "native", "denom": "cw20:terra1td743l5k5cmfy7tqq202g7vkmdvq35q48u2jfm", "decimals": 6, + "variant": "cosmos", "displayName": "XRUNE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/xrune.png", @@ -12489,6 +13153,7 @@ "kind": "native", "denom": "cw20:terra1366wmr8t8rrkh6mag8fagqxntmf2qe4kyte784", "decimals": 6, + "variant": "cosmos", "displayName": "aLOT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/alot.png", @@ -12498,6 +13163,7 @@ "kind": "native", "denom": "cw20:terra1z09gnzufuflz6ckd9k0u456l9dnpgsynu0yyhe", "decimals": 6, + "variant": "cosmos", "displayName": "SITY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sity.svg", @@ -12507,6 +13173,7 @@ "kind": "native", "denom": "cw20:terra13zx49nk8wjavedjzu8xkk95r3t0ta43c9ptul7", "decimals": 6, + "variant": "cosmos", "displayName": "GLOW", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/glow.png", @@ -12516,6 +13183,7 @@ "kind": "native", "denom": "cw20:terra100yeqvww74h4yaejj6h733thgcafdaukjtw397", "decimals": 6, + "variant": "cosmos", "displayName": "APOLLO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/apollo.png", @@ -12525,6 +13193,7 @@ "kind": "native", "denom": "cw20:terra1a7ye2splpfzyenu0yrdu8t83uzgusx2malkc7u", "decimals": 6, + "variant": "cosmos", "displayName": "ABR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/abr.svg", @@ -12534,6 +13203,7 @@ "kind": "native", "denom": "cw20:terra1y3d5qexmyac0fg53pfglh2pjk0664ymfvcq9mc", "decimals": 8, + "variant": "cosmos", "displayName": "whGTPS", "coingeckoId": "not-found", "icon": "not-found", @@ -12543,6 +13213,7 @@ "kind": "native", "denom": "cw20:terra1dh9478k2qvqhqeajhn75a2a7dsnf74y5ukregw", "decimals": 6, + "variant": "cosmos", "displayName": "PRISM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/prism.png", @@ -12552,6 +13223,7 @@ "kind": "native", "denom": "cw20:terra1l0y8yg0s86x299nqw0p6fhh7ngex3r4phtjeuq", "decimals": 2, + "variant": "cosmos", "displayName": "SDOLLAR", "coingeckoId": "not-found", "icon": "not-found", @@ -12561,6 +13233,7 @@ "kind": "native", "denom": "cw20:terra1ku5e0dhutxhuxudsmsn5647wwcz6ndr3rsh90k", "decimals": 6, + "variant": "cosmos", "displayName": "whSAIL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/whsail.png", @@ -12570,6 +13243,7 @@ "kind": "native", "denom": "cw20:terra1rl0cpwgtwl4utnaynugevdje37fnmsea7rv4uu", "decimals": 8, + "variant": "cosmos", "displayName": "whgSAIL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/whgsail.png", @@ -12579,6 +13253,7 @@ "kind": "native", "denom": "cw20:terra1042wzrwg2uk6jqxjm34ysqquyr9esdgm5qyswz", "decimals": 6, + "variant": "cosmos", "displayName": "xPRISM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/xprism.png", @@ -12588,6 +13263,7 @@ "kind": "native", "denom": "cw20:terra13fs83g5atgjwuh7c5ydzh6n7gecel6xyhhy2t5", "decimals": 9, + "variant": "cosmos", "displayName": "CDE", "coingeckoId": "not-found", "icon": "not-found", @@ -12597,6 +13273,7 @@ "kind": "native", "denom": "cw20:terra1rl20t79ffsrqfa29rke48tj05gj9jxumm92vg8", "decimals": 6, + "variant": "cosmos", "displayName": "CTX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/ctx.png", @@ -12606,6 +13283,7 @@ "kind": "native", "denom": "cw20:terra13zaagrrrxj47qjwczsczujlvnnntde7fdt0mau", "decimals": 6, + "variant": "cosmos", "displayName": "cLuna", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cluna.png", @@ -12615,6 +13293,7 @@ "kind": "native", "denom": "cw20:terra1tlgelulz9pdkhls6uglfn5lmxarx7f2gxtdzh2", "decimals": 6, + "variant": "cosmos", "displayName": "pLuna", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/pluna.png", @@ -12624,6 +13303,7 @@ "kind": "native", "denom": "cw20:terra17wkadg0tah554r35x6wvff0y5s7ve8npcjfuhz", "decimals": 6, + "variant": "cosmos", "displayName": "yLuna", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/yluna.png", @@ -12633,6 +13313,7 @@ "kind": "native", "denom": "cw20:terra1cl7whtrqmz5ldr553q69qahck8xvk80fm33qjx", "decimals": 6, + "variant": "cosmos", "displayName": "ATLO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/atlo.png", @@ -12642,6 +13323,7 @@ "kind": "native", "denom": "cw20:terra1vchw83qt25j89zqwdpmdzj722sqxthnckqzxxp", "decimals": 6, + "variant": "cosmos", "displayName": "LOCAL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/local.png", @@ -12651,6 +13333,7 @@ "kind": "native", "denom": "cw20:terra15k5r9r8dl8r7xlr29pry8a9w7sghehcnv5mgp6", "decimals": 6, + "variant": "cosmos", "displayName": "LUV", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luv.png", @@ -12660,6 +13343,7 @@ "kind": "native", "denom": "cw20:terra1f62tqesptvmhtzr8sudru00gsdtdz24srgm7wp", "decimals": 6, + "variant": "cosmos", "displayName": "ROBO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/robo.png", @@ -12669,6 +13353,7 @@ "kind": "native", "denom": "cw20:terra1vwz7t30q76s7xx6qgtxdqnu6vpr3ak3vw62ygk", "decimals": 6, + "variant": "cosmos", "displayName": "LUART", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/luart.png", @@ -12678,6 +13363,7 @@ "kind": "native", "denom": "cw20:terra12hgwnpupflfpuual532wgrxu2gjp0tcagzgx4n", "decimals": 6, + "variant": "cosmos", "displayName": "MARS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mars.svg", @@ -12687,6 +13373,7 @@ "kind": "native", "denom": "cw20:terra1a04v570f9cxp49mk06vjsm8axsswndpwwt67k4", "decimals": 6, + "variant": "cosmos", "displayName": "XMARS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/xmars.svg", @@ -12696,6 +13383,7 @@ "kind": "native", "denom": "cw20:terra1vpws4hmpmpsqwnz3gljn8zj42rv7rkpc5atgt4", "decimals": 8, + "variant": "cosmos", "displayName": "DFIAT", "coingeckoId": "not-found", "icon": "not-found", @@ -12705,6 +13393,7 @@ "kind": "native", "denom": "cw20:terra1hppnw4jppmrzzga4yvd8s87y3dwkhe27xwwl5d", "decimals": 6, + "variant": "cosmos", "displayName": "CERES", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/ceres.svg", @@ -12714,6 +13403,7 @@ "kind": "native", "denom": "cw20:terra1z3e2e4jpk4n0xzzwlkgcfvc95pc5ldq0xcny58", "decimals": 8, + "variant": "cosmos", "displayName": "wasAVAX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/wasavax.svg", @@ -12723,6 +13413,7 @@ "kind": "native", "denom": "cw20:terra1zd6let0zg0xjn2sestagxv4ax24a4ml6j40qdr", "decimals": 6, + "variant": "cosmos", "displayName": "MINT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mint.svg", @@ -12732,6 +13423,7 @@ "kind": "native", "denom": "cw20:terra1ustvnmngueq0p4jd7gfnutgvdc6ujpsjhsjd02", "decimals": 8, + "variant": "cosmos", "displayName": "SD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sd.png", @@ -12741,6 +13433,7 @@ "kind": "native", "denom": "cw20:terra1ln2z938phz0nc2wepxpzfkwp6ezn9yrz9zv9ep", "decimals": 8, + "variant": "cosmos", "displayName": "xSD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/xsd.png", @@ -12750,6 +13443,7 @@ "kind": "native", "denom": "cw20:terra1uux6gwd6pzr0gfzrru5kne55cxex9d0700c72r", "decimals": 8, + "variant": "cosmos", "displayName": "PAXG", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/paxg.png", @@ -12759,6 +13453,7 @@ "kind": "native", "denom": "cw20:terra1efjugpjc50d8sha7lr8s48cr7wmsthz94eevcl", "decimals": 8, + "variant": "cosmos", "displayName": "whDAO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/whdao.png", @@ -12768,6 +13463,7 @@ "kind": "native", "denom": "cw20:terra1su6g4t4vwx7y0uh3ksancyaurj4l6w9pfs40qt", "decimals": 18, + "variant": "cosmos", "displayName": "LINK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/link.png", @@ -12777,6 +13473,7 @@ "kind": "native", "denom": "cw20:terra14v9wrjs55qsn9lkvylsqela3w2ytwxzkycqzcr", "decimals": 6, + "variant": "cosmos", "displayName": "SAYVE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sayve.png", @@ -12786,6 +13483,7 @@ "kind": "native", "denom": "cw20:terra1z55rhw0ut70jxdmpvge98mvj0rkwcz74q77z0u", "decimals": 6, + "variant": "cosmos", "displayName": "GUIDES", "coingeckoId": "not-found", "icon": "not-found", @@ -12795,6 +13493,7 @@ "kind": "native", "denom": "cw20:terra1mt2ytlrxhvd5c4d4fshxxs3zcus3fkdmuv4mk2", "decimals": 6, + "variant": "cosmos", "displayName": "BRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/bro.svg", @@ -12804,6 +13503,7 @@ "kind": "native", "denom": "cw20:terra1qryq5wreecx2wd3cdtzz94syr4z0a92l60asds", "decimals": 6, + "variant": "cosmos", "displayName": "bBRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/bbro.svg", @@ -12813,6 +13513,7 @@ "kind": "native", "denom": "cw20:terra15zvyhmv6gwddht7kt4q6w5nasn4tcpgzcdfmgr", "decimals": 18, + "variant": "cosmos", "displayName": "GTPS", "coingeckoId": "not-found", "icon": "not-found", @@ -12822,6 +13523,7 @@ "kind": "native", "denom": "cw20:terra15pkdjxv2ewjzn9x665y26pfz2h6ymak4d4e8se", "decimals": 18, + "variant": "cosmos", "displayName": "GFI", "coingeckoId": "not-found", "icon": "not-found", @@ -12831,6 +13533,7 @@ "kind": "native", "denom": "cw20:terra1fyjsxx73jrufw8ufgtuswa773dvdkny92k70wa", "decimals": 18, + "variant": "cosmos", "displayName": "ULC", "coingeckoId": "not-found", "icon": "not-found", @@ -12840,6 +13543,7 @@ "kind": "native", "denom": "cw20:terra1689ys6p6gfu0q6xrjqkzfn80sdyhurjqn0jfdl", "decimals": 6, + "variant": "cosmos", "displayName": "SST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sst.png", @@ -12849,6 +13553,7 @@ "kind": "native", "denom": "cw20:terra1rg8f993m9834afwazersesgx7jjxv4p87q9wvc", "decimals": 8, + "variant": "cosmos", "displayName": "ATLAS", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/atlas.png", @@ -12858,6 +13563,7 @@ "kind": "native", "denom": "cw20:terra1nc6flp57m5hsr6y5y8aexzszy43ksr0drdr8rp", "decimals": 8, + "variant": "cosmos", "displayName": "AUDIO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/audio.png", @@ -12867,6 +13573,7 @@ "kind": "native", "denom": "cw20:terra1hj8de24c3yqvcsv9r8chr03fzwsak3hgd8gv3m", "decimals": 8, + "variant": "cosmos", "displayName": "AVAX", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/avax.png", @@ -12876,6 +13583,7 @@ "kind": "native", "denom": "cw20:terra1apxgj5agkkfdm2tprwvykug0qtahxvfmugnhx2", "decimals": 8, + "variant": "cosmos", "displayName": "BAT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/bat.png", @@ -12885,6 +13593,7 @@ "kind": "native", "denom": "cw20:terra1skjr69exm6v8zellgjpaa2emhwutrk5a6dz7dd", "decimals": 8, + "variant": "cosmos", "displayName": "BUSDbs", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/busdbs.png", @@ -12894,6 +13603,7 @@ "kind": "native", "denom": "cw20:terra1zmclyfepfmqvfqflu8r3lv6f75trmg05z7xq95", "decimals": 8, + "variant": "cosmos", "displayName": "DAI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/dai.png", @@ -12903,6 +13613,7 @@ "kind": "native", "denom": "cw20:terra1dtqlfecglk47yplfrtwjzyagkgcqqngd5lgjp8", "decimals": 8, + "variant": "cosmos", "displayName": "MATICpo", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/maticpo.png", @@ -12912,6 +13623,7 @@ "kind": "native", "denom": "cw20:terra15a9dr3a2a2lj5fclrw35xxg9yuxg0d908wpf2y", "decimals": 8, + "variant": "cosmos", "displayName": "MIMet", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mimet.png", @@ -12921,6 +13633,7 @@ "kind": "native", "denom": "cw20:terra1ht5sepn28z999jx33sdduuxm9acthad507jg9q", "decimals": 6, + "variant": "cosmos", "displayName": "RAY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/ray.png", @@ -12930,6 +13643,7 @@ "kind": "native", "denom": "cw20:terra17h82zsq6q8x5tsgm5ugcx4gytw3axguvzt4pkc", "decimals": 6, + "variant": "cosmos", "displayName": "SBR", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sbr.png", @@ -12939,6 +13653,7 @@ "kind": "native", "denom": "cw20:terra1huku2lecfjhq9d00k5a8dh73gw7dwe6vvuf2dd", "decimals": 8, + "variant": "cosmos", "displayName": "SHIB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/shib.png", @@ -12948,6 +13663,7 @@ "kind": "native", "denom": "cw20:terra1dkam9wd5yvaswv4yq3n2aqd4wm5j8n82qc0c7c", "decimals": 6, + "variant": "cosmos", "displayName": "SRMso", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/srmso.png", @@ -12957,6 +13673,7 @@ "kind": "native", "denom": "cw20:terra1pvel56a2hs93yd429pzv9zp5aptcjg5ulhkz7w", "decimals": 6, + "variant": "cosmos", "displayName": "USDCav", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdcav.png", @@ -12966,6 +13683,7 @@ "kind": "native", "denom": "cw20:terra1yljlrxvkar0c6ujpvf8g57m5rpcwl7r032zyvu", "decimals": 8, + "variant": "cosmos", "displayName": "USDCbs", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdcbs.png", @@ -12975,6 +13693,7 @@ "kind": "native", "denom": "cw20:terra1kkyyh7vganlpkj0gkc2rfmhy858ma4rtwywe3x", "decimals": 6, + "variant": "cosmos", "displayName": "USDCpo", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdcpo.png", @@ -12984,6 +13703,7 @@ "kind": "native", "denom": "cw20:terra1e6mq63y64zcxz8xyu5van4tgkhemj3r86yvgu4", "decimals": 6, + "variant": "cosmos", "displayName": "USDCso", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdcso.png", @@ -12993,6 +13713,7 @@ "kind": "native", "denom": "cw20:terra1eqvq3thjhye7anv6f6mhxpjhyvww8zjvqcdgjx", "decimals": 6, + "variant": "cosmos", "displayName": "USDTav", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdtav.png", @@ -13002,6 +13723,7 @@ "kind": "native", "denom": "cw20:terra1vlqeghv5mt5udh96kt5zxlh2wkh8q4kewkr0dd", "decimals": 8, + "variant": "cosmos", "displayName": "USDTbs", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdtbs.png", @@ -13011,6 +13733,7 @@ "kind": "native", "denom": "cw20:terra1hd9n65snaluvf7en0p4hqzse9eqecejz2k8rl5", "decimals": 6, + "variant": "cosmos", "displayName": "USDTso", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/usdtso.png", @@ -13020,6 +13743,7 @@ "kind": "native", "denom": "cw20:terra1fpfn2kkr8mv390wx4dtpfk3vkjx9ch3thvykl3", "decimals": 8, + "variant": "cosmos", "displayName": "gOHM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/gohm.png", @@ -13029,6 +13753,7 @@ "kind": "native", "denom": "cw20:terra1qvlpf2v0zmru3gtex40sqq02wxp39x3cjh359y", "decimals": 8, + "variant": "cosmos", "displayName": "mSOL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/msol.png", @@ -13038,6 +13763,7 @@ "kind": "native", "denom": "cw20:terra1w7ywr6waxtjuvn5svk5wqydqpjj0q9ps7qct4d", "decimals": 8, + "variant": "cosmos", "displayName": "stETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/steth.png", @@ -13047,6 +13773,7 @@ "kind": "native", "denom": "cw20:terra1srp2u95kxps35nvan88gn96nfqhukqya2d0ffc", "decimals": 6, + "variant": "cosmos", "displayName": "LCT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/lct.png", @@ -13056,6 +13783,7 @@ "kind": "native", "denom": "cw20:terra18zqcnl83z98tf6lly37gghm7238k7lh79u4z9a", "decimals": 6, + "variant": "cosmos", "displayName": "bATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/batom.svg", @@ -13065,6 +13793,7 @@ "kind": "native", "denom": "cw20:terra128pe5jpempxu0nws5lw28se9zknhsr78626cpn", "decimals": 6, + "variant": "cosmos", "displayName": "webATOM", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/webatom.svg", @@ -13074,6 +13803,7 @@ "kind": "native", "denom": "cw20:terra1laczhlpxlgmrwr9un9ds74qxd2fj4754nf82dn", "decimals": 6, + "variant": "cosmos", "displayName": "WCOIN", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/wcoin.svg", @@ -13083,6 +13813,7 @@ "kind": "native", "denom": "cw20:terra1thhm2u93m2stytzynhsxh5h3jrtg540x4punqy", "decimals": 6, + "variant": "cosmos", "displayName": "LCTfancard", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/lctfancard.png", @@ -13092,6 +13823,7 @@ "kind": "native", "denom": "cw20:terra1yeyr6taynkwdl85ppaggr3zr8txhf66cny2ang", "decimals": 6, + "variant": "cosmos", "displayName": "KNTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/kntc.svg", @@ -13101,6 +13833,7 @@ "kind": "native", "denom": "cw20:terra1g53pyke8jtmt4lwvk4yl0xaqc4u0qlsl8dz3ex", "decimals": 6, + "variant": "cosmos", "displayName": "kUST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/kust.svg", @@ -13110,6 +13843,7 @@ "kind": "native", "denom": "cw20:terra1rl4zyexjphwgx6v3ytyljkkc4mrje2pyznaclv", "decimals": 6, + "variant": "cosmos", "displayName": "STEAK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/steak.svg", @@ -13119,6 +13853,7 @@ "kind": "native", "denom": "cw20:terra1jkkt5638cd5pur0u5jnr2juw0v6hz5d6z8xu8m", "decimals": 6, + "variant": "cosmos", "displayName": "CST", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cst.png", @@ -13128,6 +13863,7 @@ "kind": "native", "denom": "cw20:terra1kz7qszu7p4dg9lts7m9m7lpuarsnan47jh3fam", "decimals": 6, + "variant": "cosmos", "displayName": "CSTfancard", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cstfancard.png", @@ -13137,6 +13873,7 @@ "kind": "native", "denom": "cw20:terra1amz5c45l34n7w8m5a3z7rd7u0k037x4nnsemwj", "decimals": 9, + "variant": "cosmos", "displayName": "NWLD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/nwld.png", @@ -13146,6 +13883,7 @@ "kind": "native", "denom": "cw20:terra1cdc6nlsx0l6jmt3nnx7gxjggf902wge3n2z76k", "decimals": 6, + "variant": "cosmos", "displayName": "FURY", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/fury.png", @@ -13155,6 +13893,7 @@ "kind": "native", "denom": "cw20:terra17n223dxpkypc5c48la7aqjvverczg82ga3cr93", "decimals": 6, + "variant": "cosmos", "displayName": "RCT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/rct.svg", @@ -13164,6 +13903,7 @@ "kind": "native", "denom": "cw20:terra14vw4sfqwe7jw8ppcc7u44vq7hy9qa2nlstnxmu", "decimals": 6, + "variant": "cosmos", "displayName": "VITC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/vitc.png", @@ -13173,6 +13913,7 @@ "kind": "native", "denom": "cw20:terra1948uvsah8aw40dhsa9mhl3htq8lraj0smlh77g", "decimals": 6, + "variant": "cosmos", "displayName": "SB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sb.png", @@ -13182,6 +13923,7 @@ "kind": "native", "denom": "cw20:terra1rz964297kvt86rteajhtp4hsffhcum0ye8eljh", "decimals": 6, + "variant": "cosmos", "displayName": "TOAD", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/toad.svg", @@ -13191,6 +13933,7 @@ "kind": "native", "denom": "cw20:terra1yhlhrea3rgyx2xdnsswsfakn28qa8z7yp5gmhd", "decimals": 6, + "variant": "cosmos", "displayName": "orionASTRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/orionastro.png", @@ -13200,6 +13943,7 @@ "kind": "native", "denom": "cw20:terra1j4hwavavmtsafa6zr0npalfz3tk9gf3p4787mp", "decimals": 6, + "variant": "cosmos", "displayName": "RETRO", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/retro.png", @@ -13209,6 +13953,7 @@ "kind": "native", "denom": "cw20:terra16wggm67a34msdxasg2vergm2pt289y7930wv7d", "decimals": 6, + "variant": "cosmos", "displayName": "TND", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tnd.png", @@ -13218,6 +13963,7 @@ "kind": "native", "denom": "cw20:terra1n9k2he20h5vpyn4mgv7pg4pzvw2n3wc4a86v3g", "decimals": 6, + "variant": "cosmos", "displayName": "sTND", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/stnd.svg", @@ -13227,6 +13973,7 @@ "kind": "native", "denom": "cw20:terra1ezz5xply2v3xdyv32gy5tcd7zq4k235q4xtzwe", "decimals": 9, + "variant": "cosmos", "displayName": "NWT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/nwt.png", @@ -13236,6 +13983,7 @@ "kind": "native", "denom": "uaud", "decimals": 6, + "variant": "cosmos", "displayName": "AUTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/aut.svg", @@ -13245,6 +13993,7 @@ "kind": "native", "denom": "ucad", "decimals": 6, + "variant": "cosmos", "displayName": "CATC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cat.svg", @@ -13254,6 +14003,7 @@ "kind": "native", "denom": "uchf", "decimals": 6, + "variant": "cosmos", "displayName": "CHTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cht.svg", @@ -13263,6 +14013,7 @@ "kind": "native", "denom": "ucny", "decimals": 6, + "variant": "cosmos", "displayName": "CNTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/cnt.svg", @@ -13272,6 +14023,7 @@ "kind": "native", "denom": "udkk", "decimals": 6, + "variant": "cosmos", "displayName": "DKTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/dkt.svg", @@ -13281,6 +14033,7 @@ "kind": "native", "denom": "ueur", "decimals": 6, + "variant": "cosmos", "displayName": "EUTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/eut.svg", @@ -13290,6 +14043,7 @@ "kind": "native", "denom": "ugbp", "decimals": 6, + "variant": "cosmos", "displayName": "GBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/gbt.svg", @@ -13299,6 +14053,7 @@ "kind": "native", "denom": "uhkd", "decimals": 6, + "variant": "cosmos", "displayName": "HKTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/hkt.svg", @@ -13308,6 +14063,7 @@ "kind": "native", "denom": "uidr", "decimals": 6, + "variant": "cosmos", "displayName": "IDTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/idt.svg", @@ -13317,6 +14073,7 @@ "kind": "native", "denom": "uinr", "decimals": 6, + "variant": "cosmos", "displayName": "INTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/int.svg", @@ -13326,6 +14083,7 @@ "kind": "native", "denom": "ujpy", "decimals": 6, + "variant": "cosmos", "displayName": "JPTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/jpt.svg", @@ -13335,6 +14093,7 @@ "kind": "native", "denom": "umnt", "decimals": 6, + "variant": "cosmos", "displayName": "MNTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/mnt.svg", @@ -13344,6 +14103,7 @@ "kind": "native", "denom": "umyr", "decimals": 6, + "variant": "cosmos", "displayName": "MYTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/myt.svg", @@ -13353,6 +14113,7 @@ "kind": "native", "denom": "unok", "decimals": 6, + "variant": "cosmos", "displayName": "NOTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/not.svg", @@ -13362,6 +14123,7 @@ "kind": "native", "denom": "uphp", "decimals": 6, + "variant": "cosmos", "displayName": "PHTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/pht.svg", @@ -13371,6 +14133,7 @@ "kind": "native", "denom": "usdr", "decimals": 6, + "variant": "cosmos", "displayName": "SDTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sdt.svg", @@ -13380,6 +14143,7 @@ "kind": "native", "denom": "usek", "decimals": 6, + "variant": "cosmos", "displayName": "SETC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/set.svg", @@ -13389,6 +14153,7 @@ "kind": "native", "denom": "usgd", "decimals": 6, + "variant": "cosmos", "displayName": "SGTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/sgt.svg", @@ -13398,6 +14163,7 @@ "kind": "native", "denom": "uthb", "decimals": 6, + "variant": "cosmos", "displayName": "THTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/tht.svg", @@ -13407,6 +14173,7 @@ "kind": "native", "denom": "utwd", "decimals": 6, + "variant": "cosmos", "displayName": "TWTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/twt.svg", @@ -13416,6 +14183,7 @@ "kind": "native", "denom": "cw20:terra1mpq5zkkm39nmjrjg9raknpfrfmcfwv0nh0whvn", "decimals": 6, + "variant": "cosmos", "displayName": "NEB", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/neb.png", @@ -13425,6 +14193,7 @@ "kind": "native", "denom": "cw20:terra1g6fm3yu79gv0rc8067n2nnfpf0vks6n0wpzaf4u7w48tdrmj98zsy7uu00", "decimals": 6, + "variant": "cosmos", "displayName": "TRIT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/terra/images/trit.png", @@ -13463,6 +14232,7 @@ "kind": "native", "denom": "utgd", "decimals": 6, + "variant": "cosmos", "displayName": "TGD", "coingeckoId": "tgrade", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/tgrade/images/tgrade-symbol-gradient.svg", @@ -13528,6 +14298,7 @@ "kind": "native", "denom": "uulas", "decimals": 6, + "variant": "cosmos", "displayName": "ULAS", "coingeckoId": "ulas", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/ulastestnet/images/logo.png", @@ -13565,6 +14336,7 @@ "kind": "native", "denom": "uumee", "decimals": 6, + "variant": "cosmos", "displayName": "UMEE", "coingeckoId": "umee", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/umee/images/umee.svg", @@ -13603,6 +14375,7 @@ "kind": "native", "denom": "nund", "decimals": 9, + "variant": "cosmos", "displayName": "FUND", "coingeckoId": "unification", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/unification/images/fund.svg", @@ -13641,6 +14414,7 @@ "kind": "native", "denom": "muno", "decimals": 6, + "variant": "cosmos", "displayName": "UNION", "coingeckoId": "unionlabs", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/union/images/union.png", @@ -13679,6 +14453,7 @@ "kind": "native", "denom": "uguu", "decimals": 6, + "variant": "cosmos", "displayName": "GUU", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/ununifi/images/ununifi.svg", @@ -13717,6 +14492,7 @@ "kind": "native", "denom": "auptick", "decimals": 18, + "variant": "cosmos", "displayName": "UPTICK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/uptick/images/uptick.svg", @@ -13755,6 +14531,7 @@ "kind": "native", "denom": "auptick", "decimals": 18, + "variant": "cosmos", "displayName": "UPTICK", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/upticktestnet/images/uptick.png", @@ -13792,6 +14569,7 @@ "kind": "native", "denom": "uvdl", "decimals": 6, + "variant": "cosmos", "displayName": "VDL", "coingeckoId": "vidulum", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/vidulum/images/vdl.svg", @@ -13830,6 +14608,7 @@ "kind": "native", "denom": "avce", "decimals": 18, + "variant": "cosmos", "displayName": "VCE", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/vincechaintestnet/images/vince.png", @@ -13868,6 +14647,7 @@ "kind": "native", "denom": "utest", "decimals": 6, + "variant": "cosmos", "displayName": "TEST", "coingeckoId": "not-found", "icon": "not-found", @@ -13877,6 +14657,7 @@ "kind": "native", "denom": "uworm", "decimals": 6, + "variant": "cosmos", "displayName": "WORM", "coingeckoId": "not-found", "icon": "not-found", @@ -13886,6 +14667,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/46YEtoSN1AcwgGSRoWruoS6bnVh8XpMp5aQTpKohCJYh", "decimals": 8, + "variant": "cosmos", "displayName": "SUI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/sui/images/sui.svg", @@ -13895,6 +14677,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/5BWqpR48Lubd55szM5i62zK7TFkddckhbT48yy6mNbDp", "decimals": 8, + "variant": "cosmos", "displayName": "WETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/eth-white.svg", @@ -13904,6 +14687,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/5TSQTEhJ5Q6r1YqCmCxTRSPiV2pGx5rZUQf6g2XH4e1b", "decimals": 8, + "variant": "cosmos", "displayName": "wstETH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/wsteth.svg", @@ -13913,6 +14697,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/5wS2fGojbL9RhGEAeQBdkHPUAciYDxjDTMYvdf9aDn2r", "decimals": 8, + "variant": "cosmos", "displayName": "APT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/aptos/images/aptos.svg", @@ -13922,6 +14707,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/8iuAc6DSeLvi2JDUtwJxLytsZT8R19itXebZsNReLLNi", "decimals": 6, + "variant": "cosmos", "displayName": "USDT", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdt.svg", @@ -13931,6 +14717,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/8sYgCzLRJC3J7qPn2bNbx6PiGcarhyx8rBhVaNnfvHCA", "decimals": 8, + "variant": "cosmos", "displayName": "SOL", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/solana/images/sol.svg", @@ -13940,6 +14727,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/95mnwzvJZJ3fKz77xfGN2nR5to9pZmH8YNvaxgLgw5AR", "decimals": 5, + "variant": "cosmos", "displayName": "Bonk", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/solana/images/bonk.png", @@ -13949,6 +14737,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/BGkuAcga2WArUghF8L6kt6uCAhAzrxmn1QcbQqi5r5bd", "decimals": 8, + "variant": "cosmos", "displayName": "WBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/wbtc.svg", @@ -13958,6 +14747,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/BhqTYfQogyt7jX7cx7x8uHEQP1x9fdtdBZtK4Swghgpw", "decimals": 8, + "variant": "cosmos", "displayName": "tBTC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/tbtc.svg", @@ -13967,6 +14757,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/EKiMEqDnPKokFGcSXDvGMk6Gt1BJ6BC7BDZzTmEpWLH1", "decimals": 8, + "variant": "cosmos", "displayName": "DAI", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/dai.svg", @@ -13976,6 +14767,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/GGh9Ufn1SeDGrhzEkMyRKt5568VbbxZK2yvWNsd6PbXt", "decimals": 6, + "variant": "cosmos", "displayName": "USDC", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdc.svg", @@ -13985,6 +14777,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/5ZLmAZpcbaP4EGyihSmpfwryzDr84h51tboV392BCjW4", "decimals": 6, + "variant": "cosmos", "displayName": "avalanche.USDC.wh", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/ethereum/images/usdc.svg", @@ -13994,6 +14787,7 @@ "kind": "native", "denom": "factory/wormhole14ejqjyq8um4p3xfqj74yld5waqljf88fz25yxnma0cngspxe3les00fpjx/B8ohBnfisop27exk2gtNABJyYjLwQA7ogrp5uNzvZCoy", "decimals": 6, + "variant": "cosmos", "displayName": "PYTH", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/solana/images/pyth.svg", @@ -14032,6 +14826,7 @@ "kind": "native", "denom": "uxion", "decimals": 6, + "variant": "cosmos", "displayName": "XION", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/xion/images/burnt-round.png", @@ -14070,6 +14865,7 @@ "kind": "native", "denom": "axpla", "decimals": 18, + "variant": "cosmos", "displayName": "XPLA", "coingeckoId": "xpla", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/xpla/images/xpla.svg", @@ -14108,6 +14904,7 @@ "kind": "native", "denom": "azeta", "decimals": 18, + "variant": "cosmos", "displayName": "tZETA", "coingeckoId": "not-found", "icon": "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/zetachaintestnet/images/zetachaintestnet.svg", diff --git a/package.json b/package.json index e12cfd01ea..602ff8f505 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,10 @@ "build:web": "expo export -p web", "lint": "npx tsx packages/scripts/paralint", "lint-fix": "eslint --cache --ext .js,.jsx,.ts,.tsx . --fix", - "unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport=\"packages/api;packages/contracts-clients;packages/evm-contracts-clients;packages/screens/RiotGame/RiotGameBridgeScreen.tsx;packages/components/socialFeed/RichText/inline-toolbar;app.config.js;weshd;./App.tsx;packages/modules/FileSystem/index.ts;.*\\.web|.electron|.native|.d.ts\"", + "unused-exports": "ts-unused-exports ./tsconfig.json --excludePathsFromReport=\"packages/api;packages/contracts-clients;packages/evm-contracts-clients;packages/screens/RiotGame/RiotGameBridgeScreen.tsx;packages/components/socialFeed/RichText/inline-toolbar;app.config.js;weshd;./App.tsx;./cypress.config.ts;packages/modules/FileSystem/index.ts;.*\\.web|.electron|.native|.d.ts\"", "validate-networks": "tsx packages/scripts/validateNetworks.ts", - "postinstall": "patch-package" + "postinstall": "patch-package", + "install-gno": "tsx packages/scripts/install-gno" }, "engines": { "node": "v18.1.0" @@ -25,11 +26,12 @@ "@chain-registry/utils": "1.18.0", "@cosmjs/amino": "0.32.2", "@cosmjs/cosmwasm-stargate": "0.32.2", - "@cosmjs/encoding": "^0.32.2", - "@cosmjs/math": "^0.32.2", + "@cosmjs/crypto": "0.32.2", + "@cosmjs/encoding": "0.32.2", + "@cosmjs/math": "0.32.2", "@cosmjs/proto-signing": "0.32.2", "@cosmjs/stargate": "0.32.2", - "@cosmjs/tendermint-rpc": "^0.32.2", + "@cosmjs/tendermint-rpc": "0.32.2", "@cosmology/core": "2.0.1", "@dotlottie/react-player": "^1.6.5", "@draft-js-plugins/anchor": "^4.2.0", @@ -44,6 +46,8 @@ "@expo-google-fonts/exo": "^0.2.2", "@expo/metro-runtime": "~3.1.3", "@gnolang/gno-js-client": "^1.3.0", + "@gnolang/tm2-js-client": "^1.2.1", + "@hookform/resolvers": "^3.6.0", "@improbable-eng/grpc-web": "^0.15.0", "@improbable-eng/grpc-web-node-http-transport": "^0.15.0", "@improbable-eng/grpc-web-react-native-transport": "^0.15.0", @@ -59,6 +63,7 @@ "@react-native-clipboard/clipboard": "^1.13.2", "@react-native-community/slider": "4.4.2", "@react-native-masked-view/masked-view": "0.3.0", + "@react-native-picker/picker": "2.6.1", "@react-navigation/bottom-tabs": "^6.5.11", "@react-navigation/drawer": "^6.6.6", "@react-navigation/native": "^6.0.11", @@ -128,6 +133,7 @@ "react-native-gesture-handler": "~2.14.0", "react-native-get-random-values": "~1.8.0", "react-native-heroicons": "^3.2.0", + "react-native-hoverable": "^0.2.0", "react-native-image-picker": "^7.1.0", "react-native-keyboard-aware-scrollview": "^2.1.0", "react-native-leaflet-view": "^0.1.2", @@ -139,6 +145,7 @@ "react-native-qrcode-svg": "^6.2.0", "react-native-reanimated": "^3.6.2", "react-native-reanimated-carousel": "4.0.0-alpha.9", + "react-native-reanimated-table": "^0.0.2", "react-native-redash": "^18.0.0", "react-native-safe-area-context": "4.8.2", "react-native-screens": "~3.29.0", @@ -162,7 +169,8 @@ "victory-native": "^36.6.8", "waveform-data": "^4.3.0", "yaml": "^2.3.4", - "zod": "^3.22.4" + "zod": "^3.22.4", + "zustand": "^4.4.7" }, "devDependencies": { "@axelar-network/axelarjs-types": "^0.33.0", @@ -175,6 +183,7 @@ "@babel/plugin-transform-shorthand-properties": "^7.23.3", "@babel/plugin-transform-template-literals": "^7.23.3", "@cosmwasm/ts-codegen": "^0.35.7", + "@faker-js/faker": "^8.4.1", "@types/draft-convert": "^2.1.4", "@types/draft-js": "^0.11.9", "@types/html-to-draftjs": "^1.4.0", @@ -187,6 +196,7 @@ "babel-plugin-react-inline-svg-unique-id": "^1.3.1", "cors-anywhere": "^0.4.4", "cross-env": "^7.0.3", + "cypress": "^13.9.0", "depcheck": "^1.4.7", "dotenv": "^16.3.1", "electron": "^27.1.2", diff --git a/packages/components/FlexRow.tsx b/packages/components/FlexRow.tsx index 9fb0adfcfd..1891c6612c 100644 --- a/packages/components/FlexRow.tsx +++ b/packages/components/FlexRow.tsx @@ -16,7 +16,7 @@ type Direction = | "column-reverse" | undefined; -const FlexRow: React.FC = ({ +export const FlexRow: React.FC = ({ breakpoint, width = "100%", alignItems = "center", diff --git a/packages/components/ProgressLine.tsx b/packages/components/ProgressLine.tsx index 87f2d77027..6bad62b582 100644 --- a/packages/components/ProgressLine.tsx +++ b/packages/components/ProgressLine.tsx @@ -1,22 +1,23 @@ import { LinearGradient } from "expo-linear-gradient"; -import React from "react"; -import { View, ViewStyle } from "react-native"; +import { FC, memo } from "react"; +import { StyleProp, View, ViewStyle } from "react-native"; import { neutral33 } from "../utils/style/colors"; -const DEFAULT_WIDTH = 200; - -interface ProgressLineProps { - percent: number; - width?: number; - style?: ViewStyle; -} - -export const ProgressLine: React.FC = ({ - percent, - width = DEFAULT_WIDTH, - style, -}) => { +/** + * @param gain - The gain of the progress line. Must be between 0 and 1 inclusive. + * @param style - The style of the container View. + */ +export const ProgressLine: FC<{ + gain: number; + style?: StyleProp; +}> = memo(({ gain, style }) => { + if (gain < 0) { + gain = 0; + } + if (gain > 1) { + gain = 1; + } return ( = ({ height: 4, borderRadius: 4, backgroundColor: neutral33, - width, + flexGrow: 1, position: "relative", }, style, @@ -36,7 +37,7 @@ export const ProgressLine: React.FC = ({ locations={[0, 0.5, 1]} colors={["#5433FF", "#20BDFF", "#A5FECB"]} style={{ - width: (percent / 100) * width, + width: `${gain * 100}%`, height: 4, position: "absolute", top: 0, @@ -46,4 +47,4 @@ export const ProgressLine: React.FC = ({ /> ); -}; +}); diff --git a/packages/components/Search/SearchBarInput.tsx b/packages/components/Search/SearchBarInput.tsx index f49f613ea8..d6d1d8f465 100644 --- a/packages/components/Search/SearchBarInput.tsx +++ b/packages/components/Search/SearchBarInput.tsx @@ -1,5 +1,5 @@ import React, { useRef } from "react"; -import { StyleProp, TextInput, StyleSheet } from "react-native"; +import { StyleProp, TextInput, StyleSheet, ViewStyle } from "react-native"; import { useSelector } from "react-redux"; import searchSVG from "../../../assets/icons/search.svg"; @@ -32,8 +32,19 @@ export const SearchBarInput: React.FC<{ text: string; onChangeText: (text: string) => void; onInteraction?: () => void; + placeholder?: string; style?: StyleProp; -}> = ({ onInteraction, text, onChangeText, style }) => { + inputStyle?: StyleProp; + noBrokenCorners?: boolean; +}> = ({ + onInteraction, + text, + onChangeText, + style, + placeholder, + inputStyle, + noBrokenCorners = false, +}) => { const ref = useRef(null); const fullWidth = StyleSheet.flatten(style)?.width === "100%"; return ( @@ -52,6 +63,7 @@ export const SearchBarInput: React.FC<{ > { ); }; -const SearchResultsSection: React.FC<{ +export const SearchResultsSection: React.FC<{ title: string; style: StyleProp; isFirstSection?: boolean; diff --git a/packages/components/TopMenu/TopMenuAccount.tsx b/packages/components/TopMenu/TopMenuAccount.tsx index bf0398a5fa..0ddf6f9d1c 100644 --- a/packages/components/TopMenu/TopMenuAccount.tsx +++ b/packages/components/TopMenu/TopMenuAccount.tsx @@ -1,4 +1,4 @@ -import { TextStyle, ViewStyle } from "react-native"; +import { TextStyle, View, ViewStyle } from "react-native"; import chevronRightSVG from "../../../assets/icons/chevron-right.svg"; import useSelectedWallet from "../../hooks/useSelectedWallet"; @@ -10,17 +10,16 @@ import FlexCol from "../FlexCol"; import FlexRow from "../FlexRow"; import { OmniLink } from "../OmniLink"; import { SVG } from "../SVG"; -import { UserNameInline } from "../UserNameInline"; +import { UsernameWithAvatar } from "../user/UsernameWithAvatar"; export const TopMenuAccount: React.FC = () => { const selectedWallet = useSelectedWallet(); return ( - + + + diff --git a/packages/components/TopMenu/WalletView.tsx b/packages/components/TopMenu/WalletView.tsx index e3af59400b..bef60539a3 100644 --- a/packages/components/TopMenu/WalletView.tsx +++ b/packages/components/TopMenu/WalletView.tsx @@ -22,6 +22,7 @@ export const WalletView: React.FC<{ {tinyAddress(userInfo?.metadata?.tokenId || wallet?.address || "", 16)} diff --git a/packages/components/UserNameInline.tsx b/packages/components/UserNameInline.tsx deleted file mode 100644 index 7b8888cdd2..0000000000 --- a/packages/components/UserNameInline.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from "react"; -import { StyleProp, TextStyle } from "react-native"; - -import { BrandText } from "./BrandText"; -import FlexRow from "./FlexRow"; -import { OmniLink } from "./OmniLink"; -import { UserAvatarWithFrame } from "./images/AvatarWithFrame"; -import { useNSUserInfo } from "../hooks/useNSUserInfo"; -import { parseUserId } from "../networks"; -import { fontSemibold14 } from "../utils/style/fonts"; -import { layout } from "../utils/style/layout"; -import { tinyAddress } from "../utils/text"; - -type PlayerNameProps = { - userId: string | undefined; - multisignWalletAddres?: string | null; - style?: StyleProp; -}; - -export const UserNameInline: React.FC = ({ - userId, - style, -}) => { - const [, userAddress] = parseUserId(userId); - const userInfo = useNSUserInfo(userId); - const name = - userInfo?.metadata?.tokenId || tinyAddress(userAddress, 30) || ""; - - return ( - - - - - {name} - - - - ); -}; diff --git a/packages/components/WalletProviderIcon.tsx b/packages/components/WalletProviderIcon.tsx index 69899e06a3..b48dba7a7f 100644 --- a/packages/components/WalletProviderIcon.tsx +++ b/packages/components/WalletProviderIcon.tsx @@ -6,6 +6,7 @@ import adenaSVG from "../../assets/icons/adena.svg"; import keplrSVG from "../../assets/icons/keplr.svg"; import leapSVG from "../../assets/icons/leap-cosmos-logo.svg"; import metamaskSVG from "../../assets/icons/metamask.svg"; +import warningSVG from "../../assets/icons/warning.svg"; import { WalletProvider } from "../utils/walletProvider"; export const WalletProviderIcon: React.FC<{ @@ -21,6 +22,8 @@ export const WalletProviderIcon: React.FC<{ return ; case WalletProvider.Adena: return ; + case WalletProvider.Gnotest: + return ; default: return ; } diff --git a/packages/components/buttons/IconButton.tsx b/packages/components/buttons/IconButton.tsx index e900b86c77..84c1b232d4 100644 --- a/packages/components/buttons/IconButton.tsx +++ b/packages/components/buttons/IconButton.tsx @@ -55,6 +55,7 @@ export const IconButton: React.FC<{ borderRadius: borderRadiusButton(size), backgroundColor, paddingHorizontal: 20, + alignItems: "center", }, ]} > diff --git a/packages/components/buttons/PrimaryButton.tsx b/packages/components/buttons/PrimaryButton.tsx index 276751b903..a3132cb882 100644 --- a/packages/components/buttons/PrimaryButton.tsx +++ b/packages/components/buttons/PrimaryButton.tsx @@ -35,6 +35,7 @@ export const PrimaryButton: React.FC<{ RightComponent?: React.FC; color?: string; isLoading?: boolean; + testID?: string; }> = ({ // If no width, the buttons will fit the content including paddingHorizontal 20 width, @@ -51,6 +52,7 @@ export const PrimaryButton: React.FC<{ iconColor, color = primaryColor, isLoading: isLoadingProp, + testID, }) => { const [isLocalLoading, setIsLocalLoading] = useState(false); const [hovered, setHovered] = useState(false); @@ -89,6 +91,7 @@ export const PrimaryButton: React.FC<{ onHoverOut={() => { setHovered(false); }} + testID={testID} > ; + style?: StyleProp; loading?: boolean; disabled?: boolean; iconSVG?: React.FC; outline?: boolean; + testID?: string | undefined; } export const SimpleButton: React.FC = ({ @@ -38,27 +39,33 @@ export const SimpleButton: React.FC = ({ style, iconSVG = null, outline = false, + testID, }) => { let padH: number; let padV: number; + let radius: number; switch (size) { case "XL": padH = layout.spacing_x3; padV = layout.spacing_x2_5; + radius = layout.spacing_x1_5; break; case "SM": padH = layout.spacing_x2; padV = layout.spacing_x1_5; + radius = layout.spacing_x0_75; break; case "XS": padH = layout.spacing_x1_5; padV = layout.spacing_x0_5; + radius = layout.spacing_x0_5; break; case "M": default: padH = layout.spacing_x2_5; padV = layout.spacing_x2; + radius = layout.spacing_x1; break; } @@ -67,15 +74,20 @@ export const SimpleButton: React.FC = ({ disabled={disabled} style={[containerStyle, (loading || disabled) && { opacity: 0.6 }]} onPress={() => !loading && onPress?.()} + testID={testID} > = ({ ); }; - -// FIXME: remove StyleSheet.create -// eslint-disable-next-line no-restricted-syntax -const styles = StyleSheet.create({ - btnStyle: { - alignSelf: "center", - borderRadius: layout.spacing_x1_5, - ...(fontSemibold14 as object), - }, -}); diff --git a/packages/components/buttons/SocialButton.tsx b/packages/components/buttons/SocialButton.tsx index 4e0931729a..23ca47e682 100644 --- a/packages/components/buttons/SocialButton.tsx +++ b/packages/components/buttons/SocialButton.tsx @@ -1,12 +1,18 @@ import React from "react"; -import { ViewStyle, StyleProp, TouchableOpacity } from "react-native"; +import { + ViewStyle, + StyleProp, + TouchableOpacity, + View, + Pressable, + ColorValue, +} from "react-native"; import { SvgProps } from "react-native-svg"; -import { BrandText } from "../BrandText"; -import { SVG } from "../SVG"; -import { Box } from "../boxes/Box"; - -import { neutral22, neutral33, withAlpha } from "@/utils/style/colors"; +import { BrandText } from "@/components/BrandText"; +import { SVG } from "@/components/SVG"; +import { Box } from "@/components/boxes/Box"; +import { withAlpha, neutral22, neutral33 } from "@/utils/style/colors"; import { fontMedium14 } from "@/utils/style/fonts"; export const iconSize = 32; @@ -14,12 +20,78 @@ export const iconPadding = 12; export const outerPadding = 6; export const innerGap = 8; +const IconWithText: React.FC<{ + text?: string; + iconSvg: React.FC; + textColor?: ColorValue; + iconColor?: ColorValue; +}> = ({ text, iconSvg, textColor, iconColor }) => { + return ( + + + + + {!!text && ( + + {text} + + )} + + ); +}; + +export const IconWithTextButton: React.FC<{ + text?: string; + iconSvg: React.FC; + textColor?: ColorValue; + onPress?: () => void; + style?: StyleProp; +}> = ({ text, iconSvg, style, onPress, textColor }) => { + const [hovered, setHovered] = React.useState(false); + return ( + setHovered(true)} + onHoverOut={() => setHovered(false)} + > + + + ); +}; + export const SocialButton: React.FC<{ text?: string; iconSvg: React.FC; + textColor?: ColorValue; + iconColor?: ColorValue; onPress?: () => void; style?: StyleProp; -}> = ({ text, onPress, iconSvg, style }) => { +}> = ({ text, onPress, iconSvg, style, textColor, iconColor }) => { return ( - - - - {!!text && ( - - {text} - - )} + ); diff --git a/packages/components/connectWallet/ConnectAdenaButton.tsx b/packages/components/connectWallet/ConnectAdenaButton.tsx index 153ad7f12c..d02ff246c8 100644 --- a/packages/components/connectWallet/ConnectAdenaButton.tsx +++ b/packages/components/connectWallet/ConnectAdenaButton.tsx @@ -31,6 +31,7 @@ export const ConnectAdenaButton: React.FC<{ const address = account.data.address; const chainId = account.data.chainId; const network = getGnoNetworkFromChainId(chainId); + if (!network) { throw new Error(`Unsupported chainId ${chainId}`); } diff --git a/packages/components/hub/AssetRatioByChain.tsx b/packages/components/hub/AssetRatioByChain.tsx index 8b353ec18d..d64033a46e 100644 --- a/packages/components/hub/AssetRatioByChain.tsx +++ b/packages/components/hub/AssetRatioByChain.tsx @@ -43,8 +43,10 @@ const ListItem: React.FC = ({ title, icon, percent }) => { {title} { description="Get funds to develop, contribute and build new feature for Communities" info="Apply here" iconSVG={labsSVG} - onPress={() => Linking.openURL("https://teritori.com/grants")} + onPress={() => Linking.openURL("https://teritori.com/projects")} /> diff --git a/packages/components/images/RoundedGradientImage.tsx b/packages/components/images/RoundedGradientImage.tsx index 25ff66fe1b..1c55d0d964 100644 --- a/packages/components/images/RoundedGradientImage.tsx +++ b/packages/components/images/RoundedGradientImage.tsx @@ -4,12 +4,16 @@ import { View, ViewStyle, StyleProp } from "react-native"; import { OptimizedImage } from "../OptimizedImage"; -type RoundedGradientImageSize = "M" | "XS" | "XXS"; +type RoundedGradientImageSize = "L" | "M" | "S" | "XS" | "XXS"; const dimension = (size: RoundedGradientImageSize) => { switch (size) { + case "L": + return 236; case "M": return 140; + case "S": + return 56; case "XS": return 32; case "XXS": @@ -18,8 +22,12 @@ const dimension = (size: RoundedGradientImageSize) => { }; const imageDimension = (size: RoundedGradientImageSize) => { switch (size) { + case "L": + return dimension(size) - 4; case "M": return dimension(size) - 4; + case "S": + return dimension(size) - 2; case "XS": return dimension(size) - 2; case "XXS": @@ -32,7 +40,8 @@ export const RoundedGradientImage: React.FC<{ fallbackURI?: string | null | undefined; size?: RoundedGradientImageSize; style?: StyleProp; -}> = ({ sourceURI, fallbackURI, size = "M", style }) => { + square?: boolean; +}> = ({ sourceURI, fallbackURI, size = "M", style, square = false }) => { return ( @@ -75,7 +88,7 @@ export const RoundedGradientImage: React.FC<{ position: "absolute", width: dimension(size), height: dimension(size), - borderRadius: 999, + borderRadius: square ? 12 : 999, }} colors={["#01B7C5", "#782C96"]} /> diff --git a/packages/components/inputs/SelectInput.tsx b/packages/components/inputs/SelectInput.tsx index 3644d7039f..0c5c374577 100644 --- a/packages/components/inputs/SelectInput.tsx +++ b/packages/components/inputs/SelectInput.tsx @@ -52,6 +52,7 @@ type Props = { allowSearchValue?: boolean; name?: string; isLoading?: boolean; + testID?: string; renderItem?: ({ onPressItem, item, @@ -75,6 +76,7 @@ export const SelectInput: React.FC = ({ name, isLoading, renderItem, + testID, }) => { const [openMenu, setOpenMenu] = useState(false); const [hovered, setHovered] = useState(false); @@ -131,6 +133,7 @@ export const SelectInput: React.FC = ({ if (!disabled || selectableData.length) setOpenMenu((value) => !value); }} disabled={disabled || !selectableData.length} + testID={testID} > {label && ( <> diff --git a/packages/components/inputs/TextInputCustom.tsx b/packages/components/inputs/TextInputCustom.tsx index ef95e3184e..6c8f29a1e6 100644 --- a/packages/components/inputs/TextInputCustom.tsx +++ b/packages/components/inputs/TextInputCustom.tsx @@ -264,6 +264,8 @@ export const TextInputCustom = ({ boxMainContainerStyle, noBrokenCorners && styles.noCropBorderBg, hovered && { borderColor: secondaryColor }, + height !== undefined && + height <= styles.mainContainer.minHeight && { minHeight: height }, ]} width={width} fullWidth={!width} diff --git a/packages/components/layout/GridList.tsx b/packages/components/layout/GridList.tsx index 4c4d2cda4d..61257e6715 100644 --- a/packages/components/layout/GridList.tsx +++ b/packages/components/layout/GridList.tsx @@ -16,6 +16,12 @@ type GridListType = React.FC<{ gap?: number; noFixedHeight?: boolean; onEndReached?: ComponentProps>["onEndReached"]; + ListHeaderComponent?: ComponentProps< + typeof FlatList + >["ListHeaderComponent"]; + ListFooterComponent?: ComponentProps< + typeof FlatList + >["ListFooterComponent"]; }>; type ItemWrapper = @@ -36,6 +42,8 @@ export function GridList({ noFixedHeight, keyExtractor, renderItem, + ListHeaderComponent, + ListFooterComponent, }: ComponentProps>): ReturnType> { const { height } = useMaxResolution({ isLarge: true }); const [containerWidth, setContainerWidth] = useState(0); @@ -90,6 +98,7 @@ export function GridList({ > onLayout={(e) => setContainerWidth(e.nativeEvent.layout.width)} contentContainerStyle={!noFixedHeight && { height }} + ListHeaderComponent={ListHeaderComponent} columnWrapperStyle={ elemsPerRow < 2 ? undefined @@ -109,6 +118,7 @@ export function GridList({ size={Math.min(height, containerWidth) / 3} /> } + ListFooterComponent={ListFooterComponent} renderItem={renderItemWithFixedWidth} /> ); diff --git a/packages/components/loaders/AnimatedLoader.tsx b/packages/components/loaders/AnimatedLoader.tsx index 452d91599b..545fc9ddb5 100644 --- a/packages/components/loaders/AnimatedLoader.tsx +++ b/packages/components/loaders/AnimatedLoader.tsx @@ -5,7 +5,8 @@ import { StyleProp, View, ViewStyle } from "react-native"; export const AnimatedLoader: React.FC<{ size?: number; style?: StyleProp; -}> = memo(({ size: sizeProp, style }) => { + testID?: string | undefined; +}> = memo(({ size: sizeProp, style, testID }) => { const size = sizeProp || 80; return ( diff --git a/packages/components/loaders/LoaderFullScreen.tsx b/packages/components/loaders/LoaderFullScreen.tsx index 2c1df5f34b..4cf5dc51bb 100644 --- a/packages/components/loaders/LoaderFullScreen.tsx +++ b/packages/components/loaders/LoaderFullScreen.tsx @@ -17,7 +17,7 @@ export const LoaderFullScreen: React.FC<{ visible: boolean }> = ({ zIndex: 10, }} > - + ); diff --git a/packages/components/modals/ConnectWalletModal.tsx b/packages/components/modals/ConnectWalletModal.tsx index fd48cc2721..c32cde9703 100644 --- a/packages/components/modals/ConnectWalletModal.tsx +++ b/packages/components/modals/ConnectWalletModal.tsx @@ -19,6 +19,8 @@ import { ConnectWalletButton } from "../connectWallet/components/ConnectWalletBu import { SeparatorGradient } from "../separators/SeparatorGradient"; import { SpacerColumn } from "../spacer"; +import { ConnectGnotestButton } from "@/context/WalletsProvider/gnotest"; + export const ConnectWalletModal: FC<{ forceNetworkFeature?: NetworkFeature; label?: string; @@ -109,6 +111,7 @@ export const ConnectWalletModal: FC<{ visible={isDisclaimerVisible} onClose={toggleDisclaimer} /> + ); }; diff --git a/packages/components/navigation/getNormalModeScreens.tsx b/packages/components/navigation/getNormalModeScreens.tsx index 20bcff6e4d..5b134f0389 100644 --- a/packages/components/navigation/getNormalModeScreens.tsx +++ b/packages/components/navigation/getNormalModeScreens.tsx @@ -39,6 +39,13 @@ import { MultisigWalletDashboardScreen } from "@/screens/Multisig/MultisigWallet import { MyCollectionScreen } from "@/screens/MyCollection/MyCollectionScreen"; import { OrganizationDeployerScreen } from "@/screens/Organizations/OrganizationDeployerScreen"; import { OrganizationsScreen } from "@/screens/Organizations/OrganizationsScreen"; +import { ProjectsCompleteMilestoneScreen } from "@/screens/Projects/CompleteMilestoneScreen"; +import { ProjectsConflictSolvingScreen } from "@/screens/Projects/ProjectsConflictSolvingScreen"; +import { ProjectsDetailScreen } from "@/screens/Projects/ProjectsDetailScreen"; +import { ProjectsMakeRequestScreen } from "@/screens/Projects/ProjectsMakeRequestScreen"; +import { ProjectsManagerScreen } from "@/screens/Projects/ProjectsManagerScreen"; +import { ProjectsPaymentScreen } from "@/screens/Projects/ProjectsPaymentScreen"; +import { ProjectsScreen } from "@/screens/Projects/ProjectsScreen"; import { ReadyLaunchpadApplicationsScreen } from "@/screens/ReadyLaunchpadApplications/ReadyLaunchpadApplicationsScreen"; import { RiotGameBreedingScreen } from "@/screens/RiotGame/RiotGameBreedingScreen"; import { RiotGameEnrollScreen } from "@/screens/RiotGame/RiotGameEnrollScreen"; @@ -318,6 +325,67 @@ export const getNormalModeScreens = ({ appMode }: { appMode: AppMode }) => { }} /> + {/* ==== Projects program */} + null, title: screenTitle("Projects Program") }} + /> + + null, + title: screenTitle("Projects Program Detail"), + }} + /> + + null, + title: screenTitle("Projects Program Manager"), + }} + /> + + null, + title: screenTitle("Projects Program Payment"), + }} + /> + + null, + title: screenTitle("Projects Milestone"), + }} + /> + + null, + title: screenTitle("Projects Program Make Request"), + }} + /> + + null, + title: screenTitle("Project Conflict Solving"), + }} + /> + {/* ==== Teritori Name Service*/} = ({ userId, addrLen = 30 }) => { + const [, userAddress] = parseUserId(userId); + const userInfo = useNSUserInfo(userId); + + if (!userId) { + return ( + + None + + ); + } + + const name = + userInfo?.metadata?.tokenId || tinyAddress(userAddress, addrLen) || ""; + + return ( + + + + {name} + + + ); +}; diff --git a/packages/context/SidebarProvider.tsx b/packages/context/SidebarProvider.tsx index 72e56176c6..5f99f59074 100644 --- a/packages/context/SidebarProvider.tsx +++ b/packages/context/SidebarProvider.tsx @@ -15,12 +15,14 @@ import { import { useAppDispatch } from "../store/store"; import { SIDEBAR_LIST } from "../utils/sidebar"; +import { useDeveloperMode } from "@/hooks/useDeveloperMode"; import { getValuesFromId, SEPARATOR } from "@/utils/dapp-store"; export const useSidebar = () => { const isSidebarExpanded = useSelector(selectSidebarExpanded); const selectedApps = useSelector(selectCheckedApps); const availableApps = useSelector(selectAvailableApps); + const [developerMode] = useDeveloperMode(); const dispatch = useAppDispatch(); // on mobile sidebar is not expanded on load const isMobile = useIsMobile(); @@ -51,31 +53,46 @@ export const useSidebar = () => { [key: string]: any; }; - selectedApps.map((element) => { - const { appId, groupKey } = getValuesFromId(element); - if (!availableApps[groupKey]) { - return; - } - const option = availableApps[groupKey].options[appId]; - if (option === undefined) { - return; - } + selectedApps + .filter((element) => { + const { appId, groupKey } = getValuesFromId(element); + if (!availableApps[groupKey]) { + return false; + } + const option = availableApps[groupKey].options[appId]; + if (option === undefined) { + return false; + } + if (option.devOnly && !developerMode) { + return false; + } + return true; + }) + .map((element) => { + const { appId, groupKey } = getValuesFromId(element); + if (!availableApps[groupKey]) { + return; + } + const option = availableApps[groupKey].options[appId]; + if (option === undefined) { + return; + } - dynamicAppsSelection[element] = SIDEBAR_LIST[option.id] - ? SIDEBAR_LIST[option.id] - : { - id: option.id, - title: option.title, - route: option.route, - url: option.url, - icon: option.icon, - }; - }); + dynamicAppsSelection[element] = SIDEBAR_LIST[option.id] + ? SIDEBAR_LIST[option.id] + : { + id: option.id, + title: option.title, + route: option.route, + url: option.url, + icon: option.icon, + }; + }); dynamicAppsSelection["dappstore"] = SIDEBAR_LIST["DAppsStore"]; return dynamicAppsSelection; - }, [availableApps, selectedApps]); + }, [availableApps, selectedApps, developerMode]); const toggleSidebar = () => { dispatch(setSidebarExpanded(!isSidebarExpanded)); diff --git a/packages/context/WalletsProvider/WalletsProvider.tsx b/packages/context/WalletsProvider/WalletsProvider.tsx index 119e7e3833..5ad2410978 100644 --- a/packages/context/WalletsProvider/WalletsProvider.tsx +++ b/packages/context/WalletsProvider/WalletsProvider.tsx @@ -1,6 +1,7 @@ import React, { createContext, ReactNode, useContext, useMemo } from "react"; import { useAdena } from "./adena"; +import { useGnotest } from "./gnotest"; import { useKeplr } from "./keplr"; import { useLeap } from "./leap"; import { useMetamask } from "./metamask"; @@ -33,6 +34,7 @@ export const WalletsProvider: React.FC<{ children: ReactNode }> = React.memo( const [hasLeap, leapIsReady, leapWallets] = useLeap(); const [hasMetamask, metamaskIsReady, metamaskWallets] = useMetamask(); const [hasAdena, adenaIsReady, adenaWallets] = useAdena(); + const [hasGnotest, , gnotestWallets] = useGnotest(); const selectedNativeWallet = useSelectedNativeWallet(); const hasNative = !!selectedNativeWallet; @@ -137,25 +139,34 @@ export const WalletsProvider: React.FC<{ children: ReactNode }> = React.memo( } } + if (hasGnotest) { + walletProviders.push(WalletProvider.Gnotest); + if (gnotestWallets?.[0]?.connected) { + wallets.push(gnotestWallets[0]); + } + } + return { wallets, walletProviders, ready: keplrIsReady && metamaskIsReady && adenaIsReady && leapIsReady, }; }, [ + adenaIsReady, + adenaWallets, + gnotestWallets, + hasAdena, + hasGnotest, hasKeplr, hasLeap, hasMetamask, - hasAdena, hasNative, keplrIsReady, - metamaskIsReady, - adenaIsReady, - leapIsReady, keplrWallets, + leapIsReady, leapWallets, + metamaskIsReady, metamaskWallets, - adenaWallets, selectedNativeWallet, ]); diff --git a/packages/context/WalletsProvider/gnotest/index.tsx b/packages/context/WalletsProvider/gnotest/index.tsx new file mode 100644 index 0000000000..c09e82dec7 --- /dev/null +++ b/packages/context/WalletsProvider/gnotest/index.tsx @@ -0,0 +1,309 @@ +import { + GnoJSONRPCProvider, + GnoWallet, + MsgSend, + decodeTxMessages, +} from "@gnolang/gno-js-client"; +import { TxFee, TransactionEndpoint, Tx } from "@gnolang/tm2-js-client"; +import Long from "long"; +import React from "react"; +import { Pressable } from "react-native"; +import { create } from "zustand"; + +import { Wallet } from "../wallet"; + +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 { WalletProvider } from "@/utils/walletProvider"; + +type UseGnotestResult = [true, boolean, Wallet[]] | [false, boolean, undefined]; + +interface GnotestState { + connected: boolean; + wallet: GnoWallet | null; + address: string | null; + accounts: Account[]; + connect: () => void; +} + +interface Account { + wallet: GnoWallet; + address: string; +} + +const network = gnoDevNetwork; + +interface UserMetadata { + imageURI: string; + displayName: string; + bio: string; +} + +interface TestUser { + name: string; + mnemonic: string; + nsName: string; + extra: UserMetadata; +} + +const faucetMnemo = + "chimney entire define lesson scale embark copy bird rough govern surprise soda sand evidence decline crisp clinic merry slice balance typical creek lumber hollow"; + +const testUsers: TestUser[] = [ + { + name: "alice", + nsName: "alicetests", + mnemonic: + "clog eager dumb mango sibling crime soldier climb switch either lock festival raven please badge weasel misery host grant make half assume strategy buzz", + extra: { + imageURI: + "ipfs://bafybeignxvdw76wqsokthgyp4d3jrvbdiikqwo4vbff6rwb5ivj7fb3aoi", + displayName: "Alice", + bio: "Seasoned marketing strategist with a knack for digital campaigns and brand building. With a background in content creation and SEO, has helped numerous startups grow their online presence and engage with their target audience effectively. Outside of work, is an avid reader, enjoys playing guitar, and is a dedicated coffee enthusiast. Reach out for insights on marketing trends and effective brand strategies.", + }, + }, + { + name: "bob", + nsName: "bobtests", + mnemonic: + "vault garment mosquito legend merit level flavor width correct amazing window skill title dwarf another journey cube junior chat ivory toilet area pool brick", + extra: { + imageURI: + "ipfs://bafkreifvzfnitqu6yq2t4dp37oeaqvitepuutrox332kcs5jncl7uxo6sy", + displayName: "Bob", + bio: "Passionate software developer with over 10 years of experience in web and mobile application development. Specializes in JavaScript, React, and Node.js, and loves creating seamless user experiences. When not coding, enjoys hiking, photography, and volunteering at local animal shelters. Connect to discuss innovative tech solutions and collaborative projects.", + }, + }, + { + name: "dredd", + nsName: "dreddtests", + mnemonic: + "nice daughter demise elevator gate manage observe real outer awkward profit sheriff total boat collect voice speed board pilot rifle nephew grid permit frozen", + extra: { + imageURI: + "ipfs://bafkreiahgkmcppkvxhkus7baqnflrcrff2pkjboi4t4eebt3dq6ui66foi", + displayName: "Dredd", + bio: "Cybersecurity expert with a focus on network security and ethical hacking. With over 15 years in the field, has a proven track record of safeguarding organizations from cyber threats and vulnerabilities. Also a frequent speaker at industry conferences and enjoys writing articles on the latest in cybersecurity. In free time, practices martial arts and enjoys building custom PCs. Connect for advice on protecting digital assets and staying ahead of cyber threats.", + }, + }, +]; + +const useGnotestStore = create((set, get) => ({ + connected: false, + accounts: [], + wallet: null, + address: null, + connect: async () => { + const state = get(); + if (state.connected) { + return; + } + + const provider = new GnoJSONRPCProvider(network.endpoint); + + const faucetWallet = await GnoWallet.fromMnemonic(faucetMnemo); + faucetWallet.connect(provider); + const faucerAddr = await faucetWallet.getAddress(); + + // fund users + const msgs = await Promise.all( + testUsers.map(async (testUser) => { + const wallet = await GnoWallet.fromMnemonic(testUser.mnemonic); + wallet.connect(provider); + // fund user + const msg: MsgSend = { + from_address: faucerAddr, + to_address: await wallet.getAddress(), + amount: "200000000000ugnot", + }; + return msg; + }), + ); + const tx: Tx = { + messages: msgs.map((msg) => ({ + typeUrl: "/bank.MsgSend", + value: MsgSend.encode(msg).finish(), + })), + fee: { + gasFee: "1ugnot", + gasWanted: Long.fromNumber(1000000), + }, + memo: "", + signatures: [], + }; + const signed = await faucetWallet.signTransaction(tx, decodeTxMessages); + await faucetWallet.sendTransaction( + signed, + TransactionEndpoint.BROADCAST_TX_COMMIT, + ); + + const accounts = await Promise.all( + testUsers.map(async (testUser) => { + const wallet = await GnoWallet.fromMnemonic(testUser.mnemonic); + wallet.connect(provider); + const addr = await wallet.getAddress(); + + // try register user + try { + const nsRealm = network.nameServiceContractAddress; + const send = new Map(); + send.set("ugnot", 200000000); + wallet.callMethod( + nsRealm, + "Register", + ["", testUser.nsName, JSON.stringify(testUser.extra)], + TransactionEndpoint.BROADCAST_TX_COMMIT, + send, + { + gasWanted: Long.fromNumber(10000000), + gasFee: "1ugnot", + }, + ); + } catch (e) { + console.warn("Failed to register user", testUser, e); + } + + return { + wallet, + address: addr, + }; + }), + ); + set((state) => ({ + ...state, + connected: true, + accounts, + wallet: accounts[0].wallet, + address: accounts[0].address, + })); + }, +})); + +const setupAdenaMock = () => { + (window as any).adena = { + SetTestUser: async (name: string) => { + const testUser = testUsers.find((u) => u.name === name); + if (!testUser) { + throw new Error("Test user not found: " + name); + } + const newAddr = await ( + await GnoWallet.fromMnemonic(testUser.mnemonic) + ).getAddress(); + const state = useGnotestStore.getState(); + const account = state.accounts.find((a) => a.address === newAddr); + if (!account) { + throw new Error("Account not found: " + newAddr); + } + useGnotestStore.setState({ + wallet: account.wallet, + address: account.address, + }); + return getUserId(network.id, account.address); + }, + GetAccount: async () => { + const state = useGnotestStore.getState(); + return { + data: { + address: state.address, + chainId: network.chainId, + }, + }; + }, + DoContract: async (req: RequestDocontractMessage) => { + try { + const state = useGnotestStore.getState(); + if (!state.wallet) { + throw new Error("Wallet not connected"); + } + const msg = req.messages[0]; + if (msg.type !== "/vm.m_call") { + throw new Error("Unsupported message type: " + msg.type); + } + const txFee: TxFee = { + gasWanted: Long.fromNumber(req.gasWanted), + gasFee: req.gasFee.toString() + "ugnot", + }; + let sendMap; + if (msg.value.send) { + sendMap = new Map(); + const end = msg.value.send.length - "ugnot".length; + const sendAmount = (msg.value.send as string).substring(0, end); + 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, + ); + return { + status: "success", + data: { + hash: res.hash, + height: res.height, + }, + }; + } catch (e) { + console.error("docontract error", e); + const errMsg = e instanceof Error ? e.message : `${e}`; + return { + status: "failure", + message: errMsg, + data: { + error: e, + }, + }; + } + }, + }; +}; + +export const useGnotest: () => UseGnotestResult = () => { + const { wallet, address } = useGnotestStore(); + const wallets: Wallet[] = []; + if (wallet && address) { + wallets.push({ + address, + provider: WalletProvider.Gnotest, + networkKind: network.kind, + networkId: network.id, + userId: getUserId(network.id, address), + connected: true, + id: `gnotest`, + }); + } + return [true, true, wallets]; +}; + +export const ConnectGnotestButton: React.FC<{ + onDone?: () => void; +}> = ({ onDone }) => { + const { connect } = useGnotestStore(); + const dispatch = useAppDispatch(); + return ( + { + setupAdenaMock(); + connect(); + dispatch(setSelectedWalletId("gnotest")); + onDone?.(); + }} + testID="connect-gnotest-wallet" + /> + ); +}; + +const TestHiddenButton: React.FC<{ + testID: string | undefined; + onPress: () => void; +}> = ({ testID, onPress }) => { + return ( + + ); +}; diff --git a/packages/hooks/useNSNameInfo.ts b/packages/hooks/useNSNameInfo.ts index 8b5c1ec0ae..4e53905d07 100644 --- a/packages/hooks/useNSNameInfo.ts +++ b/packages/hooks/useNSNameInfo.ts @@ -64,6 +64,7 @@ export const useNSNameInfo = ( if (!address) { return null; } + // TODO: use profile realm const res: NftInfoResponse = { extension: {}, }; @@ -111,7 +112,7 @@ const gnoGetAddressByUsername = async ( try { const res = await provider.evaluateExpression( network.nameServiceContractAddress, - `GetUserByName(${JSON.stringify(name)}).address`, + `GetUserByName(${JSON.stringify(name)}).Address`, ); const address = extractGnoString(res); return address; diff --git a/packages/networks/cosmos-hub-theta/currencies.ts b/packages/networks/cosmos-hub-theta/currencies.ts index 44f9c60612..c16f288398 100644 --- a/packages/networks/cosmos-hub-theta/currencies.ts +++ b/packages/networks/cosmos-hub-theta/currencies.ts @@ -6,6 +6,7 @@ export const cosmosHubThetaCurrencies: CurrencyInfo[] = [ denom: "uatom", displayName: "ATOM", decimals: 6, + variant: "cosmos", coingeckoId: "cosmos", icon: "cosmos-hub-circle.svg", kind: "native", diff --git a/packages/networks/cosmos-hub/currencies.ts b/packages/networks/cosmos-hub/currencies.ts index 592f1d04ed..9fc336ed21 100644 --- a/packages/networks/cosmos-hub/currencies.ts +++ b/packages/networks/cosmos-hub/currencies.ts @@ -6,6 +6,7 @@ export const cosmosHubCurrencies: CurrencyInfo[] = [ denom: "uatom", displayName: "ATOM", decimals: 6, + variant: "cosmos", coingeckoId: "cosmos", icon: "cosmos-hub-circle.svg", kind: "native", diff --git a/packages/networks/cosmos-registry.ts b/packages/networks/cosmos-registry.ts index 918dc84e99..63180bdf09 100644 --- a/packages/networks/cosmos-registry.ts +++ b/packages/networks/cosmos-registry.ts @@ -54,6 +54,7 @@ export const networksFromCosmosRegistry = (): CosmosNetworkInfo[] => { decimals: asset.denom_units.find((du) => du.denom === asset.display) ?.exponent || 6, + variant: "cosmos", displayName: asset.symbol, coingeckoId: asset.coingecko_id || "not-found", icon: diff --git a/packages/networks/ethereum-goerli/currencies.ts b/packages/networks/ethereum-goerli/currencies.ts index 2f1072d9d6..4f3c6b3636 100644 --- a/packages/networks/ethereum-goerli/currencies.ts +++ b/packages/networks/ethereum-goerli/currencies.ts @@ -6,6 +6,7 @@ export const ethereumGoerliCurrencies: CurrencyInfo[] = [ denom: "0x0000000000000000000000000000000000000000", // native currency: wei displayName: "GoerliETH", decimals: 18, + variant: "ethereum", coingeckoId: "ethereum", icon: "ethereum-circle.svg", kind: "native", diff --git a/packages/networks/ethereum/currencies.ts b/packages/networks/ethereum/currencies.ts index 46989a1966..be77c1116f 100644 --- a/packages/networks/ethereum/currencies.ts +++ b/packages/networks/ethereum/currencies.ts @@ -6,6 +6,7 @@ export const ethereumCurrencies: CurrencyInfo[] = [ denom: "0x0000000000000000000000000000000000000000", // native currency: wei displayName: "ETH", decimals: 18, + variant: "ethereum", coingeckoId: "ethereum", icon: "ethereum-circle.svg", kind: "native", diff --git a/packages/networks/features.ts b/packages/networks/features.ts index 7e718ba128..5be05ca8d0 100644 --- a/packages/networks/features.ts +++ b/packages/networks/features.ts @@ -13,6 +13,7 @@ export enum NetworkFeature { RiotP2E = "RiotP2E", NFTBridge = "NFTBridge", CosmWasmPremiumFeed = "CosmWasmPremiumFeed", + GnoProjectManager = "GnoProjectManager", NFTMarketplaceLeaderboard = "NFTMarketplaceLeaderboard", CosmWasmNFTsBurner = "CosmWasmNFTsBurner", } @@ -43,12 +44,26 @@ type CosmWasmSocialFeed = { feedContractAddress: string; }; +// Gno Project Manager + +const zodGnoProjectManager = z.object({ + type: z.literal(NetworkFeature.GnoProjectManager), + projectsManagerPkgPath: z.string(), + paymentsDenom: z.string(), +}); + +type GnoProjectManager = z.infer; + +// Registry + export const allFeatureObjects = [ zodCosmWasmPremiumFeed, zodCosmWasmNFTsBurner, + zodGnoProjectManager, ]; export type NetworkFeatureObject = | CosmWasmPremiumFeed | CosmWasmSocialFeed - | CosmWasmNFTsBurner; + | CosmWasmNFTsBurner + | GnoProjectManager; diff --git a/packages/networks/gno-dev/currencies.ts b/packages/networks/gno-dev/currencies.ts index baa0c8ce2e..1f1fb4bf11 100644 --- a/packages/networks/gno-dev/currencies.ts +++ b/packages/networks/gno-dev/currencies.ts @@ -6,6 +6,17 @@ export const gnoCurrencies: CurrencyInfo[] = [ denom: "ugnot", displayName: "GNOT", decimals: 6, + variant: "gno", + coingeckoId: "gno", + icon: "gno.svg", + kind: "native", + color: currencyGNOcolor, + }, + { + denom: "gno.land/r/demo/tori20", + displayName: "TORI", + decimals: 6, + variant: "grc20", coingeckoId: "gno", icon: "gno.svg", kind: "native", diff --git a/packages/networks/gno-dev/index.ts b/packages/networks/gno-dev/index.ts index 0f1b3dd3f0..3af1938c6e 100644 --- a/packages/networks/gno-dev/index.ts +++ b/packages/networks/gno-dev/index.ts @@ -10,28 +10,38 @@ export const gnoDevNetwork: GnoNetworkInfo = { NetworkFeature.Organizations, NetworkFeature.SocialFeed, NetworkFeature.UPP, + NetworkFeature.GnoProjectManager, + ], + featureObjects: [ + { + type: NetworkFeature.GnoProjectManager, + projectsManagerPkgPath: "gno.land/r/teritori/projects_manager", + paymentsDenom: "ugnot", + }, ], currencies: gnoCurrencies, stakeCurrency: "ugnot", idPrefix: "gnodev", chainId: "dev", - endpoint: "http://127.0.0.1:36657/http://127.0.0.1:26657", + endpoint: "http://127.0.0.1:26657", txExplorer: "https://gnoscan.io/transactions/details?txhash=$hash", accountExplorer: "https://gnoscan.io/accounts/$address", contractExplorer: "https://gnoscan.io/realms/details?path=$address", testnet: true, - backendEndpoint: "http://localhost:9090", + // backendEndpoint: "http://localhost:9090", + backendEndpoint: "https://dapp-backend.testnet.teritori.com", vaultContractAddress: "", - daoRegistryPkgPath: "gno.land/r/demo/teritori/dao_registry", - socialFeedsPkgPath: "gno.land/r/demo/teritori/social_feeds", - socialFeedsDAOPkgPath: "gno.land/r/demo/teritori/social_feeds_dao", + daoRegistryPkgPath: "gno.land/r/teritori/dao_registry", + socialFeedsPkgPath: "gno.land/r/teritori/social_feeds", + socialFeedsDAOPkgPath: "gno.land/r/teritori/social_feeds_dao", nameServiceContractAddress: "gno.land/r/demo/users", - modboardsPkgPath: "gno.land/r/demo/teritori/modboards", - groupsPkgPath: "gno.land/r/demo/teritori/groups", - votingGroupPkgPath: "gno.land/p/demo/teritori/dao_voting_group", - daoProposalSinglePkgPath: "gno.land/p/demo/teritori/dao_proposal_single", - daoInterfacesPkgPath: "gno.land/p/demo/teritori/dao_interfaces", - daoCorePkgPath: "gno.land/p/demo/teritori/dao_core", + modboardsPkgPath: "gno.land/r/teritori/modboards", + groupsPkgPath: "gno.land/r/teritori/groups", + votingGroupPkgPath: "gno.land/p/teritori/dao_voting_group", + daoProposalSinglePkgPath: "gno.land/p/teritori/dao_proposal_single", + + daoInterfacesPkgPath: "gno.land/p/teritori/dao_interfaces", + daoCorePkgPath: "gno.land/p/teritori/dao_core", nameServiceDefaultImage: "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", gnowebURL: "http://127.0.0.1:8888", diff --git a/packages/networks/gno-portal/currencies.ts b/packages/networks/gno-portal/currencies.ts index baa0c8ce2e..b1beeaf715 100644 --- a/packages/networks/gno-portal/currencies.ts +++ b/packages/networks/gno-portal/currencies.ts @@ -10,5 +10,6 @@ export const gnoCurrencies: CurrencyInfo[] = [ icon: "gno.svg", kind: "native", color: currencyGNOcolor, + variant: "gno", }, ]; diff --git a/packages/networks/gno-portal/index.ts b/packages/networks/gno-portal/index.ts index bacdd1158e..ac76c7d796 100644 --- a/packages/networks/gno-portal/index.ts +++ b/packages/networks/gno-portal/index.ts @@ -26,13 +26,13 @@ export const gnoPortalNetwork: GnoNetworkInfo = { nameServiceContractAddress: "gno.land/r/demo/users", nameServiceDefaultImage: "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", - daoRegistryPkgPath: "gno.land/r/demo/teritori/dao_registry", - socialFeedsPkgPath: "gno.land/r/demo/teritori/social_feeds", - socialFeedsDAOPkgPath: "gno.land/r/demo/teritori/social_feeds_dao", - // modboardsPkgPath: "gno.land/r/demo/teritori/modboards_v4", - groupsPkgPath: "gno.land/r/demo/teritori/groups", - votingGroupPkgPath: "gno.land/p/demo/teritori/dao_voting_group", - daoProposalSinglePkgPath: "gno.land/p/demo/teritori/dao_proposal_single", - daoInterfacesPkgPath: "gno.land/p/demo/teritori/dao_interfaces", - daoCorePkgPath: "gno.land/p/demo/teritori/dao_core", + daoRegistryPkgPath: "gno.land/r/teritori/dao_registry", + socialFeedsPkgPath: "gno.land/r/teritori/social_feeds", + socialFeedsDAOPkgPath: "gno.land/r/teritori/social_feeds_dao", + // modboardsPkgPath: "gno.land/r/teritori/modboards_v4", + groupsPkgPath: "gno.land/r/teritori/groups", + votingGroupPkgPath: "gno.land/p/teritori/dao_voting_group", + daoProposalSinglePkgPath: "gno.land/p/teritori/dao_proposal_single", + daoInterfacesPkgPath: "gno.land/p/teritori/dao_interfaces", + daoCorePkgPath: "gno.land/p/teritori/dao_core", }; diff --git a/packages/networks/gno-teritori/currencies.ts b/packages/networks/gno-teritori/currencies.ts index baa0c8ce2e..cb3c09b8b1 100644 --- a/packages/networks/gno-teritori/currencies.ts +++ b/packages/networks/gno-teritori/currencies.ts @@ -6,6 +6,7 @@ export const gnoCurrencies: CurrencyInfo[] = [ denom: "ugnot", displayName: "GNOT", decimals: 6, + variant: "gno", coingeckoId: "gno", icon: "gno.svg", kind: "native", diff --git a/packages/networks/gno-teritori/index.ts b/packages/networks/gno-teritori/index.ts index 52a0639099..65ce09d447 100644 --- a/packages/networks/gno-teritori/index.ts +++ b/packages/networks/gno-teritori/index.ts @@ -10,6 +10,14 @@ export const gnoTeritoriNetwork: GnoNetworkInfo = { NetworkFeature.Organizations, NetworkFeature.SocialFeed, NetworkFeature.UPP, + NetworkFeature.GnoProjectManager, + ], + featureObjects: [ + { + type: NetworkFeature.GnoProjectManager, + projectsManagerPkgPath: "gno.land/r/teritori/escrow", + paymentsDenom: "ugnot", + }, ], currencies: gnoCurrencies, stakeCurrency: "ugnot", @@ -25,15 +33,15 @@ export const gnoTeritoriNetwork: GnoNetworkInfo = { nameServiceContractAddress: "gno.land/r/demo/users", nameServiceDefaultImage: "ipfs://bafkreignptjimiu7wuux6mk6uh4hb4odb6ff62ny4bvdokrhes7g67huse", - daoRegistryPkgPath: "gno.land/r/demo/teritori/dao_registry_v4", - socialFeedsPkgPath: "gno.land/r/demo/teritori/social_feeds_v4", - socialFeedsDAOPkgPath: "gno.land/r/demo/teritori/social_feeds_dao_v2", - modboardsPkgPath: "gno.land/r/demo/teritori/modboards_v4", - groupsPkgPath: "gno.land/r/demo/teritori/groups_v4", - votingGroupPkgPath: "gno.land/p/demo/teritori/dao_voting_group_v2", - daoProposalSinglePkgPath: "gno.land/p/demo/teritori/dao_proposal_single_v4", - daoInterfacesPkgPath: "gno.land/p/demo/teritori/dao_interfaces_v5", - daoCorePkgPath: "gno.land/p/demo/teritori/dao_core_v4", + daoRegistryPkgPath: "gno.land/r/teritori/dao_registry_v4", + socialFeedsPkgPath: "gno.land/r/teritori/social_feeds_v4", + socialFeedsDAOPkgPath: "gno.land/r/teritori/social_feeds_dao_v2", + modboardsPkgPath: "gno.land/r/teritori/modboards_v4", + groupsPkgPath: "gno.land/r/teritori/groups_v4", + 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", + daoCorePkgPath: "gno.land/p/teritori/dao_core_v4", gnowebURL: "https://testnet.gno.teritori.com", faucetURL: "https://testnet.gno.teritori.com:5050/?toaddr=$addr", }; diff --git a/packages/networks/gno-test3/currencies.ts b/packages/networks/gno-test3/currencies.ts index baa0c8ce2e..cb3c09b8b1 100644 --- a/packages/networks/gno-test3/currencies.ts +++ b/packages/networks/gno-test3/currencies.ts @@ -6,6 +6,7 @@ export const gnoCurrencies: CurrencyInfo[] = [ denom: "ugnot", displayName: "GNOT", decimals: 6, + variant: "gno", coingeckoId: "gno", icon: "gno.svg", kind: "native", diff --git a/packages/networks/gno-test4/currencies.ts b/packages/networks/gno-test4/currencies.ts index baa0c8ce2e..b1beeaf715 100644 --- a/packages/networks/gno-test4/currencies.ts +++ b/packages/networks/gno-test4/currencies.ts @@ -10,5 +10,6 @@ export const gnoCurrencies: CurrencyInfo[] = [ icon: "gno.svg", kind: "native", color: currencyGNOcolor, + variant: "gno", }, ]; diff --git a/packages/networks/osmosis-testnet/currencies.ts b/packages/networks/osmosis-testnet/currencies.ts index a303be43a1..1e42e542e6 100644 --- a/packages/networks/osmosis-testnet/currencies.ts +++ b/packages/networks/osmosis-testnet/currencies.ts @@ -6,6 +6,7 @@ export const osmosisTestnetCurrencies: CurrencyInfo[] = [ denom: "uosmo", displayName: "OSMO", decimals: 6, + variant: "cosmos", coingeckoId: "osmosis", icon: "osmosis-circle.svg", kind: "native", diff --git a/packages/networks/osmosis/currencies.ts b/packages/networks/osmosis/currencies.ts index fb7be4d49c..531b6297fa 100644 --- a/packages/networks/osmosis/currencies.ts +++ b/packages/networks/osmosis/currencies.ts @@ -6,6 +6,7 @@ export const osmosisCurrencies: CurrencyInfo[] = [ denom: "uosmo", displayName: "OSMO", decimals: 6, + variant: "cosmos", coingeckoId: "osmosis", icon: "osmosis-circle.svg", kind: "native", diff --git a/packages/networks/polygon-mumbai/currencies.ts b/packages/networks/polygon-mumbai/currencies.ts index 7fa475c69f..61eaa967e9 100644 --- a/packages/networks/polygon-mumbai/currencies.ts +++ b/packages/networks/polygon-mumbai/currencies.ts @@ -6,6 +6,7 @@ export const polygonMumbaiCurrencies: CurrencyInfo[] = [ denom: "0x0000000000000000000000000000000000000000", // native currency: wei displayName: "MATIC", decimals: 18, + variant: "ethereum", coingeckoId: "matic", icon: "polygon.svg", kind: "native", diff --git a/packages/networks/polygon/currencies.ts b/packages/networks/polygon/currencies.ts index 41e99fb676..7743389439 100644 --- a/packages/networks/polygon/currencies.ts +++ b/packages/networks/polygon/currencies.ts @@ -6,6 +6,7 @@ export const polygonCurrencies: CurrencyInfo[] = [ denom: "0x0000000000000000000000000000000000000000", // native currency: wei displayName: "MATIC", decimals: 18, + variant: "ethereum", coingeckoId: "matic", icon: "polygon.svg", kind: "native", diff --git a/packages/networks/saga-test2/index.ts b/packages/networks/saga-test2/index.ts index f3cdaf397a..3b25595f0a 100644 --- a/packages/networks/saga-test2/index.ts +++ b/packages/networks/saga-test2/index.ts @@ -15,6 +15,7 @@ export const sagaTest2: CosmosNetworkInfo = { coingeckoId: "not-found", icon: "https://raw.githubusercontent.com/cosmos/chain-registry/master/saga/images/saga.svg", color: "TODO", + variant: "cosmos", }, ], features: [], diff --git a/packages/networks/saga/index.ts b/packages/networks/saga/index.ts index 94583d3411..d17d226655 100644 --- a/packages/networks/saga/index.ts +++ b/packages/networks/saga/index.ts @@ -16,6 +16,7 @@ export const sagaNetwork: CosmosNetworkInfo = { coingeckoId: "saga-2", icon: "https://raw.githubusercontent.com/cosmos/chain-registry/master/saga/images/saga.svg", color: "TODO", + variant: "cosmos", }, ], features: [], diff --git a/packages/networks/teritori-localnet/currencies.ts b/packages/networks/teritori-localnet/currencies.ts index 3eddd9280c..ef6dba4cf0 100644 --- a/packages/networks/teritori-localnet/currencies.ts +++ b/packages/networks/teritori-localnet/currencies.ts @@ -5,6 +5,7 @@ export const teritoriLocalnetCurrencies: CurrencyInfo[] = [ { denom: "utori", displayName: "TORI", + variant: "cosmos", decimals: 6, coingeckoId: "teritori", icon: "icons/networks/teritori-circle.svg", diff --git a/packages/networks/teritori-testnet/currencies.ts b/packages/networks/teritori-testnet/currencies.ts index 3dd718088a..493fb08133 100644 --- a/packages/networks/teritori-testnet/currencies.ts +++ b/packages/networks/teritori-testnet/currencies.ts @@ -5,6 +5,7 @@ export const teritoriTestnetCurrencies: CurrencyInfo[] = [ { denom: "utori", displayName: "TORI", + variant: "cosmos", decimals: 6, coingeckoId: "teritori", icon: "teritori-circle.svg", diff --git a/packages/networks/teritori/currencies.ts b/packages/networks/teritori/currencies.ts index ce384c1635..5c1bfc44f0 100644 --- a/packages/networks/teritori/currencies.ts +++ b/packages/networks/teritori/currencies.ts @@ -5,6 +5,7 @@ export const teritoriCurrencies: CurrencyInfo[] = [ { denom: "utori", displayName: "TORI", + variant: "cosmos", decimals: 6, coingeckoId: "teritori", icon: "teritori-circle.svg", diff --git a/packages/networks/types.ts b/packages/networks/types.ts index 1481d490aa..626a7357df 100644 --- a/packages/networks/types.ts +++ b/packages/networks/types.ts @@ -127,6 +127,7 @@ export type NetworkInfo = export type NativeCurrencyInfo = { kind: "native"; + variant: "cosmos" | "ethereum" | "gno" | "grc20"; denom: string; displayName: string; decimals: number; diff --git a/packages/screens/DAppStore/components/RightRail.tsx b/packages/screens/DAppStore/components/RightRail.tsx index 0592795d34..70172fde50 100644 --- a/packages/screens/DAppStore/components/RightRail.tsx +++ b/packages/screens/DAppStore/components/RightRail.tsx @@ -7,6 +7,7 @@ import { DAppBox } from "./DAppBox"; import { BrandText } from "@/components/BrandText"; import { SVGorImageIcon } from "@/components/SVG/SVGorImageIcon"; import { GridList } from "@/components/layout/GridList"; +import { useDeveloperMode } from "@/hooks/useDeveloperMode"; import { selectAvailableApps } from "@/store/slices/dapps-store"; import { layout } from "@/utils/style/layout"; import { dAppType } from "@/utils/types/dapp-store"; @@ -14,6 +15,7 @@ import { dAppType } from "@/utils/types/dapp-store"; export const RightRail = ({ searchInput }: { searchInput: string }) => { const availableApps = useSelector(selectAvailableApps); const { width } = useWindowDimensions(); + const [developerMode] = useDeveloperMode(); const isMobile = width < 760; return ( { noFixedHeight keyExtractor={(item) => item.id} data={Object.values(element.options).filter( - (option: dAppType) => - option.title + (option: dAppType) => { + if (option.devOnly && !developerMode) { + return false; + } + return option.title .toLowerCase() - .includes(searchInput.toLowerCase()), + .includes(searchInput.toLowerCase()); + }, )} minElemWidth={300} renderItem={({ item: option }, elemWidth) => { diff --git a/packages/screens/DAppStore/query/getFromFile.ts b/packages/screens/DAppStore/query/getFromFile.ts index 7b1636137d..4896bf26cb 100644 --- a/packages/screens/DAppStore/query/getFromFile.ts +++ b/packages/screens/DAppStore/query/getFromFile.ts @@ -14,6 +14,7 @@ import multisig from "@/assets/icons/multisig.svg"; import osmosisSVG from "@/assets/icons/networks/osmosis.svg"; import teritoriSVG from "@/assets/icons/networks/teritori.svg"; import pathwar from "@/assets/icons/pathwar.svg"; +import projectsProgramSVG from "@/assets/icons/projects-program.svg"; import otherAppsIcon from "@/assets/icons/random-goods-icon.svg"; import riot from "@/assets/icons/rioters-game.svg"; import staking from "@/assets/icons/staking.svg"; @@ -192,6 +193,17 @@ export function getAvailableApps(): dAppGroup { selectedByDefault: true, alwaysOn: true, }, + projects: { + id: "projects", + title: "Projects Program", + icon: projectsProgramSVG, + description: "Projects Program", + route: "Projects", + groupKey: "top-apps", + selectedByDefault: false, + alwaysOn: false, + devOnly: true, + }, toripunks: { id: "toripunks", title: "Toripunks dApp", diff --git a/packages/screens/MarketplaceLeaderboardScreen/component/MarketplaceLeaderboardTable.tsx b/packages/screens/MarketplaceLeaderboardScreen/component/MarketplaceLeaderboardTable.tsx index 39be10ff22..5361050e1e 100644 --- a/packages/screens/MarketplaceLeaderboardScreen/component/MarketplaceLeaderboardTable.tsx +++ b/packages/screens/MarketplaceLeaderboardScreen/component/MarketplaceLeaderboardTable.tsx @@ -5,7 +5,6 @@ import { FlatList, View } from "react-native"; import { screenContentMaxWidthLarge } from "../../../utils/style/layout"; import { LeaderboardEntry } from "@/api/marketplace/v1/marketplace"; -import { UserNameInline } from "@/components/UserNameInline"; import { SpacerColumn } from "@/components/spacer"; import { TableCell } from "@/components/table/TableCell"; import { TableHeader } from "@/components/table/TableHeader"; @@ -13,6 +12,7 @@ import { TableRow } from "@/components/table/TableRow"; import { TableTextCell } from "@/components/table/TableTextCell"; import { TableWrapper } from "@/components/table/TableWrapper"; import { TableColumns } from "@/components/table/utils"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; import { getMarketplaceClient } from "@/utils/backend"; const columns: TableColumns = { @@ -125,7 +125,7 @@ const MarketplaceLeaderboardTableRow: React.FC<{ flex: columns.userId.flex, }} > - + { { label: ( - + ninja.tori diff --git a/packages/screens/Projects/CompleteMilestoneScreen.tsx b/packages/screens/Projects/CompleteMilestoneScreen.tsx new file mode 100644 index 0000000000..fa0259fda9 --- /dev/null +++ b/packages/screens/Projects/CompleteMilestoneScreen.tsx @@ -0,0 +1,263 @@ +import React, { useState } from "react"; +import { View } from "react-native"; +import { SvgProps } from "react-native-svg"; + +import { HeaderBackButton } from "./components/HeaderBackButton"; +import { MilestoneStatusTag } from "./components/MilestoneStatusTag"; +import { useProject } from "./hooks/useProjects"; +import { Project, ProjectMilestone } from "./types"; +import githubSVG from "../../../assets/icons/github.svg"; +import FlexRow from "../../components/FlexRow"; +import useSelectedWallet from "../../hooks/useSelectedWallet"; + +import { BrandText } from "@/components/BrandText"; +import { ScreenContainer } from "@/components/ScreenContainer"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { PrimaryButtonOutline } from "@/components/buttons/PrimaryButtonOutline"; +import { SocialButton } from "@/components/buttons/SocialButton"; +import { RoundedGradientImage } from "@/components/images/RoundedGradientImage"; +import { TextInputCustom } from "@/components/inputs/TextInputCustom"; +import { Separator } from "@/components/separators/Separator"; +import { SpacerColumn, SpacerRow } from "@/components/spacer"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { prettyPrice } from "@/utils/coins"; +import { ScreenFC, useAppNavigation } from "@/utils/navigation"; +import { + neutral17, + neutral77, + neutralA3, + neutralFF, + primaryColor, + redDefault, +} from "@/utils/style/colors"; +import { + fontSemibold13, + fontSemibold14, + fontSemibold16, + fontSemibold20, + fontSemibold28, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +const CustomSocialButton: React.FC<{ + text: string; + iconSvg: React.FC; +}> = ({ text, iconSvg }) => { + return ( + + ); +}; + +export const ProjectsCompleteMilestoneScreen: ScreenFC< + "ProjectsCompleteMilestone" +> = ({ route: { params } }) => { + const navigation = useAppNavigation(); + + const networkId = useSelectedNetworkId(); + const { projectId, milestoneId } = params; + const [isProcessing, setIsProcessing] = useState(false); + const selectedWallet = useSelectedWallet(); + + const { data: project } = useProject(projectId); + const milestone = (project?.milestones || []).find( + (_, idx) => idx === +milestoneId, + ); + + const [report, setReport] = useState(""); + + const { execEscrowMethod } = useEscrowContract( + networkId, + selectedWallet?.address, + ); + + const completeMilestone = async ( + project: Project, + milestone: ProjectMilestone, + ) => { + setIsProcessing(true); + + const isOk = await execEscrowMethod("CompleteMilestoneAndPay", [ + project?.id?.toString(), + milestone.id.toString(), + ]); + + setIsProcessing(true); + + if (isOk) { + navigation.navigate("ProjectsDetail", { id: projectId }); + } + }; + + if (!milestone || !project) return null; + + return ( + }> + + + + + + + Project + + + + {project.metadata?.shortDescData?.name} + + + + + + + + {/* Left Col ==========================================================*/} + + + + + Milestone: {milestone.title} + + + + + + {milestone.desc} + + + + + + + + Report input: + + + + + setReport(text)} + label="" + name="name" + placeholder="Enter details here..." + hideLabel + multiline + noBrokenCorners + containerStyle={{ width: "100%" }} + textInputStyle={{ height: 80 }} + /> + + + + + + + + + + {/* Right Col =========================================================*/} + + + + Budget + + + {prettyPrice(networkId, milestone.amount, project.paymentDenom)} + + + + + + + Status + + + + + + + + + Contractor: + + + + @{project?.contractor} + + + + + + + + + + + + + completeMilestone(project, milestone)} + fullWidth + disabled={isProcessing} + text="Complete the Milestone" + /> + + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsConflictSolvingScreen/NewConflictSection.tsx b/packages/screens/Projects/ProjectsConflictSolvingScreen/NewConflictSection.tsx new file mode 100644 index 0000000000..a886671863 --- /dev/null +++ b/packages/screens/Projects/ProjectsConflictSolvingScreen/NewConflictSection.tsx @@ -0,0 +1,82 @@ +import React, { FC, useState } from "react"; + +import { BrandText } from "@/components/BrandText"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButtonOutline } from "@/components/buttons/PrimaryButtonOutline"; +import { TextInputCustom } from "@/components/inputs/TextInputCustom"; +import { SpacerColumn } from "@/components/spacer"; +import { parseUserId } from "@/networks"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { neutral17, neutralFF, redDefault } from "@/utils/style/colors"; +import { fontSemibold28 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +export const NewConflictSection: FC<{ + projectId: string | undefined; + userId: string | undefined; +}> = ({ projectId, userId }) => { + const [initialMessage, setInitialMessage] = useState(""); + const [network, userAddress] = parseUserId(userId); + + const { execEscrowMethod } = useEscrowContract(network?.id, userAddress); + + return ( + <> + + New conflict + + + + Initial message + + + + + setInitialMessage(text)} + label="" + name="initialMessage" + placeholder="Enter details here..." + hideLabel + multiline + noBrokenCorners + containerStyle={{ width: "100%" }} + textInputStyle={{ height: 80 }} + /> + + + + { + const [network, userAddress] = parseUserId(userId); + if (!network || !userAddress) { + throw new Error("Invalid user id"); + } + if (!projectId) { + throw new Error("Invalid project id"); + } + + await execEscrowMethod("RequestConflictResolution", [ + projectId, + initialMessage, + ]); + }} + /> + + + ); +}; diff --git a/packages/screens/Projects/ProjectsConflictSolvingScreen/OngoingConflictSection.tsx b/packages/screens/Projects/ProjectsConflictSolvingScreen/OngoingConflictSection.tsx new file mode 100644 index 0000000000..f2df44a926 --- /dev/null +++ b/packages/screens/Projects/ProjectsConflictSolvingScreen/OngoingConflictSection.tsx @@ -0,0 +1,298 @@ +import { Picker } from "@react-native-picker/picker"; +import moment from "moment/moment"; +import React, { FC, useState } from "react"; +import { View } from "react-native"; + +import { BrandText } from "@/components/BrandText"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButtonOutline } from "@/components/buttons/PrimaryButtonOutline"; +import { TextInputCustom } from "@/components/inputs/TextInputCustom"; +import { SpacerColumn } from "@/components/spacer"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import { getUserId, parseUserId } from "@/networks"; +import { + PartyRole, + UserRole, +} from "@/screens/Projects/ProjectsConflictSolvingScreen/types"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { useProject } from "@/screens/Projects/hooks/useProjects"; +import { + errorColor, + neutral17, + redDefault, + yellowDefault, +} from "@/utils/style/colors"; +import { fontSemibold28 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +export const OngoingConflictSection: FC<{ + userId: string | undefined; + projectId: string | undefined; +}> = ({ userId, projectId }) => { + const [network, userAddress] = parseUserId(userId); + const [outcome, setOutcome] = useState(1); + const [responseMessage, setResponseMessage] = useState(""); + const [resolutionMessage, setResolutionMessage] = useState(""); + const { data: project } = useProject(projectId); + const conflicts = project?.conflicts; + const lastConflict = conflicts?.length + ? conflicts[conflicts.length - 1] + : null; + + const { execEscrowMethod } = useEscrowContract(network?.id, userAddress); + + let responderAddress; + if (lastConflict?.initiator === project?.contractor) { + responderAddress = project?.funder; + } else { + responderAddress = project?.contractor; + } + + let userRole: UserRole; + if (lastConflict?.initiator === userAddress) { + userRole = "initiator"; + } else if (responderAddress === userAddress) { + userRole = "responder"; + } else if (project?.conflictHandler === userAddress) { + userRole = "resolver"; + } else { + userRole = "observer"; + } + + let initiatorRole: PartyRole; + if (lastConflict?.initiator === project?.contractor) { + initiatorRole = "contractor"; + } else { + initiatorRole = "funder"; + } + let responderRole: PartyRole; + if (responderAddress === project?.contractor) { + responderRole = "contractor"; + } else { + responderRole = "funder"; + } + + return ( + + + Ongoing conflict + + + + + Initial message by {initiatorRole} on{" "} + {moment(lastConflict?.createdAt).format("MMM D, YYYY")} + + + + + + + {(userRole === "responder" || !!lastConflict?.respondedAt) && ( + <> + + + + Response by {responderRole} on{" "} + {moment(lastConflict?.respondedAt).format("MMM D, YYYY")} + + + + + + {userRole === "responder" && !lastConflict?.respondedAt && ( + <> + + { + if (!projectId) { + throw new Error("Invalid project id"); + } + if (!network || !userAddress) { + throw new Error("Invalid user id"); + } + + await execEscrowMethod("RespondToConflict", [ + projectId, + responseMessage, + ]); + }} + /> + + )} + + )} + + {(!!lastConflict?.resolvedAt || userRole === "resolver") && + !lastConflict?.respondedAt && ( + <> + + + + No response from {responderRole} after{" "} + {moment(lastConflict?.createdAt).fromNow(true)} + + + + + )} + + {userRole !== "resolver" && !lastConflict?.resolvedAt && ( + <> + + + Waiting for{!lastConflict?.respondedAt ? " response or " : " "} + resolution for {moment(lastConflict?.createdAt).fromNow(true)}... + + + )} + + {(userRole === "resolver" || !!lastConflict?.resolvedAt) && ( + <> + + + Verdict + + + + + {userRole === "resolver" && !lastConflict?.resolvedAt && ( + <> + + + + { + if (!projectId) { + throw new Error("Invalid project id"); + } + if (!network || !userAddress) { + throw new Error("Invalid user id"); + } + + await execEscrowMethod("ResolveConflict", [ + projectId, + outcome.toString(), + resolutionMessage, + ]); + }} + /> + + )} + + )} + + + ); +}; + +const OutcomeSelect: FC<{ + value: number; + onChange: (value: number) => void; +}> = ({ value, onChange }) => { + return ( + selectedValue={value} onValueChange={onChange}> + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsConflictSolvingScreen/SettledConflictsSection.tsx b/packages/screens/Projects/ProjectsConflictSolvingScreen/SettledConflictsSection.tsx new file mode 100644 index 0000000000..9970e8939c --- /dev/null +++ b/packages/screens/Projects/ProjectsConflictSolvingScreen/SettledConflictsSection.tsx @@ -0,0 +1,204 @@ +import moment from "moment/moment"; +import React, { FC } from "react"; +import { View } from "react-native"; + +import { BrandText } from "@/components/BrandText"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { TextInputCustom } from "@/components/inputs/TextInputCustom"; +import { SpacerColumn } from "@/components/spacer"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import { getUserId, parseNetworkObjectId } from "@/networks"; +import { PartyRole } from "@/screens/Projects/ProjectsConflictSolvingScreen/types"; +import { useProject } from "@/screens/Projects/hooks/useProjects"; +import { errorColor, neutral17 } from "@/utils/style/colors"; +import { fontSemibold28 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +export const SettledConflictsSection: FC<{ + projectId: string | undefined; +}> = ({ projectId }) => { + const [network] = parseNetworkObjectId(projectId); + const networkId = network?.id; + const { data: project } = useProject(projectId); + return ( + <> + Settled conflicts + {[...(project?.conflicts || [])] + .reverse() + .filter((c) => !!c.outcome) + .map((conflict, index) => { + let responderAddress; + if (conflict.initiator === project?.contractor) { + responderAddress = project?.funder; + } else { + responderAddress = project?.contractor; + } + + let initiatorRole: PartyRole; + if (conflict.initiator === project?.contractor) { + initiatorRole = "contractor"; + } else { + initiatorRole = "funder"; + } + let responderRole: PartyRole; + if (responderAddress === project?.contractor) { + responderRole = "contractor"; + } else { + responderRole = "funder"; + } + + let outcomeColor, outcomeText; + switch (conflict.outcome) { + case "RESUME_CONTRACT": + outcomeColor = "white"; + outcomeText = "Project resumed"; + break; + case "REFUND_FUNDER": + outcomeColor = errorColor; + outcomeText = "Funder reimbursed"; + break; + case "PAY_CONTRACTOR": + outcomeColor = errorColor; + outcomeText = "Contractor paid"; + break; + default: + outcomeColor = errorColor; + outcomeText = "Unknown outcome"; + break; + } + + return ( + <> + + + + + Initial message by {initiatorRole} on{" "} + {moment(conflict.createdAt).format("MMM D, YYYY")} + + + + + + + + + {conflict.respondedAt ? ( + <> + + + Response by {responderRole} on{" "} + {moment(conflict.respondedAt).format("MMM D, YYYY")} + + + + + + + ) : ( + <> + + + No response from {responderRole} after{" "} + {moment(conflict.createdAt).fromNow(true)} + + + + + )} + + + + + Verdict on{" "} + {moment(conflict.resolvedAt).format("MMM D, YYYY")} + + + + + + + + {outcomeText} + + + + ); + })} + + ); +}; diff --git a/packages/screens/Projects/ProjectsConflictSolvingScreen/index.tsx b/packages/screens/Projects/ProjectsConflictSolvingScreen/index.tsx new file mode 100644 index 0000000000..a66158d4e7 --- /dev/null +++ b/packages/screens/Projects/ProjectsConflictSolvingScreen/index.tsx @@ -0,0 +1,45 @@ +import React from "react"; + +import { useProject } from "../hooks/useProjects"; + +import { ScreenContainer } from "@/components/ScreenContainer"; +import { SpacerColumn } from "@/components/spacer"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { NewConflictSection } from "@/screens/Projects/ProjectsConflictSolvingScreen/NewConflictSection"; +import { OngoingConflictSection } from "@/screens/Projects/ProjectsConflictSolvingScreen/OngoingConflictSection"; +import { SettledConflictsSection } from "@/screens/Projects/ProjectsConflictSolvingScreen/SettledConflictsSection"; +import { ScreenFC } from "@/utils/navigation"; + +export const ProjectsConflictSolvingScreen: ScreenFC< + "ProjectsConflictSolving" +> = ({ route }) => { + const projectId = route.params.projectId; + const selectedWallet = useSelectedWallet(); + const userId = selectedWallet?.userId; + const userAddress = selectedWallet?.address; + const { data: project } = useProject(projectId); + const userIsParty = + !!project && + !!userAddress && + (userAddress === project?.contractor || userAddress === project?.funder); + return ( + + + {project?.status === "ACCEPTED" && userIsParty && ( + + )} + + {project?.status === "CONFLICT" && ( + + )} + + {(project?.conflicts?.length || 0) > 0 && + !( + project?.conflicts?.length === 1 && project.status === "CONFLICT" + ) && } + + ); +}; diff --git a/packages/screens/Projects/ProjectsConflictSolvingScreen/types.ts b/packages/screens/Projects/ProjectsConflictSolvingScreen/types.ts new file mode 100644 index 0000000000..01443b2a37 --- /dev/null +++ b/packages/screens/Projects/ProjectsConflictSolvingScreen/types.ts @@ -0,0 +1,2 @@ +export type UserRole = "initiator" | "responder" | "resolver" | "observer"; +export type PartyRole = "contractor" | "funder"; diff --git a/packages/screens/Projects/ProjectsDetailScreen/MilestoneDetail.tsx b/packages/screens/Projects/ProjectsDetailScreen/MilestoneDetail.tsx new file mode 100644 index 0000000000..ee076dca20 --- /dev/null +++ b/packages/screens/Projects/ProjectsDetailScreen/MilestoneDetail.tsx @@ -0,0 +1,222 @@ +import { Link } from "@react-navigation/native"; +import { useQueryClient } from "@tanstack/react-query"; +import React, { useState } from "react"; +import { TouchableOpacity, View } from "react-native"; + +import chevronRightSVG from "@/assets/icons/chevron-right.svg"; +import closeSVG from "@/assets/icons/close.svg"; +import githubSVG from "@/assets/icons/github.svg"; +import { BrandText } from "@/components/BrandText"; +import FlexRow from "@/components/FlexRow"; +import { SVG } from "@/components/SVG"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { SelectInput, SelectInputItem } from "@/components/inputs/SelectInput"; +import { Separator } from "@/components/separators/Separator"; +import { SpacerColumn } from "@/components/spacer"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { getNetworkObjectId } from "@/networks"; +import { MilestonePriorityTag } from "@/screens/Projects/components/MilestonePriorityTag"; +import { MilestoneStatusTag } from "@/screens/Projects/components/MilestoneStatusTag"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { + MilestoneStatus, + Project, + ProjectMilestone, + zodMilestoneStatus, +} from "@/screens/Projects/types"; +import { + neutral00, + neutral22, + neutral33, + neutralA3, +} from "@/utils/style/colors"; +import { + fontSemibold13, + fontSemibold14, + fontSemibold20, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { objectKeys } from "@/utils/typescript"; + +const STATUSES: SelectInputItem[] = [ + { label: "Open", value: "MS_OPEN" }, + { label: "In Progress", value: "MS_PROGRESS" }, + { label: "Review", value: "MS_REVIEW" }, + { label: "Completed", value: "MS_COMPLETED" }, +]; + +export const MilestoneDetail: React.FC<{ + project: Project; + milestone: ProjectMilestone; + editable?: boolean; + onClose?: (milestone: ProjectMilestone) => void; +}> = ({ project, editable, milestone, onClose }) => { + const [newStatus, setNewStatus] = useState(milestone.status); + const networkId = useSelectedNetworkId(); + const selectedWallet = useSelectedWallet(); + + const { execEscrowMethod } = useEscrowContract( + networkId, + selectedWallet?.address, + ); + const [isProcessing, setIsProcessing] = useState(false); + const queryClient = useQueryClient(); + + const changeMilestoneStatus = async ( + project: Project, + milestone: ProjectMilestone, + ) => { + setIsProcessing(true); + try { + // Convert milestone status => status id + let statusId = 1; + for (const msStatus of objectKeys(zodMilestoneStatus.enum)) { + if (msStatus === newStatus) { + break; + } + statusId++; + } + + const refetch = () => + Promise.all([ + queryClient.invalidateQueries(["projects", networkId]), + queryClient.invalidateQueries([ + "project", + getNetworkObjectId(networkId, project.id), + ]), + ]); + + if (newStatus === "MS_COMPLETED") { + await execEscrowMethod("CompleteMilestoneAndPay", [ + project.id.toString(), + milestone.id.toString(), + ]); + await refetch(); + return; + } + + await execEscrowMethod("ChangeMilestoneStatus", [ + project.id.toString(), + milestone.id.toString(), + statusId.toString(), + ]); + await refetch(); + } finally { + setIsProcessing(false); + } + }; + + return ( + + onClose?.(milestone)}> + + + + {milestone.title} + + + + + + Status + + + + + + + + + + Priority + + + + + + + + Description + + + + + {milestone.desc} + + + + + + + + + Github link + + + + + + + {editable && ( + + + Change status + + + s.value === newStatus) || STATUSES[0] + } + selectItem={(item) => setNewStatus(item.value as MilestoneStatus)} + boxStyle={{ height: 32 }} + testID="milestone-select-new-status" + /> + + + + changeMilestoneStatus(project, milestone)} + fullWidth + size="SM" + /> + + )} + + ); +}; diff --git a/packages/screens/Projects/ProjectsDetailScreen/index.tsx b/packages/screens/Projects/ProjectsDetailScreen/index.tsx new file mode 100644 index 0000000000..715accdfb2 --- /dev/null +++ b/packages/screens/Projects/ProjectsDetailScreen/index.tsx @@ -0,0 +1,74 @@ +import React, { useState } from "react"; +import { View } from "react-native"; + +import { HeaderBackButton } from "../components/HeaderBackButton"; +import { ProjectInfo } from "../components/ProjectInfo"; +import { ProjectMilestones } from "../components/ProjectMilestones"; +import { useProject } from "../hooks/useProjects"; + +import { ScreenContainer } from "@/components/ScreenContainer"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { MilestoneDetail } from "@/screens/Projects/ProjectsDetailScreen/MilestoneDetail"; +import { ScreenFC } from "@/utils/navigation"; + +export const ProjectsDetailScreen: ScreenFC<"ProjectsDetail"> = ({ + route: { params }, +}) => { + const selectedWallet = useSelectedWallet(); + + const [selectedMilestoneId, setSelectedMilestoneId] = useState( + null, + ); + + const { data: project } = useProject(params.id); + + const selectedMilestone = project?.milestones.find( + (m) => m.id === selectedMilestoneId, + ); + + const onSelectMilestone = (id: string) => { + setSelectedMilestoneId(id); + }; + + if (!project) + return ( + } + /> + ); + + return ( + } + footerChildren={<>} + > + + + + + + + {/* Detail view ======================================================= */} + {selectedMilestone !== undefined && ( + setSelectedMilestoneId(null)} + editable={ + (project.contractor === selectedWallet?.address && + selectedMilestone.status === "MS_OPEN") || + (project.funder === selectedWallet?.address && + selectedMilestone.status === "MS_REVIEW") + } + /> + )} + + ); +}; diff --git a/packages/screens/Projects/ProjectsMakeRequestScreen/ConfirmAndSign.tsx b/packages/screens/Projects/ProjectsMakeRequestScreen/ConfirmAndSign.tsx new file mode 100644 index 0000000000..103be207f9 --- /dev/null +++ b/packages/screens/Projects/ProjectsMakeRequestScreen/ConfirmAndSign.tsx @@ -0,0 +1,338 @@ +import { useQueryClient } from "@tanstack/react-query"; +import React, { useMemo, useState } from "react"; +import { Image, View } from "react-native"; + +import gnoSVG from "../../../../assets/icons/networks/gno.svg"; +import projectSuccessPaymentPNG from "../../../../assets/project-success-payment.png"; +import ModalBase from "../../../components/modals/ModalBase"; +import useSelectedWallet from "../../../hooks/useSelectedWallet"; +import { Tag } from "../components/Milestone"; +import { useMakeRequestState } from "../hooks/useMakeRequestHook"; +import { + MilestoneRequest, + ProjectShortDescData, + ProjectTeamAndLinkData, +} from "../types"; + +import { BrandText } from "@/components/BrandText"; +import FlexRow from "@/components/FlexRow"; +import { SVG } from "@/components/SVG"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { SecondaryButton } from "@/components/buttons/SecondaryButton"; +import { SecondaryButtonOutline } from "@/components/buttons/SecondaryButtonOutline"; +import { SpacerColumn, SpacerRow } from "@/components/spacer"; +import { useFeedbacks } from "@/context/FeedbacksProvider"; +import { useBalances } from "@/hooks/useBalances"; +import { + useSelectedNetworkId, + useSelectedNetworkInfo, +} from "@/hooks/useSelectedNetwork"; +import { NetworkFeature, getNetworkFeature } from "@/networks"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { prettyPrice } from "@/utils/coins"; +import { useAppNavigation } from "@/utils/navigation"; +import { + neutral00, + neutral17, + neutral33, + neutral77, + neutralFF, +} from "@/utils/style/colors"; +import { + fontSemibold12, + fontSemibold13, + fontSemibold14, + fontSemibold16, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { tinyAddress } from "@/utils/text"; + +export const ConfirmAndSign: React.FC = () => { + const [isShowModal, setIsShowModal] = useState(false); + const [isShowConfirmModal, setIsShowConfirmModal] = useState(true); + const [isProcessing, setIsProcessing] = useState(false); + + const navigation = useAppNavigation(); + const { + projectFormData, + milestones, + teamAndLinkData: teamAndLinkFormData, + } = useMakeRequestState(); + const networkId = useSelectedNetworkId(); + + const pmFeature = getNetworkFeature( + networkId, + NetworkFeature.GnoProjectManager, + ); + + const selectedWallet = useSelectedWallet(); + const selectedNetwork = useSelectedNetworkInfo(); + const { balances } = useBalances( + selectedNetwork?.id, + selectedWallet?.address, + ); + const bal = balances?.find((b) => b.denom === pmFeature?.paymentsDenom); + + const { setToast } = useFeedbacks(); + + const { execEscrowMethod } = useEscrowContract( + networkId, + selectedWallet?.address, + ); + + const queryClient = useQueryClient(); + + const cancel = async () => { + setIsShowConfirmModal(false); + navigation.replace("Projects", { network: networkId }); + }; + + const confirmAndSign = async () => { + try { + setIsProcessing(true); + + if (!projectFormData.coverImg) { + setIsShowConfirmModal(false); + throw Error("Cover image file is required"); + } + + if (!pmFeature) { + throw Error("Project manager feature not found"); + } + + const coverImg = projectFormData.coverImg; + + // other party can't accept contract after duration expired + const expiryDuration = 24 * 60 * 60; // 1 day in seconds + + const shortDescData: ProjectShortDescData = { + name: projectFormData.name, + desc: projectFormData.description, + coverImg, + tags: projectFormData.tags || "", + }; + + const teamAndLinkData: ProjectTeamAndLinkData = { + websiteLink: teamAndLinkFormData.websiteLink, + twitterProfile: teamAndLinkFormData.twitterProfile, + discordLink: teamAndLinkFormData.discordLink, + githubLink: teamAndLinkFormData.githubLink, + teamDesc: teamAndLinkFormData.teamDesc, + }; + + const metadata = JSON.stringify({ + shortDescData, + teamAndLinkData, + }); + + const conflictHandler = projectFormData.arbitratorAddress; + + let send = ""; + // If creator = funder then we need to send all needed fund + if (projectFormData.creatorKind === "funder") { + send = totalFunding + pmFeature.paymentsDenom; + } + + const contractor = + projectFormData.creatorKind === "contractor" + ? projectFormData.creatorAddress + : ""; + const funder = + projectFormData.creatorKind === "funder" + ? projectFormData.creatorAddress + : ""; + + const args = [ + contractor, + funder, + pmFeature.paymentsDenom, + metadata, + expiryDuration.toString(), + JSON.stringify( + milestones.map((ms) => { + const req: MilestoneRequest = { + title: ms.title, + desc: ms.desc, + duration: ms.duration.toString(), + amount: ms.amount.toString(), + link: ms.link || "", + priority: ms.priority, + }; + return req; + }), + ), + conflictHandler, + ]; + console.log("executing contract creation", args); + + await execEscrowMethod("CreateContractJSON", args, send, 10_000_000); + + await queryClient.invalidateQueries(["projects"]); + + setIsShowConfirmModal(false); + setIsShowModal(true); + } catch (e) { + let msg = ""; + if (e instanceof Error) { + msg = e.message; + } else { + msg = `${e}`; + } + setIsShowConfirmModal(false); + setToast({ + title: "Error", + message: msg, + type: "error", + mode: "normal", + }); + throw e; + } finally { + setIsProcessing(false); + } + }; + + const totalFunding = useMemo(() => { + return milestones.reduce((total, m) => total + +m.amount, 0).toString(); + }, [milestones]); + + return ( + + + + You’re making the signature to validate a transaction + + + + + + + + + + + + {selectedNetwork?.displayName} + + + + {tinyAddress(selectedWallet?.address, 16)} + + + + {selectedWallet?.address && } + + + + + + + Funding + + + + {prettyPrice(networkId, totalFunding, pmFeature?.paymentsDenom)} + + + + + + Balance + + + + {prettyPrice(networkId, bal?.amount, pmFeature?.paymentsDenom)} + + + + + + + + + + + + + + + setIsShowModal(false)} + label="Successful payment" + visible={isShowModal} + width={480} + > + + + + + You have successfully created Project: {projectFormData?.name} + + + + { + setIsShowModal(false); + navigation.navigate("Projects", { network: networkId }); + }} + /> + + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsMakeRequestScreen/Footer.tsx b/packages/screens/Projects/ProjectsMakeRequestScreen/Footer.tsx new file mode 100644 index 0000000000..da53e6ceed --- /dev/null +++ b/packages/screens/Projects/ProjectsMakeRequestScreen/Footer.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import { View } from "react-native"; + +import FlexRow from "../../../components/FlexRow"; +import { PrimaryButton } from "../../../components/buttons/PrimaryButton"; +import { SecondaryButton } from "../../../components/buttons/SecondaryButton"; +import { Separator } from "../../../components/separators/Separator"; +import { layout } from "../../../utils/style/layout"; +import { useMakeRequestState } from "../hooks/useMakeRequestHook"; + +const DEFAULT_WIDTH = 120; + +export const MakeRequestFooter: React.FC<{ + onSubmit: () => void; + disableNext: boolean; + nextText?: string; + backText?: string; + width?: number; +}> = (props) => { + const { + actions: { goPrevStep }, + } = useMakeRequestState(); + + return ( + + + + + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsMakeRequestScreen/Milestones.tsx b/packages/screens/Projects/ProjectsMakeRequestScreen/Milestones.tsx new file mode 100644 index 0000000000..abfb82a4de --- /dev/null +++ b/packages/screens/Projects/ProjectsMakeRequestScreen/Milestones.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import { View } from "react-native"; + +import { MakeRequestFooter } from "./Footer"; +import { neutral17 } from "../../../utils/style/colors"; +import { layout } from "../../../utils/style/layout"; +import { MilestoneBoard } from "../components/MilestoneBoard"; +import { useMakeRequestState } from "../hooks/useMakeRequestHook"; +import { previewMilestoneForm } from "../types"; + +export const Milestones: React.FC = () => { + const { + milestones, + projectFormData: shortDescData, + actions: { goNextStep, setShortDesc }, + } = useMakeRequestState(); + + const goToReview = () => { + setShortDesc(shortDescData); + + goNextStep(); + }; + + return ( + + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsMakeRequestScreen/Preview.tsx b/packages/screens/Projects/ProjectsMakeRequestScreen/Preview.tsx new file mode 100644 index 0000000000..5d42837f47 --- /dev/null +++ b/packages/screens/Projects/ProjectsMakeRequestScreen/Preview.tsx @@ -0,0 +1,68 @@ +import React from "react"; +import { View } from "react-native"; + +import { MakeRequestFooter } from "./Footer"; +import { ProjectInfo } from "../components/ProjectInfo"; +import { ProjectMilestones } from "../components/ProjectMilestones"; +import { useMakeRequestState } from "../hooks/useMakeRequestHook"; +import { Project, previewMilestoneForm } from "../types"; + +import useSelectedWallet from "@/hooks/useSelectedWallet"; + +export const Preview: React.FC = () => { + const { + projectFormData, + milestones, + teamAndLinkData, + actions: { goNextStep }, + } = useMakeRequestState(); + + const selectedWallet = useSelectedWallet(); + + // Create project object just like when it returned from server + const project: Project = { + sender: selectedWallet?.address || "", + metadata: { + shortDescData: { + ...projectFormData, + coverImg: projectFormData.coverImg, + tags: projectFormData.tags || "", + }, + teamAndLinkData, + }, + budget: "", + funder: selectedWallet?.address || "", + contractor: "", + status: "CREATED", + id: "", + funded: false, + milestones: milestones.map(previewMilestoneForm), + contractorCandidates: [], + paymentDenom: "", + expireAt: new Date(), + funderFeedback: "", + contractorFeedback: "", + pausedBy: "", + conflictHandler: "", + handlerCandidate: "", + handlerSuggestor: "", + createdAt: new Date(), + rejectReason: "", + conflicts: [], + }; + + return ( + + + + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsMakeRequestScreen/ShortPresentation.tsx b/packages/screens/Projects/ProjectsMakeRequestScreen/ShortPresentation.tsx new file mode 100644 index 0000000000..ca8bdc18a6 --- /dev/null +++ b/packages/screens/Projects/ProjectsMakeRequestScreen/ShortPresentation.tsx @@ -0,0 +1,248 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { View } from "react-native"; + +import { MakeRequestFooter } from "./Footer"; +import addSVG from "../../../../assets/icons/add.svg"; +import { BrandText } from "../../../components/BrandText"; +import { PrimaryButtonOutline } from "../../../components/buttons/PrimaryButtonOutline"; +import { FileUploader } from "../../../components/fileUploader"; +import { RoundedGradientImage } from "../../../components/images/RoundedGradientImage"; +import { TextInputCustom } from "../../../components/inputs/TextInputCustom"; +import { SpacerColumn } from "../../../components/spacer"; +import { useNameSearch } from "../../../hooks/search/useNameSearch"; +import { useSelectedNetworkId } from "../../../hooks/useSelectedNetwork"; +import useSelectedWallet from "../../../hooks/useSelectedWallet"; +import { IMAGE_MIME_TYPES } from "../../../utils/mime"; +import { errorColor, neutral77, neutralA3 } from "../../../utils/style/colors"; +import { fontSemibold14, fontSemibold20 } from "../../../utils/style/fonts"; +import { TNSResult } from "../components/TNSResult"; +import { + useMakeRequestState, + zodProjectFormData, +} from "../hooks/useMakeRequestHook"; + +import { LoaderFullScreen } from "@/components/loaders/LoaderFullScreen"; +import { useIpfs } from "@/hooks/useIpfs"; +import { ButtonsGroup } from "@/screens/Projects/components/ButtonsGroup"; + +export const ShortPresentation: React.FC = () => { + const { + actions: { goNextStep, setShortDesc }, + projectFormData: shortDescData, + } = useMakeRequestState(); + const selectedWallet = useSelectedWallet(); + const caller = selectedWallet?.address; + const selectedNetworkId = useSelectedNetworkId(); + const [searchTNSText, setSearchTNSText] = useState(""); + const [isTNSVisible, setIsTNSVisible] = useState(false); + const { handleSubmit, formState, setValue, watch, setError } = useForm({ + resolver: zodResolver(zodProjectFormData), + defaultValues: shortDescData, + }); + const { errors } = formState; + const values = watch(); + const { uploadToIPFS } = useIpfs(); + const [isUploadingCover, setIsUploadingCover] = useState(false); + useEffect(() => { + if (!caller) { + // TODO: would be better to not allow this corner case, aka do something smarter when no wallet is connected + return; + } + setValue("creatorAddress", caller); + }, [setValue, caller]); + + const { names } = useNameSearch({ + networkId: selectedNetworkId, + input: searchTNSText, + limit: 12, + }); + + if (!caller) { + return null; + } + + return ( + + + + Grant details + + + + + Information about your Grant + + + + + + + I'm * + + + + + { + if (selectedId === 0) { + setValue("creatorKind", "contractor"); + } else { + setValue("creatorKind", "funder"); + } + }} + /> + + + + + + { + setSearchTNSText(text); + setIsTNSVisible(true); + setValue("targetAddress", text); + }} + value={values.targetAddress} + error={errors.targetAddress?.message} + /> + + 0} + networkId={selectedNetworkId} + names={names} + onSelected={(name) => { + setIsTNSVisible(false); + setValue("targetAddress", name); + }} + /> + + + + + setValue("name", val)} + value={values.name} + error={errors.name?.message} + /> + + + + setValue("description", val)} + value={values.description} + error={errors.description?.message} + /> + + + + setValue("arbitratorAddress", val)} + value={values.arbitratorAddress} + /> + + + + + Cover Image * + + + + + { + setIsUploadingCover(true); + try { + if (files[0].fileType !== "image") { + setError("coverImg", { message: "file is not an image" }); + return; + } + const web3URI = await uploadToIPFS(selectedWallet.userId, files[0]); + setValue("coverImg", web3URI); + } finally { + setIsUploadingCover(false); + } + }} + mimeTypes={IMAGE_MIME_TYPES} + > + {({ onPress }) => ( + + )} + + + + + {!!errors.coverImg && ( + + {errors.coverImg.message} + + )} + + + {!!values.coverImg && ( + + )} + + + + + setValue("tags", val)} + value={values.tags || ""} + error={errors.tags?.message} + /> + + { + setShortDesc(submitValues); + goNextStep(); + })} + /> + + ); +}; diff --git a/packages/screens/Projects/ProjectsMakeRequestScreen/TeamAndLinks.tsx b/packages/screens/Projects/ProjectsMakeRequestScreen/TeamAndLinks.tsx new file mode 100644 index 0000000000..70813838fe --- /dev/null +++ b/packages/screens/Projects/ProjectsMakeRequestScreen/TeamAndLinks.tsx @@ -0,0 +1,159 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import React from "react"; +import { useForm } from "react-hook-form"; +import { View } from "react-native"; + +import { MakeRequestFooter } from "./Footer"; +import { BrandText } from "../../../components/BrandText"; +import { TextInputCustom } from "../../../components/inputs/TextInputCustom"; +import { Separator } from "../../../components/separators/Separator"; +import { SpacerColumn } from "../../../components/spacer"; +import { neutral55, neutral77, neutralA3 } from "../../../utils/style/colors"; +import { + fontSemibold13, + fontSemibold14, + fontSemibold20, +} from "../../../utils/style/fonts"; +import { layout } from "../../../utils/style/layout"; +import { + useMakeRequestState, + zodProjectTeamAndLinkFormData, +} from "../hooks/useMakeRequestHook"; + +export const TeamAndLinks: React.FC = () => { + const { + actions: { goNextStep, setTeamAndLink }, + teamAndLinkData, + } = useMakeRequestState(); + + const { handleSubmit, formState, setValue, watch } = useForm({ + resolver: zodResolver(zodProjectTeamAndLinkFormData), + defaultValues: teamAndLinkData, + }); + + const { errors } = formState; + const values = watch(); + + const submit = handleSubmit((values) => { + setTeamAndLink(values); + goNextStep(); + }); + + return ( + + Links + + + + + Your Grant useful links + + + + + setValue("websiteLink", val)} + value={values.websiteLink} + error={errors.websiteLink?.message} + /> + + + + setValue("twitterProfile", val)} + value={values.twitterProfile} + error={errors.twitterProfile?.message} + /> + + + + setValue("discordLink", val)} + value={values.discordLink} + error={errors.discordLink?.message} + /> + + + + setValue("githubLink", val)} + value={values.githubLink} + error={errors.githubLink?.message} + /> + + + + Links + + + + + Your Grant useful links + + + + + + Describe your team: * + + + + 1. How many core members are you? ( Working on the project daily ) + + + 2. Past accomplishments or projects? + + + 3. Please add all relevant links for all your members. + + + + + setValue("teamDesc", val)} + value={values.teamDesc} + error={errors.teamDesc?.message} + /> + + + + + Team links and attachments + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsMakeRequestScreen/index.tsx b/packages/screens/Projects/ProjectsMakeRequestScreen/index.tsx new file mode 100644 index 0000000000..9df7b905c8 --- /dev/null +++ b/packages/screens/Projects/ProjectsMakeRequestScreen/index.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { View } from "react-native"; + +import { ConfirmAndSign } from "./ConfirmAndSign"; +import { Milestones } from "./Milestones"; +import { Preview } from "./Preview"; +import { ShortPresentation } from "./ShortPresentation"; +import { TeamAndLinks } from "./TeamAndLinks"; +import { ScreenContainer } from "../../../components/ScreenContainer"; +import { SpacerColumn } from "../../../components/spacer"; +import { NetworkKind } from "../../../networks"; +import { ScreenFC } from "../../../utils/navigation"; +import { Breadcrumb } from "../components/Breadcrumb"; +import { HeaderBackButton } from "../components/HeaderBackButton"; +import { useMakeRequestState } from "../hooks/useMakeRequestHook"; + +const renderStep = (stepIndice: number) => { + if (stepIndice === 1) return ; + if (stepIndice === 2) return ; + if (stepIndice === 3) return ; + if (stepIndice === 4) return ; + if (stepIndice === 5) return ; +}; + +export const ProjectsMakeRequestScreen: ScreenFC< + "ProjectsMakeRequest" +> = () => { + const { stepIndice } = useMakeRequestState(); + + return ( + } + > + + + + + {/* Main view============================================================ */} + {renderStep(stepIndice)} + + ); +}; diff --git a/packages/screens/Projects/ProjectsManagerScreen/ContractorCandidates.tsx b/packages/screens/Projects/ProjectsManagerScreen/ContractorCandidates.tsx new file mode 100644 index 0000000000..9bdbea2edc --- /dev/null +++ b/packages/screens/Projects/ProjectsManagerScreen/ContractorCandidates.tsx @@ -0,0 +1,177 @@ +import { useQueryClient } from "@tanstack/react-query"; +import React, { useState } from "react"; +import { TouchableOpacity, View } from "react-native"; + +import FlexRow from "../../../components/FlexRow"; +import useSelectedWallet from "../../../hooks/useSelectedWallet"; +import { ProjectStatusTag } from "../components/ProjectStatusTag"; +import { useProjects } from "../hooks/useProjects"; +import { Project } from "../types"; + +import { BrandText } from "@/components/BrandText"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { RoundedGradientImage } from "@/components/images/RoundedGradientImage"; +import { TableCell } from "@/components/table/TableCell"; +import { TableHeader } from "@/components/table/TableHeader"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import { + getNetworkObjectId, + getUserId, + parseNetworkObjectId, +} from "@/networks"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { useAppNavigation } from "@/utils/navigation"; +import { neutral33, neutralFF } from "@/utils/style/colors"; +import { fontSemibold13 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +export const ContractorCandidates: React.FC<{ networkId: string }> = ({ + networkId, +}) => { + const selectedWallet = useSelectedWallet(); + + const { projects } = useProjects(networkId, { + byCandidatesForFunder: { funder: selectedWallet?.address || "" }, + }); + + return ( + + + {projects.map((project) => ( + + ))} + + ); +}; + +const TABLE_COLS = { + name: { + label: "Project name", + flex: 3, + }, + status: { + label: "Status", + flex: 1, + }, + candidates: { + label: "Candidates", + flex: 3, + }, +}; + +const ProjectRow: React.FC<{ networkId: string; project: Project }> = ({ + networkId, + project, +}) => { + const navigation = useAppNavigation(); + + if (!project.contractorCandidates.length) return null; + + return ( + + {/* === Name === */} + + + + { + if (!project.id) { + return; + } + navigation.navigate("ProjectsDetail", { id: project.id }); + }} + > + + {project.metadata?.shortDescData?.name} + + + + + {/* === Status === */} + + + + + + + {[ + project.contractorCandidates, + // project.contractorCandidates, + // project.contractorCandidates, + ] + .flatMap((c) => c) + .map((c, idx) => ( + + ))} + + + + ); +}; + +type CandidateProps = { + projectId: string; + candidate: string; +}; + +const Candidate: React.FC = ({ projectId, candidate }) => { + const [network, localProjectId] = parseNetworkObjectId(projectId); + const networkId = network?.id; + + const selectedWallet = useSelectedWallet(); + + const [isProcessing, setIsProcessing] = useState(false); + + const queryClient = useQueryClient(); + + const { execEscrowMethod } = useEscrowContract( + networkId, + selectedWallet?.address, + ); + + const acceptCandidate = async () => { + setIsProcessing(true); + await execEscrowMethod("AcceptContractor", [localProjectId, candidate]); + await Promise.all([ + queryClient.invalidateQueries(["project", projectId]), + queryClient.invalidateQueries(["projects", networkId]), + ]); + setIsProcessing(false); + }; + + return ( + + + acceptCandidate()} + /> + + ); +}; diff --git a/packages/screens/Projects/ProjectsManagerScreen/MilestonesUpdateManager.tsx b/packages/screens/Projects/ProjectsManagerScreen/MilestonesUpdateManager.tsx new file mode 100644 index 0000000000..1e3cc1d9c7 --- /dev/null +++ b/packages/screens/Projects/ProjectsManagerScreen/MilestonesUpdateManager.tsx @@ -0,0 +1,181 @@ +import React from "react"; +import { View } from "react-native"; +import { TouchableOpacity } from "react-native-gesture-handler"; + +import githubSVG from "../../../../assets/icons/github.svg"; +import FlexRow from "../../../components/FlexRow"; +import useSelectedWallet from "../../../hooks/useSelectedWallet"; +import { MilestonePriorityTag } from "../components/MilestonePriorityTag"; +import { MilestoneStatusTag } from "../components/MilestoneStatusTag"; +import { useProjects } from "../hooks/useProjects"; +import { ProjectMilestone } from "../types"; + +import { BrandText } from "@/components/BrandText"; +import { Link } from "@/components/Link"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { SocialButton } from "@/components/buttons/SocialButton"; +import { Separator } from "@/components/separators/Separator"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { getNetworkObjectId } from "@/networks"; +import { useAppNavigation } from "@/utils/navigation"; +import { + neutral00, + neutral17, + neutral77, + neutralA3, + neutralFF, +} from "@/utils/style/colors"; +import { + fontSemibold13, + fontSemibold14, + fontSemibold16, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +export const MilestonesUpdateManager: React.FC = () => { + const navigation = useAppNavigation(); + const networkId = useSelectedNetworkId(); + const selectedWallet = useSelectedWallet(); + + // TODO: Support to default limit projects for now, make this more dynamic latter + const { projects, isLoading } = useProjects(networkId, { + byFunder: { funder: selectedWallet?.address || "" }, + }); + + if (isLoading || !selectedWallet?.address) return null; + + return ( + <> + {projects.map((project) => + (project.milestones || []) + .filter((m) => m.status === "MS_REVIEW") + .map((milestone: ProjectMilestone, milestoneIdx: number) => { + return ( + <> + + Project: {project.metadata?.shortDescData?.name} + + + + + + {project.metadata?.shortDescData?.name} + + + + {milestone.title} + + + + + + + + Builder: @{project.contractor} + + + + Funder: @{project.funder} + + + + + + + + Status + + + + + + + + + + Priority + + + + + + + + + + + + { + if (!project.id) return; + navigation.navigate("ProjectsCompleteMilestone", { + projectId: getNetworkObjectId(networkId, project.id), + milestoneId: milestone.id, + }); + }} + > + + Approve + + + + + + ); + }), + )} + + ); +}; diff --git a/packages/screens/Projects/ProjectsManagerScreen/MyProjectsManager.tsx b/packages/screens/Projects/ProjectsManagerScreen/MyProjectsManager.tsx new file mode 100644 index 0000000000..512c1a0eb3 --- /dev/null +++ b/packages/screens/Projects/ProjectsManagerScreen/MyProjectsManager.tsx @@ -0,0 +1,190 @@ +import moment from "moment"; +import React, { useMemo, useState } from "react"; +import { TouchableOpacity, View } from "react-native"; + +import useSelectedWallet from "../../../hooks/useSelectedWallet"; +import { ProjectStatusTag } from "../components/ProjectStatusTag"; +import { ProjectsStatusFilterButtons } from "../components/ProjectsStatusFilterButtons"; +import { ProjectFilter, useProjects } from "../hooks/useProjects"; +import { ContractStatusFilter, Project } from "../types"; +import { getProjectStats } from "../utils"; + +import { BrandText } from "@/components/BrandText"; +import { ProgressLine } from "@/components/ProgressLine"; +import { RoundedGradientImage } from "@/components/images/RoundedGradientImage"; +import { SpacerColumn } from "@/components/spacer"; +import { TableCell } from "@/components/table/TableCell"; +import { TableHeader } from "@/components/table/TableHeader"; +import { TableRow } from "@/components/table/TableRow"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { getNetworkObjectId, getUserId } from "@/networks"; +import { prettyPrice } from "@/utils/coins"; +import { useAppNavigation } from "@/utils/navigation"; +import { neutralFF } from "@/utils/style/colors"; +import { fontSemibold13 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +export const MyProjectsManager: React.FC<{ + type: ProjectType; +}> = ({ type }) => { + const networkId = useSelectedNetworkId(); + const selectedWallet = useSelectedWallet(); + + let filter: ProjectFilter; + switch (type) { + case "myInvestments": { + filter = { byFunder: { funder: selectedWallet?.address || "" } }; + break; + } + case "myProjects": { + filter = { byContractor: { contractor: selectedWallet?.address || "" } }; + } + } + const { projects } = useProjects(networkId, filter); + + const [statusFilter, setStatusFilter] = useState("ALL"); + + const filteredProjects = useMemo(() => { + if (!selectedWallet?.address) return []; + + return projects.filter( + (p) => statusFilter === "ALL" || p.status === statusFilter, + ); + }, [projects, statusFilter, selectedWallet?.address]); + + return ( + + + + + + + {filteredProjects.map((project) => ( + + ))} + + ); +}; + +const getTableCols = (projectType: ProjectType) => { + return { + name: { + label: "Project name", + flex: 2.5, + }, + status: { + label: "Status", + flex: 1, + }, + manager: { + label: projectType === "myProjects" ? "Funder" : "Contractor", + flex: 2.5, + }, + milestones: { + label: "Milestones", + flex: 2, + }, + grant: { + label: "Grant", + flex: 2, + }, + creationDate: { + label: "Creation date", + flex: 1, + }, + }; +}; + +const defaultCols = getTableCols("myProjects"); + +type ProjectType = "myInvestments" | "myProjects"; + +const ProjectRow: React.FC<{ project: Project; projectType: ProjectType }> = ({ + project, + projectType, +}) => { + const stats = getProjectStats(project); + const navigation = useAppNavigation(); + const networkId = useSelectedNetworkId(); + + return ( + + {/* === Name === */} + + + + { + if (!project.id) { + return; + } + navigation.navigate("ProjectsDetail", { + id: getNetworkObjectId(networkId, project.id), + }); + }} + > + + {project.metadata?.shortDescData?.name} + + + + + {/* === Status === */} + + + + + {/* === Manager === */} + + {projectType === "myProjects" && ( + + )} + {projectType === "myInvestments" && ( + + )} + + + {/* === Milestones === */} + + + {stats.completed}/{stats.total} + + + + + {/* === Grant === */} + + + {prettyPrice(networkId, project.budget, project.paymentDenom)} + + + + {/* === Creation date === */} + + + {moment(project.createdAt).format("L")} + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsManagerScreen/Requests.tsx b/packages/screens/Projects/ProjectsManagerScreen/Requests.tsx new file mode 100644 index 0000000000..4ad1cdb183 --- /dev/null +++ b/packages/screens/Projects/ProjectsManagerScreen/Requests.tsx @@ -0,0 +1,342 @@ +import { Link } from "@react-navigation/native"; +import React, { useState } from "react"; +import { View } from "react-native"; + +import githubSVG from "../../../../assets/icons/github.svg"; +import FlexRow from "../../../components/FlexRow"; +import ModalBase from "../../../components/modals/ModalBase"; +import useSelectedWallet from "../../../hooks/useSelectedWallet"; +import { ProjectStatusTag } from "../components/ProjectStatusTag"; +import { ProjectFilter, useProjects } from "../hooks/useProjects"; +import { Project } from "../types"; + +import { BrandText } from "@/components/BrandText"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { PrimaryButtonOutline } from "@/components/buttons/PrimaryButtonOutline"; +import { SocialButton } from "@/components/buttons/SocialButton"; +import { TextInputCustom } from "@/components/inputs/TextInputCustom"; +import { Separator } from "@/components/separators/Separator"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { getUserId } from "@/networks"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { useUtils } from "@/screens/Projects/hooks/useUtils"; +import { prettyPrice } from "@/utils/coins"; +import { useAppNavigation } from "@/utils/navigation"; +import { + neutral17, + neutralA3, + primaryColor, + redDefault, +} from "@/utils/style/colors"; +import { fontSemibold13, fontSemibold14 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +type RequestType = "requestsByBuilders" | "requestsByInvestors"; + +const Spacing = () => { + return ( + + ); +}; + +const RequestItem: React.FC<{ + project: Project & { contractorCandidate?: string }; + networkId: string; + walletAddress: string; + requestType: RequestType; +}> = ({ project, networkId, walletAddress, requestType }) => { + const navigation = useAppNavigation(); + + const [isProcessing, setIsProcessing] = useState(false); + const [isShowModal, setIsShowModal] = useState(false); + const [rejectReason, setRejectReason] = useState(""); + const { mustGetValue } = useUtils(); + + const { execEscrowMethod } = useEscrowContract(networkId, walletAddress); + + const githubLink = project.metadata?.teamAndLinkData?.githubLink; + + const gotoProjectDetail = (project: Project) => { + if (!project.id) { + return; + } + navigation.navigate("ProjectsDetail", { id: project.id }); + }; + + const acceptContractor = async () => { + setIsProcessing(true); + + const candidate = mustGetValue( + project.contractorCandidate, + "contractor candidate", + ); + + await execEscrowMethod("AcceptContractor", [ + project?.id?.toString(), + candidate, + ]); + + setIsProcessing(false); + }; + + const acceptContract = async (project: Project) => { + setIsProcessing(true); + await execEscrowMethod("AcceptContract", [project?.id?.toString()]); + setIsProcessing(false); + }; + + const rejectContract = async (project: Project, rejectReason: string) => { + setIsProcessing(true); + await execEscrowMethod("RejectContract", [ + project?.id?.toString(), + rejectReason, + ]); + setIsProcessing(false); + }; + + return ( + <> + + + + gotoProjectDetail(project)} + style={fontSemibold14} + > + {project.metadata?.shortDescData?.name} + + + gotoProjectDetail(project)} + style={[fontSemibold14, { color: neutralA3 }]} + > + {project.metadata?.shortDescData?.desc} + + + + + + + + Grant + + + + {prettyPrice(networkId, project.budget, project.paymentDenom)} + + + + + + + {requestType === "requestsByBuilders" && ( + + )} + {requestType === "requestsByInvestors" && ( + + Investor{" "} + + )} + + + {requestType === "requestsByBuilders" && project.contractor} + {requestType === "requestsByInvestors" && project.funder} + + + + + + + + Milestones + + + + {project.milestones.length} + + + + + + + + + + {githubLink && ( + <> + + + + + + + + + )} + + + {project.status === "CREATED" && ( + <> + setIsShowModal(true)} + /> + + acceptContract(project) + } + /> + + )} + + {project.status === "REJECTED" && ( + + {project.rejectReason} + + )} + + + + + setIsShowModal(false)} + label="Reason for rejection" + visible={isShowModal} + width={480} + > + + + rejectContract(project, rejectReason)} + /> + + + ); +}; + +export const Requests: React.FC<{ + type: RequestType; +}> = ({ type }) => { + const networkId = useSelectedNetworkId(); + const selectedWallet = useSelectedWallet(); + + // FIXME: add new filters + + let filter: ProjectFilter; + switch (type) { + case "requestsByBuilders": { + filter = { byFunder: { funder: selectedWallet?.address || "" } }; + break; + } + case "requestsByInvestors": { + filter = { byContractor: { contractor: selectedWallet?.address || "" } }; + } + } + + const { projects } = useProjects(networkId, filter); + + if (!selectedWallet?.address) { + return null; + } + + let adjustedProjects: (Project & { contractorCandidate?: string })[] = + type === "requestsByBuilders" + ? projects.filter((p) => p.sender === p.funder) + : projects.filter((p) => p.sender === p.contractor); + + if (type === "requestsByBuilders") { + // expand by contractor + adjustedProjects = adjustedProjects.reduce( + (acc, p) => { + if (!p.contractorCandidates) { + return acc; + } + const newProjects = p.contractorCandidates.map((c) => ({ + ...p, + contractorCandidate: c, + })); + return [...acc, ...newProjects]; + }, + [] as (Project & { contractorCandidate?: string })[], + ); + } + + return ( + <> + {adjustedProjects.map((project) => { + return ( + + ); + })} + + ); +}; diff --git a/packages/screens/Projects/ProjectsManagerScreen/index.tsx b/packages/screens/Projects/ProjectsManagerScreen/index.tsx new file mode 100644 index 0000000000..7b85067699 --- /dev/null +++ b/packages/screens/Projects/ProjectsManagerScreen/index.tsx @@ -0,0 +1,99 @@ +import React, { useMemo } from "react"; +import { View } from "react-native"; + +import { MilestonesUpdateManager } from "./MilestonesUpdateManager"; +import { MyProjectsManager } from "./MyProjectsManager"; +import { Requests } from "./Requests"; +import { TabOption, ViewKey } from "./types"; +import { HeaderBackButton } from "../components/HeaderBackButton"; + +import { BrandText } from "@/components/BrandText"; +import { FlexRow } from "@/components/FlexRow"; +import { ScreenContainer } from "@/components/ScreenContainer"; +import { Tabs } from "@/components/tabs/Tabs"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { NetworkKind } from "@/networks"; +import { ContractorCandidates } from "@/screens/Projects/ProjectsManagerScreen/ContractorCandidates"; +import { ScreenFC, useAppNavigation } from "@/utils/navigation"; +import { neutral33 } from "@/utils/style/colors"; +import { fontSemibold14, fontSemibold28 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { arrayIncludes, objectKeys } from "@/utils/typescript"; + +export const ProjectsManagerScreen: ScreenFC<"ProjectsManager"> = ({ + route: { params }, +}) => { + const networkId = useSelectedNetworkId(); + const navigation = useAppNavigation(); + const viewFromParams = params.view; + + const { tabOptions, managerTypes } = useMemo(() => { + const tabOptions: TabOption = { + myInvestments: { + name: "My investments", + component: , + }, + myProjects: { + name: "My projects", + component: , + }, + milestonesUpdates: { + name: "Reviews", + component: , + }, + requestsByBuilders: { + name: "Contractor candidates", + component: , + }, + requestsByInvestors: { + name: "Requests by investors", + component: , + }, + }; + return { tabOptions, managerTypes: objectKeys(tabOptions) }; + }, [networkId]); + + const view: ViewKey = useMemo(() => { + return arrayIncludes(managerTypes, viewFromParams) + ? viewFromParams + : "myInvestments"; + }, [managerTypes, viewFromParams]); + + return ( + } + > + + {tabOptions[view].name} + + + navigation.navigate("ProjectsManager", { view: tab }) + } + tabTextStyle={fontSemibold14} + /> + + + + {tabOptions[view].component} + + + ); +}; diff --git a/packages/screens/Projects/ProjectsManagerScreen/types.ts b/packages/screens/Projects/ProjectsManagerScreen/types.ts new file mode 100644 index 0000000000..07496ffb40 --- /dev/null +++ b/packages/screens/Projects/ProjectsManagerScreen/types.ts @@ -0,0 +1,17 @@ +import React from "react"; + +export type ViewKey = + | "myInvestments" + | "myProjects" + | "milestonesUpdates" + | "requestsByBuilders" + | "requestsByInvestors"; + +type ViewData = { + name: string; + component: React.ReactNode; +}; + +export type TabOption = { + [key in ViewKey]: ViewData; +}; diff --git a/packages/screens/Projects/ProjectsPaymentScreen.tsx b/packages/screens/Projects/ProjectsPaymentScreen.tsx new file mode 100644 index 0000000000..6d73bffff8 --- /dev/null +++ b/packages/screens/Projects/ProjectsPaymentScreen.tsx @@ -0,0 +1,248 @@ +import moment from "moment"; +import React, { useState } from "react"; +import { View } from "react-native"; +import { SvgProps } from "react-native-svg"; + +import { HeaderBackButton } from "./components/HeaderBackButton"; +import { Tag } from "./components/Milestone"; +import { useProject } from "./hooks/useProjects"; +import discordSVG from "../../../assets/icons/discord.svg"; +import githubSVG from "../../../assets/icons/github.svg"; +import twitterSVG from "../../../assets/icons/twitter.svg"; +import websiteSVG from "../../../assets/icons/website.svg"; +import { BrandText } from "../../components/BrandText"; +import FlexRow from "../../components/FlexRow"; +import { ScreenContainer } from "../../components/ScreenContainer"; +import { TertiaryBox } from "../../components/boxes/TertiaryBox"; +import { PrimaryButton } from "../../components/buttons/PrimaryButton"; +import { PrimaryButtonOutline } from "../../components/buttons/PrimaryButtonOutline"; +import { SocialButton } from "../../components/buttons/SocialButton"; +import { RoundedGradientImage } from "../../components/images/RoundedGradientImage"; +import { TextInputCustom } from "../../components/inputs/TextInputCustom"; +import { Separator } from "../../components/separators/Separator"; +import { SpacerColumn, SpacerRow } from "../../components/spacer"; +import { ScreenFC } from "../../utils/navigation"; +import { + neutral17, + neutral77, + neutralA3, + neutralFF, + primaryColor, + redDefault, +} from "../../utils/style/colors"; +import { + fontSemibold13, + fontSemibold14, + fontSemibold20, + fontSemibold28, +} from "../../utils/style/fonts"; +import { layout } from "../../utils/style/layout"; + +const CustomSocialButton: React.FC<{ + text: string; + iconSvg: React.FC; +}> = ({ text, iconSvg }) => { + return ( + + ); +}; + +export const ProjectsPaymentScreen: ScreenFC<"ProjectsPayment"> = ({ + route: { params }, +}) => { + const { projectId, milestoneId } = params; + + const { data: project } = useProject(projectId); + const milestone = (project?.milestones || []).find( + (_, idx) => idx === +milestoneId, + ); + + const [report, setReport] = useState(""); + + if (!milestone) return null; + + return ( + }> + + + + {milestone.title} + + + + + + {/* Left Col ==========================================================*/} + + + + {milestone.title} + + + + + + {project?.metadata?.shortDescData?.desc} + + + + + + + + + Tags: + + + {project?.metadata?.shortDescData?.tags?.split(",").map((tag) => { + return ( + + ); + })} + + + + + + + + + + + + Report input: + + + + + setReport(text)} + label="" + name="name" + placeholder="Enter details here..." + hideLabel + multiline + noBrokenCorners + containerStyle={{ width: "100%" }} + textInputStyle={{ height: 80 }} + /> + + + + + + + + + + {/* Right Col =========================================================*/} + + + + Grant + + + $50K + + + + + + + Status + + + + + + + + + Created by: + + + + @0x17dfsdvgsd98fsbsd9b8sd + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/screens/Projects/ProjectsScreen.tsx b/packages/screens/Projects/ProjectsScreen.tsx new file mode 100644 index 0000000000..a7ee5af32b --- /dev/null +++ b/packages/screens/Projects/ProjectsScreen.tsx @@ -0,0 +1,104 @@ +import React, { useState } from "react"; +import { View } from "react-native"; + +import { ProjectBox } from "./components/ProjectBox"; +import { ProjectsStatusFilterButtons } from "./components/ProjectsStatusFilterButtons"; +import { useProjects } from "./hooks/useProjects"; +import { ContractStatusFilter } from "./types"; +import { BrandText } from "../../components/BrandText"; +import { FlexRow } from "../../components/FlexRow"; +import { ScreenContainer } from "../../components/ScreenContainer"; +import { SimpleButton } from "../../components/buttons/SimpleButton"; +import { Separator } from "../../components/separators/Separator"; +import { SpacerColumn, SpacerRow } from "../../components/spacer"; +import { useSelectedNetworkId } from "../../hooks/useSelectedNetwork"; +import { NetworkKind, getNetwork } from "../../networks"; +import { ScreenFC, useAppNavigation } from "../../utils/navigation"; +import { primaryColor, secondaryColor } from "../../utils/style/colors"; +import { fontSemibold20, fontSemibold28 } from "../../utils/style/fonts"; +import { layout } from "../../utils/style/layout"; + +import { GridList } from "@/components/layout/GridList"; +import { useForceNetworkSelection } from "@/hooks/useForceNetworkSelection"; +import { joinElements } from "@/utils/react"; + +export const ProjectsScreen: ScreenFC<"Projects"> = ({ route: { params } }) => { + const network = params?.network; + useForceNetworkSelection(network); + const networkId = useSelectedNetworkId(); + const inputNetwork = getNetwork(network); + const { projects, fetchNextPage } = useProjects(networkId); + + const navigation = useAppNavigation(); + + const gotoProjectsManager = () => { + navigation.navigate("ProjectsManager", { view: "myInvestments" }); + }; + + const gotoCreateGrant = () => { + navigation.navigate("ProjectsMakeRequest", { step: 1 }); + }; + + const [statusFilter, setStatusFilter] = useState("ALL"); + + const topRightElems = [ + , + , + ]; + + return ( + } + headerChildren={Projects} + > + + + + Projects + + {joinElements(topRightElems, )} + + + + + + + } + ListFooterComponent={} + data={projects} + keyExtractor={(project) => project.id} + minElemWidth={400} + onEndReached={() => fetchNextPage()} + renderItem={({ item: project }, elemWidth) => { + return ( + + ); + }} + /> + + ); +}; diff --git a/packages/screens/Projects/components/Breadcrumb.tsx b/packages/screens/Projects/components/Breadcrumb.tsx new file mode 100644 index 0000000000..bb6e7ec02b --- /dev/null +++ b/packages/screens/Projects/components/Breadcrumb.tsx @@ -0,0 +1,132 @@ +import React, { Fragment } from "react"; +import { StyleProp, ViewStyle } from "react-native"; + +import { BrandText } from "../../../components/BrandText"; +import FlexRow from "../../../components/FlexRow"; +import { TertiaryBox } from "../../../components/boxes/TertiaryBox"; +import { + neutral00, + neutral17, + neutral22, + neutral77, + neutralFF, + primaryColor, +} from "../../../utils/style/colors"; +import { fontSemibold14, fontSemibold16 } from "../../../utils/style/fonts"; +import { layout } from "../../../utils/style/layout"; +import { useMakeRequestState } from "../hooks/useMakeRequestHook"; + +const Step: React.FC<{ + indice: number; + text: string; + active?: boolean; + disabled?: boolean; + onPress: (step: number) => void; +}> = ({ indice, text, active, disabled, onPress }) => { + return ( + + onPress(indice)} + style={[ + fontSemibold14, + { + color: disabled ? neutral77 : neutral00, + backgroundColor: active + ? primaryColor + : disabled + ? neutral22 + : neutralFF, + borderRadius: 100, + width: 32, + height: 32, + justifyContent: "center", + alignItems: "center", + display: "flex", + marginRight: layout.spacing_x2, + }, + ]} + > + {indice} + + + onPress(indice)} + numberOfLines={1} + style={[ + fontSemibold14, + { + color: active ? primaryColor : disabled ? neutral77 : neutralFF, + }, + ]} + > + {text} + + + ); +}; + +const Seperator = () => { + return ( + + {">"} + + ); +}; + +const STEPS = [ + "Short presentation", + "Team and links", + "Milestones", + "Preview", + "Confirm and Sign", +]; + +export const Breadcrumb: React.FC<{ + stepIndice?: number; + containerStyle?: StyleProp; +}> = ({ stepIndice = 1, containerStyle }) => { + const { + stepIndice: currentStepIndice, + actions: { gotoStep }, + } = useMakeRequestState(); + + // We can only goto passed steps + const gotoValidStep = (targetStepIndice: number) => { + if (targetStepIndice <= currentStepIndice) { + gotoStep(targetStepIndice); + } + }; + + return ( + + + {STEPS.map((step, idx) => { + return ( + + + {idx + 1 < STEPS.length && } + + ); + })} + + + ); +}; diff --git a/packages/screens/Projects/components/ButtonsGroup.tsx b/packages/screens/Projects/components/ButtonsGroup.tsx new file mode 100644 index 0000000000..2e5243f040 --- /dev/null +++ b/packages/screens/Projects/components/ButtonsGroup.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { View } from "react-native"; + +import { SimpleButton } from "@/components/buttons/SimpleButton"; +import { + neutral00, + neutral33, + primaryColor, + secondaryColor, +} from "@/utils/style/colors"; +import { layout } from "@/utils/style/layout"; + +type ButtonsGroupType = { + texts: string[]; + selectedId: number; + onChange?: (selectedId: number) => void; + size?: "XS" | "SM" | "M" | "XL"; +}; + +export const ButtonsGroup: React.FC = ({ + texts, + selectedId, + onChange, + size = "M", +}) => { + return ( + + {texts.map((text, btnId) => { + return ( + onChange?.(btnId)} + /> + ); + })} + + ); +}; diff --git a/packages/screens/Projects/components/FundProjectModal.tsx b/packages/screens/Projects/components/FundProjectModal.tsx new file mode 100644 index 0000000000..b83eddfd28 --- /dev/null +++ b/packages/screens/Projects/components/FundProjectModal.tsx @@ -0,0 +1,184 @@ +import { useQueryClient } from "@tanstack/react-query"; +import React, { useMemo, useState } from "react"; +import { View } from "react-native"; + +import gnoSVG from "@/assets/icons/networks/gno.svg"; +import { BrandText } from "@/components/BrandText"; +import FlexRow from "@/components/FlexRow"; +import { SVG } from "@/components/SVG"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { SecondaryButton } from "@/components/buttons/SecondaryButton"; +import ModalBase from "@/components/modals/ModalBase"; +import { SpacerColumn, SpacerRow } from "@/components/spacer"; +import { useBalances } from "@/hooks/useBalances"; +import { + useSelectedNetworkId, + useSelectedNetworkInfo, +} from "@/hooks/useSelectedNetwork"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { + NetworkFeature, + getNetworkFeature, + getNetworkObjectId, +} from "@/networks"; +import { Tag } from "@/screens/Projects/components/Milestone"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { Project } from "@/screens/Projects/types"; +import { prettyPrice } from "@/utils/coins"; +import { neutral17, neutral77 } from "@/utils/style/colors"; +import { + fontSemibold12, + fontSemibold13, + fontSemibold14, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { tinyAddress } from "@/utils/text"; + +type FundProjectModalProps = { + isVisible: boolean; + onClose: () => void; + project: Project; +}; + +export const FundProjectModal: React.FC = ({ + isVisible, + onClose, + project, +}) => { + const networkId = useSelectedNetworkId(); + const selectedWallet = useSelectedWallet(); + const selectedNetwork = useSelectedNetworkInfo(); + + const pmFeature = getNetworkFeature( + networkId, + NetworkFeature.GnoProjectManager, + ); + + const { balances } = useBalances( + selectedNetwork?.id, + selectedWallet?.address, + ); + const bal = balances?.find((b) => b.denom === pmFeature?.paymentsDenom); + + const [isSubmitting, setIsSubmitting] = useState(false); + + const { execEscrowMethod } = useEscrowContract( + networkId, + selectedWallet?.address, + ); + + const queryClient = useQueryClient(); + + const submitFunder = async () => { + setIsSubmitting(true); + try { + await execEscrowMethod( + "SubmitFunder", + [project.id?.toString()], + `${project.budget}${project.paymentDenom}`, + ); + await Promise.all([ + queryClient.invalidateQueries([ + "project", + getNetworkObjectId(networkId, project.id), + ]), + queryClient.invalidateQueries(["projects", networkId]), + ]); + } finally { + onClose(); + setIsSubmitting(false); + } + }; + + const fundingAmount = useMemo(() => { + return project.milestones + .map((m) => m.amount) + .reduce((total, amount) => total + BigInt(amount), BigInt(0)) + .toString(); + }, [project]); + + return ( + + + You’re making the signature to validate a transaction + + + + + + + + + + + + {selectedNetwork?.displayName} + + + + {tinyAddress(selectedWallet?.address, 16)} + + + + {selectedWallet?.address && } + + + + + + + Funding amount + + + + {prettyPrice(networkId, fundingAmount, pmFeature?.paymentsDenom)} + + + + + + Balance + + + + {prettyPrice(networkId, bal?.amount, pmFeature?.paymentsDenom)} + + + + + + submitFunder()} + /> + + + + + + + + ); +}; diff --git a/packages/screens/Projects/components/HeaderBackButton.tsx b/packages/screens/Projects/components/HeaderBackButton.tsx new file mode 100644 index 0000000000..e93c778f19 --- /dev/null +++ b/packages/screens/Projects/components/HeaderBackButton.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import { TouchableOpacity } from "react-native"; + +import chevronLeftSVG from "../../../../assets/icons/chevron-left.svg"; +import { BrandText } from "../../../components/BrandText"; +import FlexRow from "../../../components/FlexRow"; +import { SVG } from "../../../components/SVG"; +import { SpacerRow } from "../../../components/spacer"; +import { useAppNavigation } from "../../../utils/navigation"; +import { fontSemibold20 } from "../../../utils/style/fonts"; + +export const HeaderBackButton: React.FC = () => { + const navigation = useAppNavigation(); + + return ( + navigation.navigate("Projects")}> + + + + Projects Program + + + ); +}; diff --git a/packages/screens/Projects/components/Milestone.tsx b/packages/screens/Projects/components/Milestone.tsx new file mode 100644 index 0000000000..a6f84d0ea7 --- /dev/null +++ b/packages/screens/Projects/components/Milestone.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import { StyleProp, ViewStyle } from "react-native"; + +import { SimpleButton } from "../../../components/buttons/SimpleButton"; +import { neutral00, neutral33, neutral77 } from "../../../utils/style/colors"; +import { fontSemibold13 } from "../../../utils/style/fonts"; + +export const Tag: React.FC<{ + text: string; + color?: string; + bgColor?: string; + size?: "XS" | "SM" | "M" | "XL"; + borderColor?: string; + containerStyle?: StyleProp; +}> = ({ text, bgColor, color, borderColor, size, containerStyle }) => { + if (text === "") return null; + + return ( + + ); +}; diff --git a/packages/screens/Projects/components/MilestoneBoard.tsx b/packages/screens/Projects/components/MilestoneBoard.tsx new file mode 100644 index 0000000000..0112a4e0df --- /dev/null +++ b/packages/screens/Projects/components/MilestoneBoard.tsx @@ -0,0 +1,181 @@ +import React, { useState } from "react"; +import { StyleProp, ViewStyle } from "react-native"; +import Hoverable from "react-native-hoverable"; +import { SvgProps } from "react-native-svg"; + +import { MilestoneForm } from "./MilestoneForm"; +import { MilestoneItem } from "./MilestoneItem"; +import { MilestoneList } from "./MilestoneList"; +import addCircleSVG from "../../../../assets/icons/add-circle.svg"; +import noMilestonesSVG from "../../../../assets/icons/no-tasks.svg"; +import projectsCompletedSVG from "../../../../assets/icons/projects-completed.svg"; +import projectsInProgressSVG from "../../../../assets/icons/projects-inProgress.svg"; +import projectsOpenSVG from "../../../../assets/icons/projects-open.svg"; +import projectsReviewSVG from "../../../../assets/icons/projects-review.svg"; +import { BrandText } from "../../../components/BrandText"; +import FlexRow from "../../../components/FlexRow"; +import { SVG } from "../../../components/SVG"; +import { SimpleButton } from "../../../components/buttons/SimpleButton"; +import { SpacerColumn, SpacerRow } from "../../../components/spacer"; +import { + neutral00, + neutral33, + neutral77, + neutralFF, +} from "../../../utils/style/colors"; +import { fontSemibold13 } from "../../../utils/style/fonts"; +import { layout } from "../../../utils/style/layout"; +import { useMakeRequestState } from "../hooks/useMakeRequestHook"; +import { + ProjectMilestone, + MilestoneStatus, + MilestoneFormValues, +} from "../types"; + +export type Step = { + status: MilestoneStatus; + text: string; + iconSVG: React.FC; +}; + +const STEPS: Step[] = [ + { + status: "MS_OPEN", + text: "Open (Backlog)", + iconSVG: projectsOpenSVG, + }, + { + status: "MS_PROGRESS", + text: "In Progress", + iconSVG: projectsInProgressSVG, + }, + { + status: "MS_REVIEW", + text: "Review", + iconSVG: projectsReviewSVG, + }, + { + status: "MS_COMPLETED", + text: "Completed", + iconSVG: projectsCompletedSVG, + }, +]; + +export const MilestoneBoard: React.FC<{ + milestones: ProjectMilestone[]; + containerStyle?: StyleProp; + onSelectMilestone?: (id: string) => void; + editable?: boolean; +}> = ({ onSelectMilestone, containerStyle, editable, milestones }) => { + const [hoveredMilestone, setHoveredMilestone] = useState(); + const [isShowMilestoneForm, showMilestoneForm] = useState(false); + + const { + actions: { addMilestone, removeMilestone }, + } = useMakeRequestState(); + + const removeHoveredMilestone = (id: string) => { + setHoveredMilestone(undefined); + removeMilestone(id); + }; + + const addNewMilestone = (milestone: MilestoneFormValues) => { + showMilestoneForm(false); + console.log("adding milestone", milestone); + addMilestone(milestone); + }; + + return ( + + {STEPS.map((step, idx) => { + const listItems = (milestones || []).filter( + (milestone) => milestone.status === step.status, + ); + const isBacklogItem = idx === 0; + + return ( + + {listItems.map((milestone) => { + const isHovered = hoveredMilestone?.id === milestone.id; + return ( + + isBacklogItem && editable && setHoveredMilestone(milestone) + } + onMouseLeave={() => + isBacklogItem && editable && setHoveredMilestone(undefined) + } + > + + + + ); + })} + + {listItems.length === 0 && ( + + + + + {editable ? "No milestones" : "Empty"} + + + )} + + {isBacklogItem && isShowMilestoneForm && ( + showMilestoneForm(false)} + onSubmit={addNewMilestone} + /> + )} + + {isBacklogItem && editable && !isShowMilestoneForm && ( + showMilestoneForm(true)} + text="Add" + size="XS" + iconSVG={addCircleSVG} + color={neutralFF} + bgColor={neutral33} + style={{ + width: "100%", + justifyContent: "center", + alignItems: "center", + }} + /> + )} + + + + ); + })} + + ); +}; diff --git a/packages/screens/Projects/components/MilestoneForm.tsx b/packages/screens/Projects/components/MilestoneForm.tsx new file mode 100644 index 0000000000..728e60aa11 --- /dev/null +++ b/packages/screens/Projects/components/MilestoneForm.tsx @@ -0,0 +1,248 @@ +import { Decimal } from "@cosmjs/math"; +import { zodResolver } from "@hookform/resolvers/zod"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { TouchableOpacity, View } from "react-native"; + +import closeSVG from "../../../../assets/icons/close.svg"; +import { BrandText } from "../../../components/BrandText"; +import FlexRow from "../../../components/FlexRow"; +import { SVG } from "../../../components/SVG"; +import { TertiaryBox } from "../../../components/boxes/TertiaryBox"; +import { SimpleButton } from "../../../components/buttons/SimpleButton"; +import { + SelectInput, + SelectInputItem, +} from "../../../components/inputs/SelectInput"; +import { TextInputCustom } from "../../../components/inputs/TextInputCustom"; +import { SpacerColumn } from "../../../components/spacer"; +import { + neutral22, + neutral33, + neutral44, + neutral77, + neutralFF, + primaryColor, + redDefault, +} from "../../../utils/style/colors"; +import { fontSemibold12 } from "../../../utils/style/fonts"; +import { layout } from "../../../utils/style/layout"; +import { + MilestoneFormValues, + MilestonePriority, + zodMilestoneFormValues, +} from "../types"; + +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { + NetworkFeature, + getNativeCurrency, + getNetworkFeature, +} from "@/networks"; + +const PRIORITIES: SelectInputItem[] = [ + { label: "High", value: "MS_PRIORITY_HIGH".toString() }, + { label: "Medium", value: "MS_PRIORITY_MEDIUM".toString() }, +]; + +const initialValues: MilestoneFormValues = { + title: "", + desc: "", + priority: "MS_PRIORITY_MEDIUM", + amount: 0, + duration: 0, +}; + +export const MilestoneForm: React.FC<{ + onSubmit: (milestone: MilestoneFormValues) => void; + onClose: () => void; +}> = ({ onSubmit, onClose }) => { + const [priority, setPriority] = + useState("MS_PRIORITY_MEDIUM"); + + const networkId = useSelectedNetworkId(); + + const pmFeature = getNetworkFeature( + networkId, + NetworkFeature.GnoProjectManager, + ); + const currency = getNativeCurrency(networkId, pmFeature?.paymentsDenom); + const decimals = currency?.decimals || 0; + + const { handleSubmit, watch, formState, setValue } = useForm({ + resolver: zodResolver(zodMilestoneFormValues), + defaultValues: initialValues, + }); + const values = watch(); + const { errors } = formState; + + return ( + + + setValue("title", val)} + name="milestoneName" + label="" + placeHolder="⚡️ Type name here..." + hideLabel + fullWidth + noBrokenCorners + containerStyle={{ width: "100%" }} + height={32} + value={values.title} + error={errors.title?.message} + /> + + + + setValue("desc", val)} + value={values.desc} + error={errors.desc?.message} + /> + + + + + + Priority + + p.value === priority.toString()) || + PRIORITIES[0] + } + selectItem={(item) => setPriority(item.value as any)} + boxStyle={{ height: 32 }} + /> + + + + + + + Budget + + setValue("amount", +val)} + name="milestoneBudget" + label="" + placeHolder="Type here..." + hideLabel + fullWidth + noBrokenCorners + containerStyle={{ flex: 1 }} + height={32} + value={"" + values.amount} + error={errors.amount?.message} + testID="milestone-budget" + /> + + + + + + + Duration + + setValue("duration", +val)} + name="milestoneDuration" + label="" + placeHolder="Type here..." + hideLabel + fullWidth + noBrokenCorners + containerStyle={{ flex: 1 }} + height={32} + value={"" + values.duration} + error={errors.duration?.message} + testID="milestone-duration" + /> + + + + + + + Github link + + setValue("link", val)} + name="milestoneGithubLink" + label="" + placeHolder="Github link..." + hideLabel + fullWidth + noBrokenCorners + containerStyle={{ flex: 1 }} + height={32} + value={values.link} + error={errors.link?.message} + /> + + + + + { + console.log("adding milestone: handle sumbit", values); + values.priority = priority; + // FIXME: loss of precision + values.amount = +Decimal.fromUserInput( + values.amount.toString(), + decimals, + ).atomics; + onSubmit(values); + }, + (state) => { + console.error("adding milestone: invalid", state); + }, + )} + text="Confirm" + size="XS" + color={neutralFF} + bgColor={primaryColor} + testID="milestone-confirm" + style={{ + width: "100%", + justifyContent: "center", + alignItems: "center", + }} + /> + + + onClose?.()} + style={{ + position: "absolute", + right: -44, + top: "30%", + padding: layout.spacing_x1, + backgroundColor: neutral33, + borderWidth: 1, + borderColor: neutral44, + borderRadius: 100, + }} + > + + + + ); +}; diff --git a/packages/screens/Projects/components/MilestoneItem.tsx b/packages/screens/Projects/components/MilestoneItem.tsx new file mode 100644 index 0000000000..6b28798d5d --- /dev/null +++ b/packages/screens/Projects/components/MilestoneItem.tsx @@ -0,0 +1,162 @@ +import moment from "moment"; +import React from "react"; +import { TouchableOpacity, View } from "react-native"; + +import { Tag } from "./Milestone"; +import githubSVG from "../../../../assets/icons/github.svg"; +import trashSVG from "../../../../assets/icons/trash.svg"; +import { BrandText } from "../../../components/BrandText"; +import FlexRow from "../../../components/FlexRow"; +import { SVG } from "../../../components/SVG"; +import { TertiaryBox } from "../../../components/boxes/TertiaryBox"; +import { SocialButton } from "../../../components/buttons/SocialButton"; +import { SpacerColumn } from "../../../components/spacer"; +import { + neutral00, + neutral22, + neutral33, + neutral44, + neutral77, + neutralA3, + redDefault, +} from "../../../utils/style/colors"; +import { fontSemibold13 } from "../../../utils/style/fonts"; +import { layout } from "../../../utils/style/layout"; +import { ProjectMilestone } from "../types"; + +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { NetworkFeature, getNetworkFeature } from "@/networks"; +import { prettyPrice } from "@/utils/coins"; + +export const MilestoneItem: React.FC<{ + milestone: ProjectMilestone; + isHovered?: boolean; + onPress?: (id: string) => void; + onDelete?: (id: string) => void; +}> = ({ milestone, onPress, isHovered, onDelete }) => { + const networkId = useSelectedNetworkId(); + + const pmFeature = getNetworkFeature( + networkId, + NetworkFeature.GnoProjectManager, + ); + + return ( + + onPress?.(milestone.id)}> + + + 🔎 {milestone.title} + + + + + + {milestone.desc} + + + + + + Duration:{" "} + {moment.duration(milestone.duration, "seconds").humanize()} + + + + + {milestone.priority === "MS_PRIORITY_HIGH" && ( + + )} + + {milestone.priority === "MS_PRIORITY_MEDIUM" && ( + + )} + + {milestone.priority === "MS_PRIORITY_LOW" && ( + + )} + + + + + + + + + + {isHovered && ( + + onDelete?.(milestone.id)} + style={{ + position: "absolute", + padding: layout.spacing_x1, + backgroundColor: neutral33, + borderWidth: 1, + borderColor: neutral44, + borderRadius: 100, + }} + > + + + + )} + + ); +}; diff --git a/packages/screens/Projects/components/MilestoneList.tsx b/packages/screens/Projects/components/MilestoneList.tsx new file mode 100644 index 0000000000..dab18ae6ed --- /dev/null +++ b/packages/screens/Projects/components/MilestoneList.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { View } from "react-native"; +import { SvgProps } from "react-native-svg"; + +import { BrandText } from "../../../components/BrandText"; +import FlexRow from "../../../components/FlexRow"; +import { SVG } from "../../../components/SVG"; +import { neutral00 } from "../../../utils/style/colors"; +import { fontSemibold13 } from "../../../utils/style/fonts"; +import { layout } from "../../../utils/style/layout"; + +export const MilestoneList: React.FC<{ + iconSVG: React.FC; + text: string; + count: number; + children: React.ReactNode; +}> = ({ iconSVG, text, count, children }) => { + return ( + + + + + {text} + + + {count} + + + {children} + + ); +}; diff --git a/packages/screens/Projects/components/MilestonePriorityTag.tsx b/packages/screens/Projects/components/MilestonePriorityTag.tsx new file mode 100644 index 0000000000..658371f0ab --- /dev/null +++ b/packages/screens/Projects/components/MilestonePriorityTag.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +import { Tag } from "./Milestone"; +import { neutral33, neutralFF } from "../../../utils/style/colors"; +import { MilestonePriority } from "../types"; + +export const MilestonePriorityTag: React.FC<{ + priority: MilestonePriority; +}> = ({ priority }) => { + switch (priority) { + case "MS_PRIORITY_HIGH": + return ; + case "MS_PRIORITY_MEDIUM": + return ; + case "MS_PRIORITY_LOW": + return ; + default: + return ; + } +}; diff --git a/packages/screens/Projects/components/MilestoneStatusTag.tsx b/packages/screens/Projects/components/MilestoneStatusTag.tsx new file mode 100644 index 0000000000..33f07bf667 --- /dev/null +++ b/packages/screens/Projects/components/MilestoneStatusTag.tsx @@ -0,0 +1,22 @@ +import React from "react"; + +import { Tag } from "./Milestone"; +import { neutralFF } from "../../../utils/style/colors"; +import { MilestoneStatus } from "../types"; + +export const MilestoneStatusTag: React.FC<{ + status: MilestoneStatus; +}> = ({ status }) => { + switch (status) { + case "MS_OPEN": + return ; + case "MS_PROGRESS": + return ; + case "MS_REVIEW": + return ; + case "MS_COMPLETED": + return ; + default: + return ; + } +}; diff --git a/packages/screens/Projects/components/ProjectBox.tsx b/packages/screens/Projects/components/ProjectBox.tsx new file mode 100644 index 0000000000..343cbd9e4d --- /dev/null +++ b/packages/screens/Projects/components/ProjectBox.tsx @@ -0,0 +1,249 @@ +import { Link, useLinkBuilder } from "@react-navigation/native"; +import React, { memo, useMemo } from "react"; +import { ScrollView, TouchableOpacity, View } from "react-native"; + +import { Tag } from "./Milestone"; +import { ProjectStatusTag } from "./ProjectStatusTag"; +import { Project } from "../types"; + +import discordSVG from "@/assets/icons/discord.svg"; +import githubSVG from "@/assets/icons/github.svg"; +import shareSVG from "@/assets/icons/share.svg"; +import twitterSVG from "@/assets/icons/twitter.svg"; +import websiteSVG from "@/assets/icons/website.svg"; +import { BrandText } from "@/components/BrandText"; +import FlexRow from "@/components/FlexRow"; +import { ProgressLine } from "@/components/ProgressLine"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { SocialButton } from "@/components/buttons/SocialButton"; +import { RoundedGradientImage } from "@/components/images/RoundedGradientImage"; +import { Separator } from "@/components/separators/Separator"; +import { SpacerColumn, SpacerRow } from "@/components/spacer"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import { useFeedbacks } from "@/context/FeedbacksProvider"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import Clipboard from "@/modules/Clipboard"; +import { getNetworkObjectId, getUserId } from "@/networks"; +import { prettyPrice } from "@/utils/coins"; +import { useAppNavigation } from "@/utils/navigation"; +import { joinElements } from "@/utils/react"; +import { + neutral17, + neutral77, + neutralA3, + secondaryColor, +} from "@/utils/style/colors"; +import { + fontSemibold10, + fontSemibold13, + fontSemibold20, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { normalizeTwitterId } from "@/utils/twitter"; + +export const ProjectBox: React.FC<{ + project: Project; + width: number; +}> = memo(({ project, width }) => { + const stats = useMemo(() => { + if (!project.milestones) + return { completed: 0, total: 0, percentCompleted: 0 }; + const completed = project.milestones.filter( + (ms) => ms.status === "MS_COMPLETED", + ).length; + const total = project.milestones.length; + const percentCompleted = Math.floor((completed / total) * 100); + return { completed, total, percentCompleted }; + }, [project.milestones]); + const { setToast } = useFeedbacks(); + const buildLink = useLinkBuilder(); + + const networkId = useSelectedNetworkId(); + + const navigation = useAppNavigation(); + + const gotoProjectsDetail = (localId: string | undefined) => { + if (!localId) { + return; + } + navigation.navigate("ProjectsDetail", { + id: getNetworkObjectId(networkId, localId), + }); + }; + + const onPress = () => gotoProjectsDetail(project.id); + + const bottomElems = [ + { + const pathname = buildLink("ProjectsDetail", { + id: getNetworkObjectId(networkId, project.id), + }); + if (!pathname) return; + + const link = new URL(window.location.href); + link.pathname = pathname; + await Clipboard.setStringAsync(link.href); + + setToast({ + title: "Copied permanent link", + message: "", + type: "success", + mode: "normal", + }); + }} + />, + ]; + if (project.metadata?.teamAndLinkData?.discordLink) { + bottomElems.push( + + + , + ); + } + if (project.metadata?.teamAndLinkData?.websiteLink) { + bottomElems.push( + + + , + ); + } + if (project.metadata?.teamAndLinkData?.githubLink) { + bottomElems.push( + + + , + ); + } + if (project.metadata?.teamAndLinkData?.twitterProfile) { + bottomElems.push( + + + , + ); + } + + return ( + + {/* Body ============================================================== */} + + + + + + + + + + {project.metadata?.shortDescData?.name} + + + + + {project.metadata?.shortDescData?.tags + ?.split(",") + .map((tag, idx) => ( + + ))} + + + + + + + {project.metadata?.shortDescData?.desc} + + + + + + + + Milestones + + + {stats.completed}/{stats.total} + + + + + + + + {/* Footer ============================================================== */} + + + + + + + Grant:{" "} + + + {prettyPrice( + networkId, + project.budget || "0", + project.paymentDenom, + )} + + + + + + + + + {joinElements(bottomElems, )} + + + + + + + ); +}); diff --git a/packages/screens/Projects/components/ProjectInfo/LeftBlock.tsx b/packages/screens/Projects/components/ProjectInfo/LeftBlock.tsx new file mode 100644 index 0000000000..94427f1023 --- /dev/null +++ b/packages/screens/Projects/components/ProjectInfo/LeftBlock.tsx @@ -0,0 +1,207 @@ +import React, { useState } from "react"; +import { View } from "react-native"; + +import { Project } from "../../types"; +import { FundProjectModal } from "../FundProjectModal"; +import { Tag } from "../Milestone"; +import { ProjectStatusTag } from "../ProjectStatusTag"; +import { ResolveConflictButton } from "../ResolveConflictButton"; +import { SubmitContractorCandidateModal } from "../SubmitContractorCandidateModal"; + +import { BrandText } from "@/components/BrandText"; +import FlexRow from "@/components/FlexRow"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { RoundedGradientImage } from "@/components/images/RoundedGradientImage"; +import { SpacerColumn, SpacerRow } from "@/components/spacer"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { getNetworkObjectId, getUserId } from "@/networks"; +import { + errorColor, + neutral00, + neutral33, + neutral77, + neutralA3, +} from "@/utils/style/colors"; +import { + fontSemibold13, + fontSemibold14, + fontSemibold20, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +type LeftBlockProps = { + networkId: string; + project: Project; +}; + +export const LeftBlock: React.FC = ({ networkId, project }) => { + const navigation = useAppNavigation(); + const [isFundModalVisible, setIsFundModalVisible] = useState(false); + + const projectId = getNetworkObjectId(networkId, project.id); + + const selectedWallet = useSelectedWallet(); + const walletAddress = selectedWallet?.address || ""; + + const projectStatus = project.status; + const shortDescData = project.metadata?.shortDescData; + + const authorId = project + ? getUserId(networkId, project.sender) + : selectedWallet?.userId; + + return ( + + {shortDescData?.name} + + + + Project creator:{" "} + + + + + + + + {projectStatus && } + + + + {shortDescData?.tags?.split(",").map((tag, idx) => { + return ( + + ); + })} + + + + {/* Image */} + + + {/* Name and Description */} + + + + {shortDescData?.desc} + + + + + + + {project && + (walletAddress === project.funder || + walletAddress === project?.contractor) && + project.status === "ACCEPTED" && ( + <> + { + project.id !== undefined && + navigation.navigate("ProjectsConflictSolving", { + projectId, + }); + }} + size="SM" + width={280} + /> + + + )} + + {project.id !== undefined && + project.status === "CONFLICT" && + (walletAddress === project.conflictHandler || + walletAddress === project.contractor || + walletAddress === project.funder) && ( + <> + + + + )} + + {/* Actions */} + {/* If current user is not contractor, not creator of project and the project has not funder yet then user can become funder */} + {walletAddress !== project.sender && + walletAddress !== project.contractor && + !project.funded && + project.status === "CREATED" && ( + setIsFundModalVisible(true)} + size="SM" + width={200} + /> + )} + + + + + + + + {project?.id && ( + setIsFundModalVisible(false)} + /> + )} + + ); +}; + +export const SubmitCandidacyButton: React.FC<{ + project: Project; +}> = ({ project }) => { + const [isSubmitContractorModalVisible, setIsSubmitContractorModalVisible] = + useState(false); + return ( + <> + setIsSubmitContractorModalVisible(false)} + project={project} + /> + setIsSubmitContractorModalVisible(true)} + size="SM" + /> + + ); +}; diff --git a/packages/screens/Projects/components/ProjectInfo/RelatedUsers.tsx b/packages/screens/Projects/components/ProjectInfo/RelatedUsers.tsx new file mode 100644 index 0000000000..39dcf666c9 --- /dev/null +++ b/packages/screens/Projects/components/ProjectInfo/RelatedUsers.tsx @@ -0,0 +1,83 @@ +import { Link } from "@react-navigation/native"; +import React from "react"; +import { Table, Row, Cell, TableWrapper } from "react-native-reanimated-table"; + +import { SubmitCandidacyButton } from "./LeftBlock"; + +import { BrandText } from "@/components/BrandText"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { SpacerColumn } from "@/components/spacer"; +import { UsernameWithAvatar } from "@/components/user/UsernameWithAvatar"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { getUserId } from "@/networks"; +import { Project } from "@/screens/Projects/types"; +import { neutral22, neutralA3 } from "@/utils/style/colors"; +import { fontSemibold14 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; + +type RelatedUsersProps = { + networkId: string; + project: Project; +}; + +export const RelatedUsers: React.FC = ({ + networkId, + project, +}) => { + const wallet = useSelectedWallet(); + const walletAddress = wallet?.address; + + const showCandidacyButton = + !!walletAddress && + !!project && + walletAddress !== project.sender && + walletAddress !== project.funder && + !project.contractor && + project.status === "CREATED" && + !project.contractorCandidates?.includes(walletAddress); + + return ( + + + + + + {[project?.funder, project?.contractor, project?.conflictHandler].map( + (cellData, cellIndex) => { + let content; + if (cellIndex === 1 && !cellData) { + if (showCandidacyButton) { + content = ; + } else { + content = ( + + + {project.contractorCandidates.length} candidates + + + ); + } + } else { + content = ( + + ); + } + return ; + }, + )} + +
+
+ ); +}; diff --git a/packages/screens/Projects/components/ProjectInfo/RightBlock.tsx b/packages/screens/Projects/components/ProjectInfo/RightBlock.tsx new file mode 100644 index 0000000000..518b5cb0bc --- /dev/null +++ b/packages/screens/Projects/components/ProjectInfo/RightBlock.tsx @@ -0,0 +1,176 @@ +import { Link } from "@react-navigation/native"; +import moment from "moment/moment"; +import React from "react"; +import { View } from "react-native"; + +import copySVG from "@/assets/icons/copy.svg"; +import discordSVG from "@/assets/icons/discord.svg"; +import githubSVG from "@/assets/icons/github.svg"; +import twitterSVG from "@/assets/icons/twitter.svg"; +import websiteSVG from "@/assets/icons/website.svg"; +import { BrandText } from "@/components/BrandText"; +import FlexRow from "@/components/FlexRow"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { IconWithTextButton } from "@/components/buttons/SocialButton"; +import { SpacerColumn, SpacerRow } from "@/components/spacer"; +import { useFeedbacks } from "@/context/FeedbacksProvider"; +import Clipboard from "@/modules/Clipboard"; +import { Project } from "@/screens/Projects/types"; +import { prettyPrice } from "@/utils/coins"; +import { joinElements } from "@/utils/react"; +import { + neutral22, + neutral77, + neutralA3, + primaryColor, + yellowDefault, +} from "@/utils/style/colors"; +import { fontSemibold20 } from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { normalizeTwitterId } from "@/utils/twitter"; + +type RightBlockProps = { + project: Project; + networkId: string; +}; + +export const RightBlock: React.FC = ({ + networkId, + project, +}) => { + const { setToast } = useFeedbacks(); + + const isFunded = project.funded; + const teamAndLinkData = project.metadata?.teamAndLinkData; + + const actions = [ + { + await Clipboard.setStringAsync(window.location.href); + setToast({ + title: "Copied permanent link", + message: "", + type: "success", + mode: "normal", + }); + }} + />, + ]; + + if (teamAndLinkData?.websiteLink) { + actions.push( + + + , + ); + } + + if (teamAndLinkData?.discordLink) { + actions.push( + + + , + ); + } + + if (teamAndLinkData?.githubLink) { + actions.push( + + + , + ); + } + + if (teamAndLinkData?.twitterProfile) { + actions.push( + + + , + ); + } + + return ( + + + + + {isFunded ? "Deposited:" : "Budget:"} + + + + {prettyPrice(networkId, project.budget, project.paymentDenom)} + + + + + + + + + + Expires on: + + + + {/* FIXME */} + {moment(/*project.duration ||*/ 0 * 1000 + Date.now()).format( + "L", + )}{" "} + + + + + + + + {joinElements(actions, )} + + + ); +}; diff --git a/packages/screens/Projects/components/ProjectInfo/index.tsx b/packages/screens/Projects/components/ProjectInfo/index.tsx new file mode 100644 index 0000000000..9d8e7ba6f5 --- /dev/null +++ b/packages/screens/Projects/components/ProjectInfo/index.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { View } from "react-native"; + +import { Project } from "../../types"; + +import FlexRow from "@/components/FlexRow"; +import { useSelectedNetworkId } from "@/hooks/useSelectedNetwork"; +import { LeftBlock } from "@/screens/Projects/components/ProjectInfo/LeftBlock"; +import { RelatedUsers } from "@/screens/Projects/components/ProjectInfo/RelatedUsers"; +import { RightBlock } from "@/screens/Projects/components/ProjectInfo/RightBlock"; +import { layout } from "@/utils/style/layout"; + +export const ProjectInfo: React.FC<{ + project: Project; +}> = ({ project }) => { + const networkId = useSelectedNetworkId(); + + return ( + + + {/* Left block ======================================================= */} + + + + + {/* Right block ======================================================= */} + + + + + + + + ); +}; diff --git a/packages/screens/Projects/components/ProjectMilestones.tsx b/packages/screens/Projects/components/ProjectMilestones.tsx new file mode 100644 index 0000000000..6545875f4c --- /dev/null +++ b/packages/screens/Projects/components/ProjectMilestones.tsx @@ -0,0 +1,78 @@ +import React, { useMemo, useState } from "react"; +import { View } from "react-native"; + +import { MilestoneBoard } from "./MilestoneBoard"; +import { BrandText } from "../../../components/BrandText"; +import FlexRow from "../../../components/FlexRow"; +import { SearchBarInput } from "../../../components/Search/SearchBarInput"; +import { Separator } from "../../../components/separators/Separator"; +import { SpacerRow } from "../../../components/spacer"; +import { + neutral77, + neutral33, + neutral00, + neutral17, +} from "../../../utils/style/colors"; +import { fontSemibold20 } from "../../../utils/style/fonts"; +import { layout } from "../../../utils/style/layout"; +import { ProjectMilestone } from "../types"; + +export const ProjectMilestones: React.FC<{ + milestones: ProjectMilestone[]; + onSelectMilestone?: (id: string) => void; +}> = ({ milestones, onSelectMilestone }) => { + const [searchText, setSearchText] = useState(""); + const [isHideInfo, setIsHideInfo] = useState(false); + + const filteredMilestones = useMemo(() => { + return milestones.filter( + (m) => m.title.includes(searchText) || m.desc.includes(searchText), + ); + }, [milestones, searchText]); + + return ( + + + setIsHideInfo(!isHideInfo)} + style={fontSemibold20} + > + All Milestones: + + + + {filteredMilestones.length} + + + + + + + + + + ); +}; diff --git a/packages/screens/Projects/components/ProjectStatusTag.tsx b/packages/screens/Projects/components/ProjectStatusTag.tsx new file mode 100644 index 0000000000..e09c710cfe --- /dev/null +++ b/packages/screens/Projects/components/ProjectStatusTag.tsx @@ -0,0 +1,92 @@ +import React from "react"; + +import { SimpleButton } from "../../../components/buttons/SimpleButton"; +import { + neutral00, + neutral22, + primaryColor, + redDefault, + secondaryColor, +} from "../../../utils/style/colors"; +import { ContractStatusFilter } from "../types"; + +export const ProjectStatusTag: React.FC<{ + status: ContractStatusFilter | undefined; + onPress?: () => void; + active?: boolean; + size?: "XS" | "SM" | "M" | "XL"; +}> = ({ status, onPress, active, size }) => { + let color, bgColor, borderColor, text; + + switch (status) { + case "ALL": + text = "All"; + color = secondaryColor; + bgColor = neutral00; + borderColor = neutral22; + break; + case "CREATED": + text = "Open"; + color = "#C8FFAE"; + bgColor = "#C8FFAE1A"; + borderColor = "#C8FFAE1A"; + break; + case "ACCEPTED": + text = "In Progress"; + color = "#EAA54B"; + bgColor = "#EAA54B1A"; + borderColor = "#EAA54B1A"; + break; + case "COMPLETED": + text = "Completed"; + color = primaryColor; + bgColor = neutral00; + borderColor = neutral22; + break; + case "CANCELED": + text = "Canceled"; + color = redDefault; + bgColor = neutral00; + borderColor = neutral22; + break; + case "REJECTED": + text = "Rejected"; + color = redDefault; + bgColor = neutral00; + borderColor = neutral22; + break; + case "CONFLICT": + text = "Conflict"; + color = redDefault; + bgColor = neutral00; + borderColor = neutral22; + break; + case "ABORTED_IN_FAVOR_OF_CONTRACTOR": + case "ABORTED_IN_FAVOR_OF_FUNDER": + text = "Aborted"; + color = redDefault; + bgColor = neutral00; + borderColor = neutral22; + break; + default: + color = secondaryColor; + bgColor = neutral00; + borderColor = neutral00; + text = "unknown"; + break; + } + + return ( + onPress?.()} + text={text} + size={size} + bgColor={bgColor} + color={color} + style={{ + borderColor: active ? secondaryColor : borderColor, + }} + testID={`project-status-${status || "UNKNOWN"}`} + /> + ); +}; diff --git a/packages/screens/Projects/components/ProjectsStatusFilterButtons.tsx b/packages/screens/Projects/components/ProjectsStatusFilterButtons.tsx new file mode 100644 index 0000000000..6fcd01bf44 --- /dev/null +++ b/packages/screens/Projects/components/ProjectsStatusFilterButtons.tsx @@ -0,0 +1,63 @@ +import React from "react"; + +import { ProjectStatusTag } from "./ProjectStatusTag"; +import FlexRow from "../../../components/FlexRow"; +import { SpacerRow } from "../../../components/spacer"; +import { ContractStatusFilter } from "../types"; + +export const ProjectsStatusFilterButtons: React.FC<{ + status: ContractStatusFilter; + onChange: (newFilter: ContractStatusFilter) => void; +}> = ({ status, onChange }) => { + return ( + + onChange("ALL")} + /> + + + + onChange("CREATED")} + /> + + + + onChange("ACCEPTED")} + /> + + + + onChange("REJECTED")} + /> + + + + onChange("COMPLETED")} + /> + + ); +}; diff --git a/packages/screens/Projects/components/ResolveConflictButton.tsx b/packages/screens/Projects/components/ResolveConflictButton.tsx new file mode 100644 index 0000000000..40994e1bbd --- /dev/null +++ b/packages/screens/Projects/components/ResolveConflictButton.tsx @@ -0,0 +1,28 @@ +import React, { FC } from "react"; + +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { useAppNavigation } from "@/utils/navigation"; + +export const ResolveConflictButton: FC<{ + projectId: string | undefined; +}> = ({ projectId }) => { + const navigation = useAppNavigation(); + return ( + <> + { + if (!projectId) { + return; + } + navigation.navigate("ProjectsConflictSolving", { + projectId, + }); + }} + size="SM" + width={280} + /> + + ); +}; diff --git a/packages/screens/Projects/components/SubmitContractorCandidateModal.tsx b/packages/screens/Projects/components/SubmitContractorCandidateModal.tsx new file mode 100644 index 0000000000..4a685de891 --- /dev/null +++ b/packages/screens/Projects/components/SubmitContractorCandidateModal.tsx @@ -0,0 +1,128 @@ +import { useQueryClient } from "@tanstack/react-query"; +import React, { useState } from "react"; +import { View } from "react-native"; + +import gnoSVG from "@/assets/icons/networks/gno.svg"; +import { BrandText } from "@/components/BrandText"; +import { SVG } from "@/components/SVG"; +import { TertiaryBox } from "@/components/boxes/TertiaryBox"; +import { PrimaryButton } from "@/components/buttons/PrimaryButton"; +import { SecondaryButton } from "@/components/buttons/SecondaryButton"; +import ModalBase from "@/components/modals/ModalBase"; +import { SpacerColumn, SpacerRow } from "@/components/spacer"; +import { + useSelectedNetworkId, + useSelectedNetworkInfo, +} from "@/hooks/useSelectedNetwork"; +import useSelectedWallet from "@/hooks/useSelectedWallet"; +import { getNetworkObjectId } from "@/networks"; +import { Tag } from "@/screens/Projects/components/Milestone"; +import { useEscrowContract } from "@/screens/Projects/hooks/useEscrowContract"; +import { Project } from "@/screens/Projects/types"; +import { neutral17, neutral77 } from "@/utils/style/colors"; +import { + fontSemibold12, + fontSemibold13, + fontSemibold14, +} from "@/utils/style/fonts"; +import { layout } from "@/utils/style/layout"; +import { tinyAddress } from "@/utils/text"; + +type SubmitContractorCandidateModalProps = { + isVisible: boolean; + onClose: () => void; + project: Project; +}; + +export const SubmitContractorCandidateModal: React.FC< + SubmitContractorCandidateModalProps +> = ({ isVisible, onClose, project }) => { + const networkId = useSelectedNetworkId(); + const selectedWallet = useSelectedWallet(); + const selectedNetwork = useSelectedNetworkInfo(); + const queryClient = useQueryClient(); + + const [isSubmitting, setIsSubmitting] = useState(false); + + const { execEscrowMethod } = useEscrowContract( + networkId, + selectedWallet?.address, + ); + + const submitContractorCandidate = async () => { + setIsSubmitting(true); + + const localIdentifier = project?.id?.toString(); + + await execEscrowMethod("SubmitContractorCandidate", [localIdentifier]); + + const projectId = getNetworkObjectId(networkId, localIdentifier); + + await queryClient.invalidateQueries(["project", projectId]); + + onClose(); + setIsSubmitting(false); + }; + + return ( + + + You’re making the signature to validate a transaction + + + + + + + + + + + + {selectedNetwork?.displayName} + + + + {tinyAddress(selectedWallet?.address, 16)} + + + + {selectedWallet?.address && } + + + + + submitContractorCandidate()} + testID="confirm-and-sign" + /> + + + + + + + + ); +}; diff --git a/packages/screens/Projects/components/TNSResult.tsx b/packages/screens/Projects/components/TNSResult.tsx new file mode 100644 index 0000000000..d133045551 --- /dev/null +++ b/packages/screens/Projects/components/TNSResult.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import { View } from "react-native"; + +import { SearchResultsSection } from "../../../components/Search/SearchBarResults"; +import { AvatarWithName } from "../../../components/user/AvatarWithName"; +import { neutral00, neutral33 } from "../../../utils/style/colors"; +import { layout } from "../../../utils/style/layout"; + +const SEARCH_RESULTS_NAMES_MARGIN = layout.spacing_x1; + +export const TNSResult: React.FC<{ + names: string[]; + visible: boolean; + networkId: string; + onSelected: (name: string) => void; +}> = ({ names, networkId, onSelected, visible }) => { + if (!visible) return null; + return ( + + + {names.map((n) => ( + onSelected(n)} + /> + ))} + + + ); +}; diff --git a/packages/screens/Projects/defaultValues.ts b/packages/screens/Projects/defaultValues.ts new file mode 100644 index 0000000000..07f282d5f8 --- /dev/null +++ b/packages/screens/Projects/defaultValues.ts @@ -0,0 +1,32 @@ +import { + ProjectFormData, + ProjectTeamAndLinkFormData, +} from "./hooks/useMakeRequestHook"; + +/* +const emptyTeamAndLink: ProjectTeamAndLinkFormData = { + websiteLink: "", + twitterProfile: "", + discordLink: "", + githubLink: "", + teamDesc: "", +}; +*/ + +export const emptyProjectFormData: ProjectFormData = { + creatorAddress: "", + name: "", + description: "", + arbitratorAddress: "", + coverImg: "", + creatorKind: "contractor", + tags: "", +}; + +export const fakeTeamAndLink: ProjectTeamAndLinkFormData = { + websiteLink: "https://website.com", + twitterProfile: "https://twitter.com", + discordLink: "https://discord.com", + githubLink: "https://github.com", + teamDesc: "This is long team description", +}; diff --git a/packages/screens/Projects/hooks/useEscrowContract.ts b/packages/screens/Projects/hooks/useEscrowContract.ts new file mode 100644 index 0000000000..bd1a391a80 --- /dev/null +++ b/packages/screens/Projects/hooks/useEscrowContract.ts @@ -0,0 +1,98 @@ +import { GnoJSONRPCProvider } from "@gnolang/gno-js-client"; + +import { useFeedbacks } from "@/context/FeedbacksProvider"; +import { + getNetworkFeature, + mustGetGnoNetwork, + NetworkFeature, +} from "@/networks"; +import { useUtils } from "@/screens/Projects/hooks/useUtils"; +import { adenaVMCall, extractGnoString } from "@/utils/gno"; + +export const useEscrowContract = ( + networkId: string | undefined, + walletAddress: string | undefined, +) => { + const { mustGetValue } = useUtils(); + const { setToastError } = useFeedbacks(); + + const _getEscrowInfo = (networkId: string) => { + const pmFeature = getNetworkFeature( + networkId, + NetworkFeature.GnoProjectManager, + ); + + if (!pmFeature) { + throw Error("Project Manager is not supported on this network"); + } + + const gnoNetwork = mustGetGnoNetwork(networkId); + const escrowPkgPath = mustGetValue( + pmFeature?.projectsManagerPkgPath, + "escrow pkg path", + ); + + return { gnoNetworkEndpoint: gnoNetwork.endpoint, escrowPkgPath }; + }; + + // This will call contract method and get the data + const queryEscrow = async (methodName: string, args: any[]) => { + if (!networkId) { + setToastError({ title: "Error", message: "networkId not given" }); + return; + } + + const { gnoNetworkEndpoint, escrowPkgPath } = _getEscrowInfo(networkId); + + const client = new GnoJSONRPCProvider(gnoNetworkEndpoint); + const argsStr = args + .map((arg) => (typeof arg === "number" ? arg : `"${arg}"`)) + .join(","); + + const res = await client.evaluateExpression( + escrowPkgPath, + `${methodName}(${argsStr})`, + ); + + return extractGnoString(res); + }; + + // This will execute the method and does not return result + // only through error in case of problem + const execEscrowMethod = async ( + func: string, + args: string[], + send: string = "", + gasWanted: number = 2_000_000, + ) => { + try { + if (!networkId) { + setToastError({ title: "Error", message: "networkId not given" }); + return false; + } + + const { escrowPkgPath } = _getEscrowInfo(networkId); + + const caller = mustGetValue(walletAddress, "caller"); + + await adenaVMCall( + networkId, + { + caller, + send, + pkg_path: escrowPkgPath, + func, + args, + }, + { gasWanted }, + ); + + return true; + } catch (e: any) { + setToastError({ title: "Error", message: e.message }); + return false; + } + }; + + return { execEscrowMethod, queryEscrow }; +}; diff --git a/packages/screens/Projects/hooks/useMakeRequestHook.tsx b/packages/screens/Projects/hooks/useMakeRequestHook.tsx new file mode 100644 index 0000000000..909527e07e --- /dev/null +++ b/packages/screens/Projects/hooks/useMakeRequestHook.tsx @@ -0,0 +1,125 @@ +import { useRoute } from "@react-navigation/native"; +import { useMemo } from "react"; +import { z } from "zod"; +import { create } from "zustand"; + +import { useAppNavigation } from "../../../utils/navigation"; +import { emptyProjectFormData, fakeTeamAndLink } from "../defaultValues"; +import { MilestoneFormValues } from "../types"; + +export const zodProjectTeamAndLinkFormData = z.object({ + websiteLink: z.string().url(), + twitterProfile: z.string().url(), + discordLink: z.string().url(), + githubLink: z.string().url(), + teamDesc: z.string(), +}); + +export type ProjectTeamAndLinkFormData = z.infer< + typeof zodProjectTeamAndLinkFormData +>; + +export const zodProjectFormData = z.object({ + name: z.string().min(3), + description: z.string().min(10), + creatorKind: z.enum(["funder", "contractor"]), + creatorAddress: z.string().min(1), + targetAddress: z.string().min(1).optional(), + arbitratorAddress: z.string().min(1), + tags: z.string(), + coverImg: z.string().min(1), // web3 uri +}); + +export type ProjectFormData = z.infer; + +type MakeRequestState = { + stepIndice: number; + projectFormData: ProjectFormData; + teamAndLinkData: ProjectTeamAndLinkFormData; + milestones: MilestoneFormValues[]; + actions: { + setShortDesc: (shortDescData: ProjectFormData) => void; + setTeamAndLink: (teamAndLinkData: ProjectTeamAndLinkFormData) => void; + + addMilestone: (milestone: MilestoneFormValues) => void; + removeMilestone: (id: string) => void; + }; +}; + +const TOTAL_STEPS = 5; + +const useMakeRequestStore = create((set, get) => ({ + stepIndice: 1, + projectFormData: emptyProjectFormData, + teamAndLinkData: fakeTeamAndLink, + milestones: [], + actions: { + setShortDesc: (shortDescData) => set({ projectFormData: shortDescData }), + setTeamAndLink: (teamAndLinkData) => { + set({ teamAndLinkData }); + }, + addMilestone: (milestone) => { + const updatedMilestones = [...get().milestones, milestone].map( + (t, idx) => { + t.id = idx.toString(); + return t; + }, + ); + + set({ milestones: updatedMilestones }); + }, + removeMilestone: (id) => { + const updatedMilestones = get() + .milestones.filter((t) => t.id !== id) + .map((t, idx) => { + t.id = idx.toString(); + return t; + }); + + set({ milestones: updatedMilestones }); + }, + }, +})); + +export const useMakeRequestState = () => { + const navigation = useAppNavigation(); + const store = useMakeRequestStore(); + const route = useRoute(); + const step = !route.params ? 1 : (route.params as any).step; + + const stepIndex = useMemo(() => { + try { + let res = step ? parseInt(step, 10) : 1; + res = Number.isInteger(res) ? res : 1; + res = res > 5 || res < 0 ? 1 : res; + return res; + } catch { + return 1; + } + }, [step]); + + const gotoStep = (stepIndex: number) => { + navigation.navigate("ProjectsMakeRequest", { step: stepIndex }); + }; + + const goNextStep = () => { + const nextStep = Math.min(stepIndex + 1, TOTAL_STEPS); + gotoStep(nextStep); + }; + + const goPrevStep = () => { + const prevStep = Math.max(stepIndex - 1, 1); + gotoStep(prevStep); + }; + + return { + ...store, + stepIndice: stepIndex, + actions: { + ...store.actions, + goNextStep, + goPrevStep, + gotoStep, + }, + }; +}; diff --git a/packages/screens/Projects/hooks/useProjects.ts b/packages/screens/Projects/hooks/useProjects.ts new file mode 100644 index 0000000000..66092791c3 --- /dev/null +++ b/packages/screens/Projects/hooks/useProjects.ts @@ -0,0 +1,114 @@ +import { GnoJSONRPCProvider } from "@gnolang/gno-js-client"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; +import { useMemo } from "react"; +import { z } from "zod"; + +import { + NetworkFeature, + getGnoNetwork, + getNetworkFeature, + parseNetworkObjectId, +} from "../../../networks"; +import { Project, zodProject } from "../types"; + +import { extractGnoJSONString } from "@/utils/gno"; + +export const useProject = (projectId: string | undefined) => { + return useQuery( + ["project", projectId], + async () => { + const [network, localIdentifierStr] = parseNetworkObjectId(projectId); + if (!network || !localIdentifierStr) { + return null; + } + + const gnoNetwork = getGnoNetwork(network.id); + if (!gnoNetwork) { + return null; + } + + const pmFeature = getNetworkFeature( + network.id, + NetworkFeature.GnoProjectManager, + ); + + if (!pmFeature) { + return null; + } + + const client = new GnoJSONRPCProvider(gnoNetwork.endpoint); + const query = `RenderContractJSON(${parseInt(localIdentifierStr, 10)})`; + const contractData = await client.evaluateExpression( + pmFeature.projectsManagerPkgPath, + query, + ); + + const j = extractGnoJSONString(contractData); + + return zodProject.parse(j); + }, + { staleTime: Infinity }, + ); +}; + +export type ProjectFilter = + | { byCandidatesForFunder: { funder: string } } + | { byFunder: { funder: string } } + | { byContractor: { contractor: string } } + | { byContractorAndFunder: { contractor: string; funder: string } } + | null; + +export const useProjects = ( + networkId: string, + filter: ProjectFilter = null, +) => { + const { data, ...other } = useInfiniteQuery<{ + projects: Project[]; + nextOffset: number; + }>( + ["projects", networkId, filter], + async ({ pageParam = 0 }) => { + const gnoNetwork = getGnoNetwork(networkId); + if (!gnoNetwork) { + return { projects: [], nextOffset: 0 }; + } + + const pmFeature = getNetworkFeature( + networkId, + NetworkFeature.GnoProjectManager, + ); + if (!pmFeature) { + return { projects: [], nextOffset: 0 }; + } + + const client = new GnoJSONRPCProvider(gnoNetwork.endpoint); + + const limit = 12; + + const pkgPath = pmFeature.projectsManagerPkgPath; + const expr = `RenderContractsJSON(${pageParam},${limit},${JSON.stringify(JSON.stringify(filter))})`; + + const contractsData = await client.evaluateExpression(pkgPath, expr); + + const j = extractGnoJSONString(contractsData); + + const projects = z.array(zodProject).parse(j); + + return { + projects, + nextOffset: pageParam + projects.length, + }; + }, + { + staleTime: Infinity, + getNextPageParam: ({ nextOffset }) => nextOffset, + }, + ); + + const projects = useMemo(() => { + if (!data) return []; + return data.pages.flatMap((page) => page.projects); + }, [data]); + + return { projects, ...other }; +}; diff --git a/packages/screens/Projects/hooks/useUtils.ts b/packages/screens/Projects/hooks/useUtils.ts new file mode 100644 index 0000000000..33951febea --- /dev/null +++ b/packages/screens/Projects/hooks/useUtils.ts @@ -0,0 +1,20 @@ +import { useFeedbacks } from "../../../context/FeedbacksProvider"; + +export const useUtils = () => { + const { setToast } = useFeedbacks(); + + const mustGetValue = (value?: string, name?: string) => { + if (!value) { + setToast({ + title: "Error", + type: "error", + mode: "normal", + message: `failed to get ${name || "value"}`, + }); + throw Error(`failed to get ${name || "value"}`); + } + return value; + }; + + return { mustGetValue }; +}; diff --git a/packages/screens/Projects/types.ts b/packages/screens/Projects/types.ts new file mode 100644 index 0000000000..dd3bf39376 --- /dev/null +++ b/packages/screens/Projects/types.ts @@ -0,0 +1,160 @@ +import * as zod from "zod"; +import { z } from "zod"; + +import { zodTryParseJSON } from "@/utils/sanitize"; + +const zodMilestonePriority = z.enum([ + "MS_PRIORITY_HIGH", + "MS_PRIORITY_MEDIUM", + "MS_PRIORITY_LOW", +]); + +export type MilestonePriority = z.infer; + +export const zodMilestoneFormValues = zod.object({ + id: zod.string().optional(), + title: zod + .string() + .min(3) + .regex(/^[^,]*$/, "Should not contain ,"), + desc: zod + .string() + .min(10) + .regex(/^[^,]*$/, "Should not contain ,"), + amount: zod.number().positive().int(), + priority: zodMilestonePriority, + link: zod.string().url().optional(), + duration: zod.number().min(1), +}); + +export type MilestoneFormValues = zod.infer; + +export interface MilestoneRequest { + title: string; + desc: string; + amount: string; + duration: string; + link: string; + priority: string; +} + +export const zodMilestoneStatus = z.enum([ + "MS_OPEN", + "MS_PROGRESS", + "MS_REVIEW", + "MS_COMPLETED", +]); + +export type MilestoneStatus = z.infer; + +const zodProjectMilestone = z.object({ + id: z.string(), + title: z.string(), + desc: z.string(), + amount: z.string(), + paid: z.string(), + duration: z.number(), // seconds + link: z.string(), + funded: z.boolean(), + priority: zodMilestonePriority, + status: zodMilestoneStatus, +}); + +export type ProjectMilestone = z.infer; + +export const previewMilestoneForm = (fm: MilestoneFormValues) => { + const m: ProjectMilestone = { + ...fm, + id: fm.id || "", + status: "MS_OPEN", + paid: "0", + amount: fm.amount.toString(), + link: fm.link || "", + funded: false, + }; + return m; +}; + +const zodContractStatus = z.enum([ + "CREATED", + "ACCEPTED", + "CANCELED", + "COMPLETED", + "REJECTED", + "CONFLICT", + "ABORTED_IN_FAVOR_OF_CONTRACTOR", + "ABORTED_IN_FAVOR_OF_FUNDER", +]); + +type ContractStatus = z.infer; + +export type ContractStatusFilter = ContractStatus | "ALL"; + +const zodConflictOutcome = z.enum([ + "RESUME_CONTRACT", + "REFUND_FUNDER", + "PAY_CONTRACTOR", +]); + +const zodConflict = z.object({ + initiator: z.string(), + createdAt: z.coerce.date(), + respondedAt: z.coerce.date().optional(), + resolvedAt: z.coerce.date().optional(), + initiatorMessage: z.string(), + responseMessage: z.string().optional(), + resolutionMessage: z.string().optional(), + outcome: zodConflictOutcome.optional(), +}); + +const zodProjectShortDescData = z.object({ + name: z.string().optional(), + desc: z.string().optional(), + coverImg: z.string().optional(), + tags: z.string().optional(), +}); + +export type ProjectShortDescData = z.infer; + +const zodProjectTeamAndLinkData = z.object({ + websiteLink: z.string().optional(), + twitterProfile: z.string().optional(), + discordLink: z.string().optional(), + githubLink: z.string().optional(), + teamDesc: z.string().optional(), +}); + +export type ProjectTeamAndLinkData = z.infer; + +const zodProjectMetadata = z.object({ + shortDescData: zodProjectShortDescData.optional(), + teamAndLinkData: zodProjectTeamAndLinkData.optional(), +}); + +export const zodProject = z.object({ + id: z.string(), + sender: z.string(), + contractor: z.string(), + contractorCandidates: z.array(z.string()), + funder: z.string(), + paymentDenom: z.string(), + metadata: z + .string() + .transform((data) => zodTryParseJSON(zodProjectMetadata, data)), + status: zodContractStatus, + expireAt: z.coerce.date(), + funderFeedback: z.string(), + contractorFeedback: z.string(), + milestones: z.array(zodProjectMilestone), + pausedBy: z.string(), + conflictHandler: z.string(), + handlerCandidate: z.string(), + handlerSuggestor: z.string(), + createdAt: z.coerce.date(), + budget: z.string(), + funded: z.boolean(), + rejectReason: z.string(), + conflicts: z.array(zodConflict), +}); + +export type Project = z.infer; diff --git a/packages/screens/Projects/utils.ts b/packages/screens/Projects/utils.ts new file mode 100644 index 0000000000..0dead50bc6 --- /dev/null +++ b/packages/screens/Projects/utils.ts @@ -0,0 +1,12 @@ +import { Project } from "./types"; + +export const getProjectStats = (project: Project) => { + if (!project.milestones) + return { completed: 0, total: 0, percentCompleted: 0 }; + const completed = project.milestones.filter( + (ms) => ms.status === "MS_COMPLETED", + ).length; + const total = project.milestones.length; + const percentCompleted = Math.floor((completed / total) * 100); + return { completed, total, percentCompleted }; +}; diff --git a/packages/screens/RiotGame/RiotGameEnrollScreen.tsx b/packages/screens/RiotGame/RiotGameEnrollScreen.tsx index 25d44550db..f10c954cca 100644 --- a/packages/screens/RiotGame/RiotGameEnrollScreen.tsx +++ b/packages/screens/RiotGame/RiotGameEnrollScreen.tsx @@ -9,7 +9,6 @@ import { useSelector } from "react-redux"; import { EnrollSlot } from "./component/EnrollSlot"; import { GameContentView } from "./component/GameContentView"; import { RipperSelectorModal } from "./component/RipperGridSelectorModal"; -import { SimpleButton } from "./component/SimpleButton"; import controllerSVG from "../../../assets/game/controller-yellow.svg"; import closeSVG from "../../../assets/icons/close.svg"; import useSelectedWallet from "../../hooks/useSelectedWallet"; @@ -18,6 +17,7 @@ import { NFT } from "@/api/marketplace/v1/marketplace"; import { BrandText } from "@/components/BrandText"; import { SVG } from "@/components/SVG"; import { LegacyTertiaryBox } from "@/components/boxes/LegacyTertiaryBox"; +import { SimpleButton } from "@/components/buttons/SimpleButton"; import { TertiaryButton } from "@/components/buttons/TertiaryButton"; import { useFeedbacks } from "@/context/FeedbacksProvider"; import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; diff --git a/packages/screens/RiotGame/component/FightSectionHeader.tsx b/packages/screens/RiotGame/component/FightSectionHeader.tsx index ca09a341f7..e3ae5e4e8b 100644 --- a/packages/screens/RiotGame/component/FightSectionHeader.tsx +++ b/packages/screens/RiotGame/component/FightSectionHeader.tsx @@ -1,11 +1,11 @@ import React from "react"; import { StyleSheet, View } from "react-native"; -import { SimpleButton } from "./SimpleButton"; import addCircleSVG from "../../../../assets/icons/add-circle.svg"; import FlexRow from "../../../components/FlexRow"; import { BrandText } from "@/components/BrandText"; +import { SimpleButton } from "@/components/buttons/SimpleButton"; import { useAppNavigation } from "@/hooks/navigation/useAppNavigation"; import { neutral17, yellowDefault } from "@/utils/style/colors"; import { fontMedium48, fontSemibold28 } from "@/utils/style/fonts"; diff --git a/packages/screens/RiotGame/component/RipperGridSelectorModal.tsx b/packages/screens/RiotGame/component/RipperGridSelectorModal.tsx index 486977a8c4..87c9b9b7b0 100644 --- a/packages/screens/RiotGame/component/RipperGridSelectorModal.tsx +++ b/packages/screens/RiotGame/component/RipperGridSelectorModal.tsx @@ -13,7 +13,6 @@ import { TouchableOpacity } from "react-native-gesture-handler"; import { RipperAvatar } from "./RipperAvatar"; import { RipperStatsSection } from "./RipperStatsSection"; -import { SimpleButton } from "./SimpleButton"; import controllerSVG from "../../../../assets/game/controller.svg"; import dashedBorderPNG from "../../../../assets/game/dashed-border.png"; import closeSVG from "../../../../assets/icons/close.svg"; @@ -23,6 +22,7 @@ import { NFT } from "@/api/marketplace/v1/marketplace"; import { BrandText } from "@/components/BrandText"; import { SVG } from "@/components/SVG"; import { LegacyTertiaryBox } from "@/components/boxes/LegacyTertiaryBox"; +import { SimpleButton } from "@/components/buttons/SimpleButton"; import { SpacerRow } from "@/components/spacer"; import { useIsMobile } from "@/hooks/useIsMobile"; import { getRipperRarity, isNFTStaked } from "@/utils/game"; diff --git a/packages/screens/RiotGame/component/RipperSelectorModal.tsx b/packages/screens/RiotGame/component/RipperSelectorModal.tsx index de66b52f93..6c3208e2d8 100644 --- a/packages/screens/RiotGame/component/RipperSelectorModal.tsx +++ b/packages/screens/RiotGame/component/RipperSelectorModal.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { Modal, ModalProps, - StyleSheet, + ViewStyle, View, ImageBackground, ScrollView, @@ -12,7 +12,6 @@ import { import { AvailableRippersGrid } from "./AvailableRippersGrid"; import { RipperAvatar } from "./RipperAvatar"; import { RipperStatsSection } from "./RipperStatsSection"; -import { SimpleButton } from "./SimpleButton"; import controllerSVG from "../../../../assets/game/controller.svg"; import dashedBorderPNG from "../../../../assets/game/dashed-border.png"; import closeSVG from "../../../../assets/icons/close.svg"; @@ -21,6 +20,7 @@ import FlexRow from "../../../components/FlexRow"; import { NFT } from "@/api/marketplace/v1/marketplace"; import { BrandText } from "@/components/BrandText"; import { SVG } from "@/components/SVG"; +import { SimpleButton } from "@/components/buttons/SimpleButton"; import { SpacerRow } from "@/components/spacer"; import { useBreeding } from "@/hooks/riotGame/useBreeding"; import { useIsMobile } from "@/hooks/useIsMobile"; @@ -89,8 +89,8 @@ export const RipperSelectorModal: React.FC = ({ visible={visible} {...props} > - - + + @@ -120,7 +120,7 @@ export const RipperSelectorModal: React.FC = ({ /> - + = ({ @@ -172,38 +172,38 @@ export const RipperSelectorModal: React.FC = ({ ); }; -// FIXME: remove StyleSheet.create -// eslint-disable-next-line no-restricted-syntax -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: withAlpha(neutral00, 0.95), - paddingTop: headerHeight, - borderWidth: 1, - }, - dashedBorder: { - width: RIPPER_IMAGE_SIZE, - height: RIPPER_IMAGE_SIZE, - marginTop: layout.spacing_x2_5, - }, - roundedContainer: { - width: RIPPER_IMAGE_SIZE - 4, - height: RIPPER_IMAGE_SIZE - 4, - position: "absolute", - left: 2, - top: 2, - borderRadius: 999, - overflow: "hidden", - }, - btnGroup: { - marginTop: layout.spacing_x2_5, - flexDirection: "row", - alignSelf: "center", - }, - closeIcon: { - position: "absolute", - right: 10, - top: 10, - zIndex: 1, - }, -}); +const styleContainer: ViewStyle = { + flex: 1, + backgroundColor: withAlpha(neutral00, 0.95), + paddingTop: headerHeight, + borderWidth: 1, +}; + +const styleDashedBorder: ViewStyle = { + width: RIPPER_IMAGE_SIZE, + height: RIPPER_IMAGE_SIZE, + marginTop: layout.spacing_x2_5, +}; + +const styleRoundedContainer: ViewStyle = { + width: RIPPER_IMAGE_SIZE - 4, + height: RIPPER_IMAGE_SIZE - 4, + position: "absolute", + left: 2, + top: 2, + borderRadius: 999, + overflow: "hidden", +}; + +const styleBtnGroup: ViewStyle = { + marginTop: layout.spacing_x2_5, + flexDirection: "row", + alignSelf: "center", +}; + +const styleCloseIcon: ViewStyle = { + position: "absolute", + right: 10, + top: 10, + zIndex: 1, +}; diff --git a/packages/screens/Settings/SettingsScreen.tsx b/packages/screens/Settings/SettingsScreen.tsx index bdcf8e0eb8..5deec15b2e 100644 --- a/packages/screens/Settings/SettingsScreen.tsx +++ b/packages/screens/Settings/SettingsScreen.tsx @@ -96,6 +96,7 @@ export const SettingsScreen: ScreenFC<"Settings"> = () => { description: "", state: testnetEnabled, }} + testID="testnet-switch" /> diff --git a/packages/screens/Settings/components/SettingItem.tsx b/packages/screens/Settings/components/SettingItem.tsx index 86f442b701..279f0d4884 100644 --- a/packages/screens/Settings/components/SettingItem.tsx +++ b/packages/screens/Settings/components/SettingItem.tsx @@ -12,11 +12,16 @@ export const SettingItem: React.FC<{ item: SettingItemType; onPress: (item: SettingItemType) => void; disabled?: boolean; -}> = ({ item, onPress, disabled }) => { + testID?: string; +}> = ({ item, onPress, disabled, testID }) => { const commonStyles = useCommonStyles(); return ( - onPress(item)} disabled={disabled}> + onPress(item)} + disabled={disabled} + testID={testID} + > {item.title} diff --git a/packages/scripts/addrFromMnemo.ts b/packages/scripts/addrFromMnemo.ts new file mode 100644 index 0000000000..bea781710f --- /dev/null +++ b/packages/scripts/addrFromMnemo.ts @@ -0,0 +1,27 @@ +import { GnoWallet } from "@gnolang/gno-js-client"; +import { program } from "commander"; + +import { NetworkKind, getNetwork } from "@/networks"; + +const main = async () => { + program.option("-n, --network ", "network"); + program.option("-m, --mnemonic ", "mnemonic"); + program.parse(); + const { network: networkId, mnemonic } = program.opts() as { + network: string; + mnemonic: string; + }; + const network = getNetwork(networkId); + if (!network) { + console.error(`Network "${networkId}" not found`); + return; + } + if (network.kind !== NetworkKind.Gno) { + console.error(`Only Gno networks are supported for now`); + } + const wallet = await GnoWallet.fromMnemonic(mnemonic); + const address = await wallet.getAddress(); + console.log(address); +}; + +main(); diff --git a/packages/scripts/gnoQuery.ts b/packages/scripts/gnoQuery.ts index 8a6f26f31c..779611a8b2 100644 --- a/packages/scripts/gnoQuery.ts +++ b/packages/scripts/gnoQuery.ts @@ -1,22 +1,34 @@ -import { GnoJSONRPCProvider } from "@gnolang/gno-js-client"; +import child_process from "child_process"; import { program } from "commander"; -import { gnoTeritoriNetwork } from "../networks/gno-teritori"; +import sqh from "./sqh"; + +import { getGnoNetwork } from "@/networks"; const main = async () => { + program.argument("network-id", "network id"); program.argument("realm", "realm pkg path"); program.argument("query", "eval query"); program.parse(); - const [realm, query] = program.args as [string, string]; - - const network = gnoTeritoriNetwork; - const provider = new GnoJSONRPCProvider(network.endpoint); - try { - const rawRes = await provider.evaluateExpression(realm, query); - console.log(rawRes); - } catch (e) { - console.error(JSON.stringify(e)); + const [networkId, realm, query] = program.args as [string, string, string]; + + const network = getGnoNetwork(networkId); + if (!network) { + console.error(`Network "${networkId}" not found`); + return; + } + + if (!realm) { + console.log("missing realm argument"); } + + if (!query) { + console.log("missing query argument"); + } + + const cmd = `gnokey query vm/qeval -remote ${sqh(network.endpoint)} -data ${sqh(`${realm}.${query}`)}`; + console.log("> " + cmd); + child_process.execSync(cmd, { stdio: "inherit" }); }; main(); diff --git a/packages/scripts/inject-projects.ts b/packages/scripts/inject-projects.ts new file mode 100644 index 0000000000..457682acbd --- /dev/null +++ b/packages/scripts/inject-projects.ts @@ -0,0 +1,113 @@ +import { faker } from "@faker-js/faker"; +import * as child_process from "child_process"; +import { program } from "commander"; +import { capitalize, range } from "lodash"; + +import sqh from "./sqh"; + +import { NetworkFeature, getGnoNetwork, getNetworkFeature } from "@/networks"; + +const main = async () => { + program + .option("-n, --network ", "Network ID") + .option("-w, --wallet ", "Wallet name") + .option("-c, --count ", "Number of projects to create") + .parse(); + + const opts = program.opts() as { + network: string; + wallet: string; + count: string; + }; + + const count = parseInt(opts.count, 10); + if (isNaN(count) || count <= 0) { + throw new Error("Invalid count"); + } + + const network = getGnoNetwork(opts.network); + if (!network) { + throw new Error("Network not found"); + } + const pmFeature = getNetworkFeature( + network.id, + NetworkFeature.GnoProjectManager, + ); + if (!pmFeature) { + throw new Error("Project manager feature not found"); + } + + console.log( + `Creating ${count} projects on ${network.displayName} with ${opts.wallet} wallet`, + ); + + for (let i = 0; i < count; i++) { + const teamAndLinkData: Record = {}; + + if (faker.helpers.maybe(() => true)) { + teamAndLinkData.websiteLink = faker.internet.url(); + } + if (faker.helpers.maybe(() => true)) { + teamAndLinkData.twitterProfile = + "@" + faker.person.firstName().toLowerCase(); + } + if (faker.helpers.maybe(() => true)) { + teamAndLinkData.discordLink = "https://discord.com"; + } + if (faker.helpers.maybe(() => true)) { + teamAndLinkData.githubLink = + "https://github.com/" + faker.person.firstName().toLowerCase(); + } + if (faker.helpers.maybe(() => true)) { + teamAndLinkData.teamDesc = faker.lorem.paragraph(); + } + + const metadata = { + shortDescData: { + name: faker.lorem.sentence(3).slice(0, -1), + desc: + capitalize(faker.hacker.phrase()) + + "\n\n" + + faker.lorem.paragraphs(10), + tags: [ + ...new Set( + range(0, faker.number.int({ min: 3, max: 10 })).map(() => + faker.hacker.noun(), + ), + ), + ].join(","), + coverImg: faker.image.urlPicsumPhotos(), + }, + teamAndLinkData, + }; + + const milestones = []; + for (let j = 0; j < faker.number.int({ min: 3, max: 6 }); j++) { + milestones.push({ + title: faker.lorem.sentence(3).slice(0, -1), + desc: faker.lorem.paragraphs(3), + priority: faker.helpers.arrayElement([ + "MS_PRIORITY_HIGH", + "MS_PRIORITY_MEDIUM", + "MS_PRIORITY_LOW", + ]), + amount: faker.number + .bigInt({ min: "420000", max: "4200000" }) + .toString(), + duration: faker.number.bigInt({ min: "3600", max: "36000" }).toString(), + link: faker.internet.url(), + }); + } + + const totalPrice = milestones.reduce( + (acc, m) => acc + BigInt(m.amount), + BigInt(0), + ); + + const cmd = `gnokey maketx call -insecure-password-stdin -pkgpath ${sqh(pmFeature.projectsManagerPkgPath)} -func "CreateContractJSON" -gas-fee 1000000ugnot -gas-wanted 10000000 -send "${totalPrice}ugnot" -broadcast -chainid ${sqh(network.chainId)} -args "" -args "g1xfjfdfyka23agew9g6qst030pr85q0ggac7vuj" -args ${sqh(pmFeature.paymentsDenom)} -args ${sqh(JSON.stringify(metadata))} -args "200000" -args ${sqh(JSON.stringify(milestones))} -args "g108cszmcvs4r3k67k7h5zuhm4el3qhlrxzhshtv" -remote ${sqh(network.endpoint)} ${sqh(opts.wallet)}`; + console.log(">", cmd); + child_process.execSync(cmd, { input: "\n" }); + } +}; + +main(); diff --git a/packages/scripts/install-gno.ts b/packages/scripts/install-gno.ts new file mode 100644 index 0000000000..66db6555c3 --- /dev/null +++ b/packages/scripts/install-gno.ts @@ -0,0 +1,18 @@ +import child_process from "child_process"; +import fs from "fs/promises"; +import os from "os"; +import path from "path"; + +import sqh from "./sqh"; + +const ref = "fec2d18f630b44ccc2121472aa2284cd9c8caf6f"; +const remote = "https://github.com/gnolang/gno.git"; + +const main = async () => { + 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); + child_process.execSync(cmd, { stdio: "inherit" }); +}; + +main(); diff --git a/packages/scripts/validateNetworks.ts b/packages/scripts/validateNetworks.ts index 13d81ed592..37fae66237 100644 --- a/packages/scripts/validateNetworks.ts +++ b/packages/scripts/validateNetworks.ts @@ -1,5 +1,7 @@ import { allNetworks, getNativeCurrency, NetworkKind } from "../networks"; +import { allFeatureObjects } from "@/networks/features"; + const ids: { [key: string]: boolean } = {}; const idPrefixes: { [key: string]: boolean } = {}; const cosmosChainIds: { [key: string]: boolean } = {}; @@ -94,6 +96,22 @@ for (const net of allNetworks) { `feature object '${feature.type}' of network '${net.id}' does not have a corresponding feature declaration`, ); } + const zodFeatureObj = allFeatureObjects.find( + (zo) => zo.shape.type.value === feature.type, + ); + if (!zodFeatureObj) { + throw new Error( + `feature object '${feature.type}' of network '${net.id}' does not have a corresponding zod declaration`, + ); + } + try { + zodFeatureObj.parse(feature); + } catch (e) { + const msg = e instanceof Error ? e.message : JSON.stringify(e, null, 2); + throw new Error( + `feature object '${feature.type}' of network '${net.id}' does not match the zod declaration: ${msg}`, + ); + } featureObjects[feature.type] = true; } } diff --git a/packages/utils/gno.ts b/packages/utils/gno.ts index 99612d9f86..ca966e119d 100644 --- a/packages/utils/gno.ts +++ b/packages/utils/gno.ts @@ -7,7 +7,7 @@ interface AdenaDoContractMessage { value: { [key in string]: any }; } -interface RequestDocontractMessage { +export interface RequestDocontractMessage { messages: AdenaDoContractMessage[]; gasFee: number; gasWanted: number; diff --git a/packages/utils/navigation.ts b/packages/utils/navigation.ts index 5b410d972f..b8309a3a87 100644 --- a/packages/utils/navigation.ts +++ b/packages/utils/navigation.ts @@ -82,6 +82,14 @@ export type RootStackParamList = { Settings: undefined; + Projects?: { network?: string }; + ProjectsManager: { view?: string }; + ProjectsPayment: { projectId: string; milestoneId: string }; + ProjectsCompleteMilestone: { projectId: string; milestoneId: string }; + ProjectsMakeRequest: { step?: number }; + ProjectsDetail: { id: string }; + ProjectsConflictSolving: { projectId: string }; + OrganizationDeployer: undefined; Organizations?: { network?: string }; CoreDAO: undefined; @@ -230,6 +238,15 @@ const navConfig: { Organizations: "orgs", CoreDAO: "core-dao", + // === Projects Program + Projects: "projects", + ProjectsPayment: "projects/payment", + ProjectsCompleteMilestone: + "projects/completeMilestone/:projectId/:milestoneId", + ProjectsManager: "projects/manager/:view", + ProjectsMakeRequest: "projects/make-request", + ProjectsDetail: "projects/:id", + ProjectsConflictSolving: "projects/:projectId/conflicts", // === Organization OrganizationGetStarted: "organization-get-started", diff --git a/packages/utils/sidebar.ts b/packages/utils/sidebar.ts index 05b2227334..ecd93d68d5 100644 --- a/packages/utils/sidebar.ts +++ b/packages/utils/sidebar.ts @@ -12,6 +12,7 @@ import messagesSVG from "../../assets/icons/messages.svg"; import multisigSVG from "../../assets/icons/multisig.svg"; import osmosisCircleSVG from "../../assets/icons/networks/osmosis-circle.svg"; import pathwarSVG from "../../assets/icons/pathwar.svg"; +import projectsProgramSVG from "../../assets/icons/projects-program.svg"; import riotersGameSVG from "../../assets/icons/rioters-game.svg"; import stakingSVG from "../../assets/icons/staking.svg"; import tnsServiceSVG from "../../assets/icons/tns-service.svg"; @@ -123,6 +124,12 @@ export const SIDEBAR_LIST: SidebarRecordType = { route: "RiotGame", icon: riotersGameSVG, }, + projectsProgram: { + title: "Projects Program", + id: "Projects Program", + route: "Projects", + icon: projectsProgramSVG, + }, riotersFooter: { title: "Rioters Footer", id: "Rioters Footer", diff --git a/packages/utils/style/colors.ts b/packages/utils/style/colors.ts index e88e9fb716..da6c983192 100644 --- a/packages/utils/style/colors.ts +++ b/packages/utils/style/colors.ts @@ -42,6 +42,7 @@ export const neutral77 = "#777777"; export const neutral88 = "#888888"; export const neutral99 = "#999999"; export const neutralA3 = "#A3A3A3"; +export const neutralFF = "#FFFFFF"; export const transparentColor = "transparent"; export const additionalRed = "#FFAEAE"; diff --git a/packages/utils/types/dapp-store.ts b/packages/utils/types/dapp-store.ts index 4dc08b1fe9..c58082f197 100644 --- a/packages/utils/types/dapp-store.ts +++ b/packages/utils/types/dapp-store.ts @@ -13,6 +13,7 @@ export interface dAppType { selectedByDefault: boolean; alwaysOn: boolean; order?: number; + devOnly?: boolean; } export interface dAppGroup { diff --git a/packages/utils/wallet/getNativeWallet.ts b/packages/utils/wallet/getNativeWallet.ts index aac55fe3ae..2480aa6a93 100644 --- a/packages/utils/wallet/getNativeWallet.ts +++ b/packages/utils/wallet/getNativeWallet.ts @@ -1,5 +1,5 @@ import { Secp256k1HdWallet } from "@cosmjs/amino"; -import { stringToPath } from "@cosmjs/amino/node_modules/@cosmjs/crypto/build/slip10"; +import { stringToPath } from "@cosmjs/crypto"; import { getValueFor, remove, save } from "./secure-store"; diff --git a/packages/utils/walletProvider.ts b/packages/utils/walletProvider.ts index 2fa980676c..e9554fb221 100644 --- a/packages/utils/walletProvider.ts +++ b/packages/utils/walletProvider.ts @@ -6,4 +6,5 @@ export enum WalletProvider { Adena = "Adena", Store = "Store", Native = "Native", + Gnotest = "Gnotest", } diff --git a/paralint.json b/paralint.json index 83e70cc65b..c979d64a46 100644 --- a/paralint.json +++ b/paralint.json @@ -1,6 +1,6 @@ { "eslint": "eslint --cache --ext .js,.jsx,.ts,.tsx .", - "tsc": "tsc", + "tsc": "tsc --pretty", "depcheck": "depcheck", "unused-exports": "yarn unused-exports", "validate-networks": "yarn validate-networks", diff --git a/tsconfig.json b/tsconfig.json index f37d0c7b51..c838cfa151 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "expo/tsconfig.base", - "target": "ES5", + "target": "ES6", "compilerOptions": { "useDefineForClassFields": false, // this is needed by cosmwasm-ts-codegen clients "strict": true, diff --git a/yarn.lock b/yarn.lock index 299fa27f09..fd2b420606 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2170,6 +2170,13 @@ __metadata: languageName: node linkType: hard +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 + languageName: node + linkType: hard + "@confio/ics23@npm:^0.6.8": version: 0.6.8 resolution: "@confio/ics23@npm:0.6.8" @@ -2288,6 +2295,21 @@ __metadata: languageName: node linkType: hard +"@cosmjs/crypto@npm:0.32.2, @cosmjs/crypto@npm:^0.32.2": + version: 0.32.2 + resolution: "@cosmjs/crypto@npm:0.32.2" + dependencies: + "@cosmjs/encoding": ^0.32.2 + "@cosmjs/math": ^0.32.2 + "@cosmjs/utils": ^0.32.2 + "@noble/hashes": ^1 + bn.js: ^5.2.0 + elliptic: ^6.5.4 + libsodium-wrappers-sumo: ^0.7.11 + checksum: 7600bd48f718b8c352038bf96af325b9597310ccf51d2885bba4603a567b52d59446d193288f519ddf354a8668bd0ffba899c518642b3c9bb039bf24a7261faa + languageName: node + linkType: hard + "@cosmjs/crypto@npm:^0.29.3, @cosmjs/crypto@npm:^0.29.5": version: 0.29.5 resolution: "@cosmjs/crypto@npm:0.29.5" @@ -2318,21 +2340,6 @@ __metadata: languageName: node linkType: hard -"@cosmjs/crypto@npm:^0.32.2": - version: 0.32.2 - resolution: "@cosmjs/crypto@npm:0.32.2" - dependencies: - "@cosmjs/encoding": ^0.32.2 - "@cosmjs/math": ^0.32.2 - "@cosmjs/utils": ^0.32.2 - "@noble/hashes": ^1 - bn.js: ^5.2.0 - elliptic: ^6.5.4 - libsodium-wrappers-sumo: ^0.7.11 - checksum: 7600bd48f718b8c352038bf96af325b9597310ccf51d2885bba4603a567b52d59446d193288f519ddf354a8668bd0ffba899c518642b3c9bb039bf24a7261faa - languageName: node - linkType: hard - "@cosmjs/crypto@npm:^0.32.4": version: 0.32.4 resolution: "@cosmjs/crypto@npm:0.32.4" @@ -2359,6 +2366,17 @@ __metadata: languageName: node linkType: hard +"@cosmjs/encoding@npm:0.32.2, @cosmjs/encoding@npm:^0.32.2": + version: 0.32.2 + resolution: "@cosmjs/encoding@npm:0.32.2" + dependencies: + base64-js: ^1.3.0 + bech32: ^1.1.4 + readonly-date: ^1.0.0 + checksum: a9ee00b730aa7f422cf3946097cb96a0c9d5d7a9f6b3aa31d8e96e4f815220928226ae2133b76e835c781df0231e9691c8ebf1c98f68468e2f936a1d431e4c30 + languageName: node + linkType: hard + "@cosmjs/encoding@npm:^0.20.0": version: 0.20.1 resolution: "@cosmjs/encoding@npm:0.20.1" @@ -2392,17 +2410,6 @@ __metadata: languageName: node linkType: hard -"@cosmjs/encoding@npm:^0.32.2": - version: 0.32.2 - resolution: "@cosmjs/encoding@npm:0.32.2" - dependencies: - base64-js: ^1.3.0 - bech32: ^1.1.4 - readonly-date: ^1.0.0 - checksum: a9ee00b730aa7f422cf3946097cb96a0c9d5d7a9f6b3aa31d8e96e4f815220928226ae2133b76e835c781df0231e9691c8ebf1c98f68468e2f936a1d431e4c30 - languageName: node - linkType: hard - "@cosmjs/encoding@npm:^0.32.4": version: 0.32.4 resolution: "@cosmjs/encoding@npm:0.32.4" @@ -2493,6 +2500,15 @@ __metadata: languageName: node linkType: hard +"@cosmjs/math@npm:0.32.2, @cosmjs/math@npm:^0.32.2": + version: 0.32.2 + resolution: "@cosmjs/math@npm:0.32.2" + dependencies: + bn.js: ^5.2.0 + checksum: 3ea9c811d1b53b61f24c10ae4e3707fb7f926194202e5c57ea5aa1a41996ffc872ce5599967f74cc6f289d30e064d372b7ff30c1128d682847b865ca597aca59 + languageName: node + linkType: hard + "@cosmjs/math@npm:^0.20.0": version: 0.20.1 resolution: "@cosmjs/math@npm:0.20.1" @@ -2520,15 +2536,6 @@ __metadata: languageName: node linkType: hard -"@cosmjs/math@npm:^0.32.2": - version: 0.32.2 - resolution: "@cosmjs/math@npm:0.32.2" - dependencies: - bn.js: ^5.2.0 - checksum: 3ea9c811d1b53b61f24c10ae4e3707fb7f926194202e5c57ea5aa1a41996ffc872ce5599967f74cc6f289d30e064d372b7ff30c1128d682847b865ca597aca59 - languageName: node - linkType: hard - "@cosmjs/math@npm:^0.32.4": version: 0.32.4 resolution: "@cosmjs/math@npm:0.32.4" @@ -2915,6 +2922,42 @@ __metadata: languageName: node linkType: hard +"@cypress/request@npm:^3.0.0": + version: 3.0.1 + resolution: "@cypress/request@npm:3.0.1" + dependencies: + aws-sign2: ~0.7.0 + aws4: ^1.8.0 + caseless: ~0.12.0 + combined-stream: ~1.0.6 + extend: ~3.0.2 + forever-agent: ~0.6.1 + form-data: ~2.3.2 + http-signature: ~1.3.6 + is-typedarray: ~1.0.0 + isstream: ~0.1.2 + json-stringify-safe: ~5.0.1 + mime-types: ~2.1.19 + performance-now: ^2.1.0 + qs: 6.10.4 + safe-buffer: ^5.1.2 + tough-cookie: ^4.1.3 + tunnel-agent: ^0.6.0 + uuid: ^8.3.2 + checksum: 7175522ebdbe30e3c37973e204c437c23ce659e58d5939466615bddcd58d778f3a8ea40f087b965ae8b8138ea8d102b729c6eb18c6324f121f3778f4a2e8e727 + languageName: node + linkType: hard + +"@cypress/xvfb@npm:^1.2.4": + version: 1.2.4 + resolution: "@cypress/xvfb@npm:1.2.4" + dependencies: + debug: ^3.1.0 + lodash.once: ^4.1.1 + checksum: 7bdcdaeb1bb692ec9d9bf8ec52538aa0bead6764753f4a067a171a511807a43fab016f7285a56bef6a606c2467ff3f1365e1ad2d2d583b81beed849ee1573fd1 + languageName: node + linkType: hard + "@dotlottie/common@npm:0.7.9": version: 0.7.9 resolution: "@dotlottie/common@npm:0.7.9" @@ -4213,6 +4256,13 @@ __metadata: languageName: node linkType: hard +"@faker-js/faker@npm:^8.4.1": + version: 8.4.1 + resolution: "@faker-js/faker@npm:8.4.1" + checksum: d802d531f8929562715adc279cfec763c9a4bc596ec67b0ce43fd0ae61b285d2b0eec6f1f4aa852452a63721a842fe7e81926dce7bd92acca94b01e2a1f55f5a + languageName: node + linkType: hard + "@gar/promisify@npm:^1.0.1": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -4291,6 +4341,15 @@ __metadata: languageName: node linkType: hard +"@hookform/resolvers@npm:^3.6.0": + version: 3.6.0 + resolution: "@hookform/resolvers@npm:3.6.0" + peerDependencies: + react-hook-form: ^7.0.0 + checksum: a230fd2d0b571ddb8795f587284211e17f942de6b75ba235f613f54d3c17561e3a6483deee48c7dd5a6f52131769c5e59e4ed04823447084c28b6191d7e2d9dd + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.13": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" @@ -5673,6 +5732,16 @@ __metadata: languageName: node linkType: hard +"@react-native-picker/picker@npm:2.6.1": + version: 2.6.1 + resolution: "@react-native-picker/picker@npm:2.6.1" + peerDependencies: + react: ">=16" + react-native: ">=0.57" + checksum: a8c58d5d945350b70fd8b5d66dfe97dd04641277db7c702912d8c41ae4da94cfef8b0b73bfa493adfdee8f089af6e0316872c3ee61a4efc18528e6e25a5452bc + languageName: node + linkType: hard + "@react-native/assets-registry@npm:0.73.1, @react-native/assets-registry@npm:~0.73.1": version: 0.73.1 resolution: "@react-native/assets-registry@npm:0.73.1" @@ -6825,6 +6894,20 @@ __metadata: languageName: node linkType: hard +"@types/sinonjs__fake-timers@npm:8.1.1": + version: 8.1.1 + resolution: "@types/sinonjs__fake-timers@npm:8.1.1" + checksum: ca09d54d47091d87020824a73f026300fa06b17cd9f2f9b9387f28b549364b141ef194ee28db762f6588de71d8febcd17f753163cb7ea116b8387c18e80ebd5c + languageName: node + linkType: hard + +"@types/sizzle@npm:^2.3.2": + version: 2.3.8 + resolution: "@types/sizzle@npm:2.3.8" + checksum: 2ac62443dc917f5f903cbd9afc51c7d6cc1c6569b4e1a15faf04aea5b13b486e7f208650014c3dc4fed34653eded3e00fe5abffe0e6300cbf0e8a01beebf11a6 + languageName: node + linkType: hard + "@types/stack-utils@npm:^2.0.0": version: 2.0.3 resolution: "@types/stack-utils@npm:2.0.3" @@ -7339,6 +7422,13 @@ __metadata: languageName: node linkType: hard +"ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: a9c2ec842038a1fabc7db9ece7d3177e2fe1c5dc6f0c51ecfbf5f39911427b89c00b5dc6b8bd95f82a26e9b16aaae2e83d45f060e98070ce4d1333038edceb0e + languageName: node + linkType: hard + "ansi-escapes@npm:^2.0.0": version: 2.0.0 resolution: "ansi-escapes@npm:2.0.0" @@ -7353,7 +7443,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.2": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -7487,6 +7577,13 @@ __metadata: languageName: node linkType: hard +"arch@npm:^2.2.0": + version: 2.2.0 + resolution: "arch@npm:2.2.0" + checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f + languageName: node + linkType: hard + "arg@npm:5.0.2": version: 5.0.2 resolution: "arg@npm:5.0.2" @@ -7638,6 +7735,22 @@ __metadata: languageName: node linkType: hard +"asn1@npm:~0.2.3": + version: 0.2.6 + resolution: "asn1@npm:0.2.6" + dependencies: + safer-buffer: ~2.1.0 + checksum: 39f2ae343b03c15ad4f238ba561e626602a3de8d94ae536c46a4a93e69578826305366dc09fbb9b56aec39b4982a463682f259c38e59f6fa380cd72cd61e493d + languageName: node + linkType: hard + +"assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": + version: 1.0.0 + resolution: "assert-plus@npm:1.0.0" + checksum: 19b4340cb8f0e6a981c07225eacac0e9d52c2644c080198765d63398f0075f83bbc0c8e95474d54224e297555ad0d631c1dcd058adb1ddc2437b41a6b424ac64 + languageName: node + linkType: hard + "assert@npm:^2.1.0": version: 2.1.0 resolution: "assert@npm:2.1.0" @@ -7676,6 +7789,13 @@ __metadata: languageName: node linkType: hard +"astral-regex@npm:^2.0.0": + version: 2.0.0 + resolution: "astral-regex@npm:2.0.0" + checksum: 876231688c66400473ba505731df37ea436e574dd524520294cc3bbc54ea40334865e01fa0d074d74d036ee874ee7e62f486ea38bc421ee8e6a871c06f011766 + languageName: node + linkType: hard + "async-limiter@npm:~1.0.0": version: 1.0.1 resolution: "async-limiter@npm:1.0.1" @@ -7683,6 +7803,13 @@ __metadata: languageName: node linkType: hard +"async@npm:^3.2.0": + version: 3.2.5 + resolution: "async@npm:3.2.5" + checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4 + languageName: node + linkType: hard + "asynciterator.prototype@npm:^1.0.0": version: 1.0.0 resolution: "asynciterator.prototype@npm:1.0.0" @@ -7720,6 +7847,20 @@ __metadata: languageName: node linkType: hard +"aws-sign2@npm:~0.7.0": + version: 0.7.0 + resolution: "aws-sign2@npm:0.7.0" + checksum: b148b0bb0778098ad8cf7e5fc619768bcb51236707ca1d3e5b49e41b171166d8be9fdc2ea2ae43d7decf02989d0aaa3a9c4caa6f320af95d684de9b548a71525 + languageName: node + linkType: hard + +"aws4@npm:^1.8.0": + version: 1.12.0 + resolution: "aws4@npm:1.12.0" + checksum: 68f79708ac7c335992730bf638286a3ee0a645cf12575d557860100767c500c08b30e24726b9f03265d74116417f628af78509e1333575e9f8d52a80edfe8cbc + languageName: node + linkType: hard + "axios@npm:0.21.1": version: 0.21.1 resolution: "axios@npm:0.21.1" @@ -8014,6 +8155,15 @@ __metadata: languageName: node linkType: hard +"bcrypt-pbkdf@npm:^1.0.0": + version: 1.0.2 + resolution: "bcrypt-pbkdf@npm:1.0.2" + dependencies: + tweetnacl: ^0.14.3 + checksum: 4edfc9fe7d07019609ccf797a2af28351736e9d012c8402a07120c4453a3b789a15f2ee1530dc49eee8f7eb9379331a8dd4b3766042b9e502f74a68e7f662291 + languageName: node + linkType: hard + "bech32@npm:1.1.4, bech32@npm:^1.1.3, bech32@npm:^1.1.4": version: 1.1.4 resolution: "bech32@npm:1.1.4" @@ -8097,6 +8247,20 @@ __metadata: languageName: node linkType: hard +"blob-util@npm:^2.0.2": + version: 2.0.2 + resolution: "blob-util@npm:2.0.2" + checksum: d543e6b92e4ca715ca33c78e89a07a2290d43e5b2bc897d7ec588c5c7bbf59df93e45225ac0c9258aa6ce4320358990f99c9288f1c48280f8ec5d7a2e088d19b + languageName: node + linkType: hard + +"bluebird@npm:^3.7.2": + version: 3.7.2 + resolution: "bluebird@npm:3.7.2" + checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef + languageName: node + linkType: hard + "blueimp-md5@npm:^2.10.0": version: 2.19.0 resolution: "blueimp-md5@npm:2.19.0" @@ -8365,7 +8529,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.4.3, buffer@npm:^5.5.0, buffer@npm:^5.6.0": +"buffer@npm:^5.4.3, buffer@npm:^5.5.0, buffer@npm:^5.6.0, buffer@npm:^5.7.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -8477,6 +8641,13 @@ __metadata: languageName: node linkType: hard +"cachedir@npm:^2.3.0": + version: 2.4.0 + resolution: "cachedir@npm:2.4.0" + checksum: 43198514eaa61f65b5535ed29ad651f22836fba3868ed58a6a87731f05462f317d39098fa3ac778801c25455483c9b7f32a2fcad1f690a978947431f12a0f4d0 + languageName: node + linkType: hard + "call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5": version: 1.0.5 resolution: "call-bind@npm:1.0.5" @@ -8569,6 +8740,13 @@ __metadata: languageName: node linkType: hard +"caseless@npm:~0.12.0": + version: 0.12.0 + resolution: "caseless@npm:0.12.0" + checksum: b43bd4c440aa1e8ee6baefee8063b4850fd0d7b378f6aabc796c9ec8cb26d27fb30b46885350777d9bd079c5256c0e1329ad0dc7c2817e0bb466810ebb353751 + languageName: node + linkType: hard + "chain-registry@npm:1.10.0": version: 1.10.0 resolution: "chain-registry@npm:1.10.0" @@ -8644,6 +8822,13 @@ __metadata: languageName: node linkType: hard +"check-more-types@npm:^2.24.0": + version: 2.24.0 + resolution: "check-more-types@npm:2.24.0" + checksum: b09080ec3404d20a4b0ead828994b2e5913236ef44ed3033a27062af0004cf7d2091fbde4b396bf13b7ce02fb018bc9960b48305e6ab2304cd82d73ed7a51ef4 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -8797,6 +8982,29 @@ __metadata: languageName: node linkType: hard +"cli-table3@npm:~0.6.1": + version: 0.6.5 + resolution: "cli-table3@npm:0.6.5" + dependencies: + "@colors/colors": 1.5.0 + string-width: ^4.2.0 + dependenciesMeta: + "@colors/colors": + optional: true + checksum: ab7afbf4f8597f1c631f3ee6bb3481d0bfeac8a3b81cffb5a578f145df5c88003b6cfff46046a7acae86596fdd03db382bfa67f20973b6b57425505abc47e42c + languageName: node + linkType: hard + +"cli-truncate@npm:^2.1.0": + version: 2.1.0 + resolution: "cli-truncate@npm:2.1.0" + dependencies: + slice-ansi: ^3.0.0 + string-width: ^4.2.0 + checksum: bf1e4e6195392dc718bf9cd71f317b6300dc4a9191d052f31046b8773230ece4fa09458813bf0e3455a5e68c0690d2ea2c197d14a8b85a7b5e01c97f4b5feb5d + languageName: node + linkType: hard + "cli-truncate@npm:^4.0.0": version: 4.0.0 resolution: "cli-truncate@npm:4.0.0" @@ -8964,7 +9172,7 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.20": +"colorette@npm:^2.0.16, colorette@npm:^2.0.20": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d @@ -8978,7 +9186,7 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8": +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -9015,6 +9223,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^6.2.1": + version: 6.2.1 + resolution: "commander@npm:6.2.1" + checksum: d7090410c0de6bc5c67d3ca41c41760d6d268f3c799e530aafb73b7437d1826bbf0d2a3edac33f8b57cc9887b4a986dce307fa5557e109be40eadb7c43b21742 + languageName: node + linkType: hard + "commander@npm:^7.2.0": version: 7.2.0 resolution: "commander@npm:7.2.0" @@ -9029,6 +9244,13 @@ __metadata: languageName: node linkType: hard +"common-tags@npm:^1.8.0": + version: 1.8.2 + resolution: "common-tags@npm:1.8.2" + checksum: 767a6255a84bbc47df49a60ab583053bb29a7d9687066a18500a516188a062c4e4cd52de341f22de0b07062e699b1b8fe3cfa1cb55b241cb9301aeb4f45b4dff + languageName: node + linkType: hard + "commondir@npm:^1.0.1": version: 1.0.1 resolution: "commondir@npm:1.0.1" @@ -9134,6 +9356,13 @@ __metadata: languageName: node linkType: hard +"core-util-is@npm:1.0.2": + version: 1.0.2 + resolution: "core-util-is@npm:1.0.2" + checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab + languageName: node + linkType: hard + "core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -9440,6 +9669,58 @@ __metadata: languageName: node linkType: hard +"cypress@npm:^13.9.0": + version: 13.9.0 + resolution: "cypress@npm:13.9.0" + dependencies: + "@cypress/request": ^3.0.0 + "@cypress/xvfb": ^1.2.4 + "@types/sinonjs__fake-timers": 8.1.1 + "@types/sizzle": ^2.3.2 + arch: ^2.2.0 + blob-util: ^2.0.2 + bluebird: ^3.7.2 + buffer: ^5.7.1 + cachedir: ^2.3.0 + chalk: ^4.1.0 + check-more-types: ^2.24.0 + cli-cursor: ^3.1.0 + cli-table3: ~0.6.1 + commander: ^6.2.1 + common-tags: ^1.8.0 + dayjs: ^1.10.4 + debug: ^4.3.4 + enquirer: ^2.3.6 + eventemitter2: 6.4.7 + execa: 4.1.0 + executable: ^4.1.1 + extract-zip: 2.0.1 + figures: ^3.2.0 + fs-extra: ^9.1.0 + getos: ^3.2.1 + is-ci: ^3.0.1 + is-installed-globally: ~0.4.0 + lazy-ass: ^1.6.0 + listr2: ^3.8.3 + lodash: ^4.17.21 + log-symbols: ^4.0.0 + minimist: ^1.2.8 + ospath: ^1.2.2 + pretty-bytes: ^5.6.0 + process: ^0.11.10 + proxy-from-env: 1.0.0 + request-progress: ^3.0.0 + semver: ^7.5.3 + supports-color: ^8.1.1 + tmp: ~0.2.1 + untildify: ^4.0.0 + yauzl: ^2.10.0 + bin: + cypress: bin/cypress + checksum: ec2e0237d2a38d53d4e5c05fe1c37f7fe0bb13de798045ba091b6a5cf0edd7b3a282f4d8f4c063e3988ee048d06dcf4fef91d83d63a56cee9f9ff68685f3946d + languageName: node + linkType: hard + "d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:^3.1.6": version: 3.2.4 resolution: "d3-array@npm:3.2.4" @@ -9564,6 +9845,22 @@ __metadata: languageName: node linkType: hard +"dashdash@npm:^1.12.0": + version: 1.14.1 + resolution: "dashdash@npm:1.14.1" + dependencies: + assert-plus: ^1.0.0 + checksum: 3634c249570f7f34e3d34f866c93f866c5b417f0dd616275decae08147dcdf8fccfaa5947380ccfb0473998ea3a8057c0b4cd90c875740ee685d0624b2983598 + languageName: node + linkType: hard + +"dayjs@npm:^1.10.4": + version: 1.11.11 + resolution: "dayjs@npm:1.11.11" + checksum: 84788275aad8a87fee4f1ce4be08861df29687aae6b7b43dd65350118a37dda56772a3902f802cb2dc651dfed447a5a8df62d88f0fb900dba8333e411190a5d5 + languageName: node + linkType: hard + "dayjs@npm:^1.8.15": version: 1.11.10 resolution: "dayjs@npm:1.11.10" @@ -10092,6 +10389,16 @@ __metadata: languageName: node linkType: hard +"ecc-jsbn@npm:~0.1.1": + version: 0.1.2 + resolution: "ecc-jsbn@npm:0.1.2" + dependencies: + jsbn: ~0.1.0 + safer-buffer: ^2.1.0 + checksum: 22fef4b6203e5f31d425f5b711eb389e4c6c2723402e389af394f8411b76a488fa414d309d866e2b577ce3e8462d344205545c88a8143cc21752a5172818888a + languageName: node + linkType: hard + "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -10224,6 +10531,16 @@ __metadata: languageName: node linkType: hard +"enquirer@npm:^2.3.6": + version: 2.4.1 + resolution: "enquirer@npm:2.4.1" + dependencies: + ansi-colors: ^4.1.1 + strip-ansi: ^6.0.1 + checksum: f080f11a74209647dbf347a7c6a83c8a47ae1ebf1e75073a808bc1088eb780aa54075bfecd1bcdb3e3c724520edb8e6ee05da031529436b421b71066fcc48cb5 + languageName: node + linkType: hard + "entities@npm:^4.2.0, entities@npm:^4.4.0, entities@npm:^4.5.0": version: 4.5.0 resolution: "entities@npm:4.5.0" @@ -10947,6 +11264,13 @@ __metadata: languageName: node linkType: hard +"eventemitter2@npm:6.4.7": + version: 6.4.7 + resolution: "eventemitter2@npm:6.4.7" + checksum: 1b36a77e139d6965ebf3a36c01fa00c089ae6b80faa1911e52888f40b3a7057b36a2cc45dcd1ad87cda3798fe7b97a0aabcbb8175a8b96092a23bb7d0f039e66 + languageName: node + linkType: hard + "eventemitter3@npm:1.x.x": version: 1.2.0 resolution: "eventemitter3@npm:1.2.0" @@ -10993,6 +11317,23 @@ __metadata: languageName: node linkType: hard +"execa@npm:4.1.0": + version: 4.1.0 + resolution: "execa@npm:4.1.0" + dependencies: + cross-spawn: ^7.0.0 + get-stream: ^5.0.0 + human-signals: ^1.1.1 + is-stream: ^2.0.0 + merge-stream: ^2.0.0 + npm-run-path: ^4.0.0 + onetime: ^5.1.0 + signal-exit: ^3.0.2 + strip-final-newline: ^2.0.0 + checksum: e30d298934d9c52f90f3847704fd8224e849a081ab2b517bbc02f5f7732c24e56a21f14cb96a08256deffeb2d12b2b7cb7e2b014a12fb36f8d3357e06417ed55 + languageName: node + linkType: hard + "execa@npm:^1.0.0": version: 1.0.0 resolution: "execa@npm:1.0.0" @@ -11025,6 +11366,15 @@ __metadata: languageName: node linkType: hard +"executable@npm:^4.1.1": + version: 4.1.1 + resolution: "executable@npm:4.1.1" + dependencies: + pify: ^2.2.0 + checksum: f01927ce59bccec804e171bf859a26e362c1f50aa9ebc69f7cafdcce3859d29d4b6267fd47237c18b0a1830614bd3f0ee14b7380d9bad18a4e7af9b5f0b6984f + languageName: node + linkType: hard + "expand-tilde@npm:^2.0.0, expand-tilde@npm:^2.0.2": version: 2.0.2 resolution: "expand-tilde@npm:2.0.2" @@ -11361,7 +11711,7 @@ __metadata: languageName: node linkType: hard -"extend@npm:^3.0.0": +"extend@npm:^3.0.0, extend@npm:~3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 @@ -11397,7 +11747,7 @@ __metadata: languageName: node linkType: hard -"extract-zip@npm:^2.0.1": +"extract-zip@npm:2.0.1, extract-zip@npm:^2.0.1": version: 2.0.1 resolution: "extract-zip@npm:2.0.1" dependencies: @@ -11414,6 +11764,20 @@ __metadata: languageName: node linkType: hard +"extsprintf@npm:1.3.0": + version: 1.3.0 + resolution: "extsprintf@npm:1.3.0" + checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 + languageName: node + linkType: hard + +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 + languageName: node + linkType: hard + "eyes@npm:^0.1.8": version: 0.1.8 resolution: "eyes@npm:0.1.8" @@ -11598,6 +11962,15 @@ __metadata: languageName: node linkType: hard +"figures@npm:^3.2.0": + version: 3.2.0 + resolution: "figures@npm:3.2.0" + dependencies: + escape-string-regexp: ^1.0.5 + checksum: 85a6ad29e9aca80b49b817e7c89ecc4716ff14e3779d9835af554db91bac41c0f289c418923519392a1e582b4d10482ad282021330cd045bb7b80c84152f2a2b + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -11792,6 +12165,13 @@ __metadata: languageName: node linkType: hard +"forever-agent@npm:~0.6.1": + version: 0.6.1 + resolution: "forever-agent@npm:0.6.1" + checksum: 766ae6e220f5fe23676bb4c6a99387cec5b7b62ceb99e10923376e27bfea72f3c3aeec2ba5f45f3f7ba65d6616965aa7c20b15002b6860833bb6e394dea546a8 + languageName: node + linkType: hard + "form-data@npm:^2.3.3": version: 2.5.1 resolution: "form-data@npm:2.5.1" @@ -11825,6 +12205,17 @@ __metadata: languageName: node linkType: hard +"form-data@npm:~2.3.2": + version: 2.3.3 + resolution: "form-data@npm:2.3.3" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.6 + mime-types: ^2.1.12 + checksum: 10c1780fa13dbe1ff3100114c2ce1f9307f8be10b14bf16e103815356ff567b6be39d70fc4a40f8990b9660012dc24b0f5e1dde1b6426166eb23a445ba068ca3 + languageName: node + linkType: hard + "freeport-async@npm:2.0.0": version: 2.0.0 resolution: "freeport-async@npm:2.0.0" @@ -12014,7 +12405,7 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^5.1.0": +"get-stream@npm:^5.0.0, get-stream@npm:^5.1.0": version: 5.2.0 resolution: "get-stream@npm:5.2.0" dependencies: @@ -12056,6 +12447,24 @@ __metadata: languageName: node linkType: hard +"getos@npm:^3.2.1": + version: 3.2.1 + resolution: "getos@npm:3.2.1" + dependencies: + async: ^3.2.0 + checksum: 42fd78a66d47cebd3e09de5566cc0044e034b08f4a000a310dbd89a77b02c65d8f4002554bfa495ea5bdc4fa9d515f5ac785a7cc474ba45383cc697f865eeaf1 + languageName: node + linkType: hard + +"getpass@npm:^0.1.1": + version: 0.1.7 + resolution: "getpass@npm:0.1.7" + dependencies: + assert-plus: ^1.0.0 + checksum: ab18d55661db264e3eac6012c2d3daeafaab7a501c035ae0ccb193c3c23e9849c6e29b6ac762b9c2adae460266f925d55a3a2a3a3c8b94be2f222df94d70c046 + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -12168,6 +12577,15 @@ __metadata: languageName: node linkType: hard +"global-dirs@npm:^3.0.0": + version: 3.0.1 + resolution: "global-dirs@npm:3.0.1" + dependencies: + ini: 2.0.0 + checksum: 70147b80261601fd40ac02a104581432325c1c47329706acd773f3a6ce99bb36d1d996038c85ccacd482ad22258ec233c586b6a91535b1a116b89663d49d6438 + languageName: node + linkType: hard + "global-modules@npm:^1.0.0": version: 1.0.0 resolution: "global-modules@npm:1.0.0" @@ -12568,6 +12986,17 @@ __metadata: languageName: node linkType: hard +"http-signature@npm:~1.3.6": + version: 1.3.6 + resolution: "http-signature@npm:1.3.6" + dependencies: + assert-plus: ^1.0.0 + jsprim: ^2.0.2 + sshpk: ^1.14.1 + checksum: 10be2af4764e71fee0281392937050201ee576ac755c543f570d6d87134ce5e858663fe999a7adb3e4e368e1e356d0d7fec6b9542295b875726ff615188e7a0c + languageName: node + linkType: hard + "http2-wrapper@npm:^1.0.0-beta.5.2": version: 1.0.3 resolution: "http2-wrapper@npm:1.0.3" @@ -12598,6 +13027,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^1.1.1": + version: 1.1.1 + resolution: "human-signals@npm:1.1.1" + checksum: d587647c9e8ec24e02821b6be7de5a0fc37f591f6c4e319b3054b43fd4c35a70a94c46fc74d8c1a43c47fde157d23acd7421f375e1c1365b09a16835b8300205 + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -12750,6 +13186,13 @@ __metadata: languageName: node linkType: hard +"ini@npm:2.0.0": + version: 2.0.0 + resolution: "ini@npm:2.0.0" + checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e + languageName: node + linkType: hard + "ini@npm:^1.3.4, ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" @@ -12999,6 +13442,17 @@ __metadata: languageName: node linkType: hard +"is-ci@npm:^3.0.1": + version: 3.0.1 + resolution: "is-ci@npm:3.0.1" + dependencies: + ci-info: ^3.2.0 + bin: + is-ci: bin.js + checksum: 192c66dc7826d58f803ecae624860dccf1899fc1f3ac5505284c0a5cf5f889046ffeb958fa651e5725d5705c5bcb14f055b79150ea5fcad7456a9569de60260e + languageName: node + linkType: hard + "is-core-module@npm:^2.12.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" @@ -13113,6 +13567,16 @@ __metadata: languageName: node linkType: hard +"is-installed-globally@npm:~0.4.0": + version: 0.4.0 + resolution: "is-installed-globally@npm:0.4.0" + dependencies: + global-dirs: ^3.0.0 + is-path-inside: ^3.0.2 + checksum: 3359840d5982d22e9b350034237b2cda2a12bac1b48a721912e1ab8e0631dd07d45a2797a120b7b87552759a65ba03e819f1bd63f2d7ab8657ec0b44ee0bf399 + languageName: node + linkType: hard + "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -13324,6 +13788,13 @@ __metadata: languageName: node linkType: hard +"is-typedarray@npm:~1.0.0": + version: 1.0.0 + resolution: "is-typedarray@npm:1.0.0" + checksum: 3508c6cd0a9ee2e0df2fa2e9baabcdc89e911c7bd5cf64604586697212feec525aa21050e48affb5ffc3df20f0f5d2e2cf79b08caa64e1ccc9578e251763aef7 + languageName: node + linkType: hard + "is-unicode-supported@npm:^0.1.0": version: 0.1.0 resolution: "is-unicode-supported@npm:0.1.0" @@ -13440,6 +13911,13 @@ __metadata: languageName: node linkType: hard +"isstream@npm:~0.1.2": + version: 0.1.2 + resolution: "isstream@npm:0.1.2" + checksum: 1eb2fe63a729f7bdd8a559ab552c69055f4f48eb5c2f03724430587c6f450783c8f1cd936c1c952d0a927925180fcc892ebd5b174236cf1065d4bd5bdb37e963 + languageName: node + linkType: hard + "istanbul-lib-coverage@npm:^3.2.0": version: 3.2.2 resolution: "istanbul-lib-coverage@npm:3.2.2" @@ -13774,6 +14252,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:~0.1.0": + version: 0.1.1 + resolution: "jsbn@npm:0.1.1" + checksum: e5ff29c1b8d965017ef3f9c219dacd6e40ad355c664e277d31246c90545a02e6047018c16c60a00f36d561b3647215c41894f5d869ada6908a2e0ce4200c88f2 + languageName: node + linkType: hard + "jsc-android@npm:^250231.0.0": version: 250231.0.0 resolution: "jsc-android@npm:250231.0.0" @@ -13895,6 +14380,13 @@ __metadata: languageName: node linkType: hard +"json-schema@npm:0.4.0": + version: 0.4.0 + resolution: "json-schema@npm:0.4.0" + checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72 + languageName: node + linkType: hard + "json-stable-stringify-without-jsonify@npm:^1.0.1": version: 1.0.1 resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" @@ -13902,7 +14394,7 @@ __metadata: languageName: node linkType: hard -"json-stringify-safe@npm:^5.0.1": +"json-stringify-safe@npm:^5.0.1, json-stringify-safe@npm:~5.0.1": version: 5.0.1 resolution: "json-stringify-safe@npm:5.0.1" checksum: 48ec0adad5280b8a96bb93f4563aa1667fd7a36334f79149abd42446d0989f2ddc58274b479f4819f1f00617957e6344c886c55d05a4e15ebb4ab931e4a6a8ee @@ -13961,6 +14453,18 @@ __metadata: languageName: node linkType: hard +"jsprim@npm:^2.0.2": + version: 2.0.2 + resolution: "jsprim@npm:2.0.2" + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + checksum: d175f6b1991e160cb0aa39bc857da780e035611986b5492f32395411879fdaf4e513d98677f08f7352dac93a16b66b8361c674b86a3fa406e2e7af6b26321838 + languageName: node + linkType: hard + "jsx-ast-utils@npm:^2.4.1 || ^3.0.0": version: 3.3.5 resolution: "jsx-ast-utils@npm:3.3.5" @@ -14017,6 +14521,13 @@ __metadata: languageName: node linkType: hard +"lazy-ass@npm:^1.6.0": + version: 1.6.0 + resolution: "lazy-ass@npm:1.6.0" + checksum: 5a3ebb17915b03452320804466345382a6c25ac782ec4874fecdb2385793896cd459be2f187dc7def8899180c32ee0ab9a1aa7fe52193ac3ff3fe29bb0591729 + languageName: node + linkType: hard + "leaflet.markercluster@npm:^1.5.3": version: 1.5.3 resolution: "leaflet.markercluster@npm:1.5.3" @@ -14210,6 +14721,27 @@ __metadata: languageName: node linkType: hard +"listr2@npm:^3.8.3": + version: 3.14.0 + resolution: "listr2@npm:3.14.0" + dependencies: + cli-truncate: ^2.1.0 + colorette: ^2.0.16 + log-update: ^4.0.0 + p-map: ^4.0.0 + rfdc: ^1.3.0 + rxjs: ^7.5.1 + through: ^2.3.8 + wrap-ansi: ^7.0.0 + peerDependencies: + enquirer: ">= 2.3.0 < 3" + peerDependenciesMeta: + enquirer: + optional: true + checksum: fdb8b2d6bdf5df9371ebd5082bee46c6d0ca3d1e5f2b11fbb5a127839855d5f3da9d4968fce94f0a5ec67cac2459766abbb1faeef621065ebb1829b11ef9476d + languageName: node + linkType: hard + "listr2@npm:^8.0.1": version: 8.0.1 resolution: "listr2@npm:8.0.1" @@ -14284,6 +14816,13 @@ __metadata: languageName: node linkType: hard +"lodash.once@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.once@npm:4.1.1" + checksum: d768fa9f9b4e1dc6453be99b753906f58990e0c45e7b2ca5a3b40a33111e5d17f6edf2f768786e2716af90a8e78f8f91431ab8435f761fef00f9b0c256f6d245 + languageName: node + linkType: hard + "lodash.throttle@npm:^4.1.1": version: 4.1.1 resolution: "lodash.throttle@npm:4.1.1" @@ -14307,7 +14846,7 @@ __metadata: languageName: node linkType: hard -"log-symbols@npm:^4.1.0": +"log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": version: 4.1.0 resolution: "log-symbols@npm:4.1.0" dependencies: @@ -14317,6 +14856,18 @@ __metadata: languageName: node linkType: hard +"log-update@npm:^4.0.0": + version: 4.0.0 + resolution: "log-update@npm:4.0.0" + dependencies: + ansi-escapes: ^4.3.0 + cli-cursor: ^3.1.0 + slice-ansi: ^4.0.0 + wrap-ansi: ^6.2.0 + checksum: ae2f85bbabc1906034154fb7d4c4477c79b3e703d22d78adee8b3862fa913942772e7fa11713e3d96fb46de4e3cabefbf5d0a544344f03b58d3c4bff52aa9eb2 + languageName: node + linkType: hard + "log-update@npm:^6.0.0": version: 6.0.0 resolution: "log-update@npm:6.0.0" @@ -14942,7 +15493,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:~2.1.19, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -15061,7 +15612,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.6": +"minimist@npm:^1.2.0, minimist@npm:^1.2.6, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 @@ -15593,7 +16144,7 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^4.0.1": +"npm-run-path@npm:^4.0.0, npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" dependencies: @@ -15903,6 +16454,13 @@ __metadata: languageName: node linkType: hard +"ospath@npm:^1.2.2": + version: 1.2.2 + resolution: "ospath@npm:1.2.2" + checksum: 505f48a4f4f1c557d6c656ec985707726e3714721680139be037613e903aa8c8fa4ddd8d1342006f9b2dc0065e6e20f8b7bea2ee05354f31257044790367b347 + languageName: node + linkType: hard + "p-cancelable@npm:^2.0.0": version: 2.1.1 resolution: "p-cancelable@npm:2.1.1" @@ -16217,6 +16775,13 @@ __metadata: languageName: node linkType: hard +"performance-now@npm:^2.1.0": + version: 2.1.0 + resolution: "performance-now@npm:2.1.0" + checksum: 534e641aa8f7cba160f0afec0599b6cecefbb516a2e837b512be0adbe6c1da5550e89c78059c7fabc5c9ffdf6627edabe23eb7c518c4500067a898fa65c2b550 + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -16238,6 +16803,13 @@ __metadata: languageName: node linkType: hard +"pify@npm:^2.2.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba + languageName: node + linkType: hard + "pify@npm:^4.0.1": version: 4.0.1 resolution: "pify@npm:4.0.1" @@ -16421,7 +16993,7 @@ __metadata: languageName: node linkType: hard -"pretty-bytes@npm:5.6.0": +"pretty-bytes@npm:5.6.0, pretty-bytes@npm:^5.6.0": version: 5.6.0 resolution: "pretty-bytes@npm:5.6.0" checksum: 9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd @@ -16465,7 +17037,7 @@ __metadata: languageName: node linkType: hard -"process@npm:^0.11.1": +"process@npm:^0.11.1, process@npm:^0.11.10": version: 0.11.10 resolution: "process@npm:0.11.10" checksum: bfcce49814f7d172a6e6a14d5fa3ac92cc3d0c3b9feb1279774708a719e19acd673995226351a082a9ae99978254e320ccda4240ddc474ba31a76c79491ca7c3 @@ -16610,6 +17182,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:1.0.0": + version: 1.0.0 + resolution: "proxy-from-env@npm:1.0.0" + checksum: 292e28d1de0c315958d71d8315eb546dd3cd8c8cbc2dab7c54eeb9f5c17f421771964ad0b5e1f77011bab2305bdae42e1757ce33bdb1ccc3e87732322a8efcf1 + languageName: node + linkType: hard + "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -16617,6 +17196,13 @@ __metadata: languageName: node linkType: hard +"psl@npm:^1.1.33": + version: 1.9.0 + resolution: "psl@npm:1.9.0" + checksum: 20c4277f640c93d393130673f392618e9a8044c6c7bf61c53917a0fddb4952790f5f362c6c730a9c32b124813e173733f9895add8d26f566ed0ea0654b2e711d + languageName: node + linkType: hard + "public-encrypt@npm:^4.0.0": version: 4.0.3 resolution: "public-encrypt@npm:4.0.3" @@ -16678,6 +17264,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.10.4": + version: 6.10.4 + resolution: "qs@npm:6.10.4" + dependencies: + side-channel: ^1.0.4 + checksum: 31e4fedd759d01eae52dde6692abab175f9af3e639993c5caaa513a2a3607b34d8058d3ae52ceeccf37c3025f22ed5e90e9ddd6c2537e19c0562ddd10dc5b1eb + languageName: node + linkType: hard + "query-string@npm:^7.1.3": version: 7.1.3 resolution: "query-string@npm:7.1.3" @@ -16690,6 +17285,13 @@ __metadata: languageName: node linkType: hard +"querystringify@npm:^2.1.1": + version: 2.2.0 + resolution: "querystringify@npm:2.2.0" + checksum: 5641ea231bad7ef6d64d9998faca95611ed4b11c2591a8cae741e178a974f6a8e0ebde008475259abe1621cb15e692404e6b6626e927f7b849d5c09392604b15 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -16968,6 +17570,16 @@ __metadata: languageName: node linkType: hard +"react-native-hoverable@npm:^0.2.0": + version: 0.2.0 + resolution: "react-native-hoverable@npm:0.2.0" + peerDependencies: + react: "*" + react-native: "*" + checksum: 9ca468d07c660ea30013da3921e09fdb42b479ce9f69e867e60bd56dbb49cb407998e07d3aa2ab6662e55f7b1cec9bc806ba9517ca9fd31839c1ebcbd4a7d26f + languageName: node + linkType: hard + "react-native-image-picker@npm:^7.1.0": version: 7.1.0 resolution: "react-native-image-picker@npm:7.1.0" @@ -17095,6 +17707,16 @@ __metadata: languageName: node linkType: hard +"react-native-reanimated-table@npm:^0.0.2": + version: 0.0.2 + resolution: "react-native-reanimated-table@npm:0.0.2" + peerDependencies: + react: ">=16.8.0" + react-native: ">=0.6.0" + checksum: ab1a326d9880bc49febbb1ab325ede544c86b4ca2faf713904e8aa64d05478e36f6d545a819bc82a7d234c23ea6cf9e4fcbc2dc804a7e7f9240d5f21eb28e76c + languageName: node + linkType: hard + "react-native-reanimated@npm:^3.6.2": version: 3.6.2 resolution: "react-native-reanimated@npm:3.6.2" @@ -17642,6 +18264,15 @@ __metadata: languageName: node linkType: hard +"request-progress@npm:^3.0.0": + version: 3.0.0 + resolution: "request-progress@npm:3.0.0" + dependencies: + throttleit: ^1.0.0 + checksum: 6ea1761dcc8a8b7b5894afd478c0286aa31bd69438d7050294bd4fd0d0b3e09b5cde417d38deef9c49809039c337d8744e4bb49d8632b0c3e4ffa5e8a687e0fd + languageName: node + linkType: hard + "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -17688,6 +18319,13 @@ __metadata: languageName: node linkType: hard +"requires-port@npm:^1.0.0": + version: 1.0.0 + resolution: "requires-port@npm:1.0.0" + checksum: eee0e303adffb69be55d1a214e415cf42b7441ae858c76dfc5353148644f6fd6e698926fc4643f510d5c126d12a705e7c8ed7e38061113bdf37547ab356797ff + languageName: node + linkType: hard + "reselect@npm:^4.1.8": version: 4.1.8 resolution: "reselect@npm:4.1.8" @@ -18030,7 +18668,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.8.1": +"rxjs@npm:^7.5.1, rxjs@npm:^7.8.1": version: 7.8.1 resolution: "rxjs@npm:7.8.1" dependencies: @@ -18083,7 +18721,7 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.1.0": +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 @@ -18562,6 +19200,28 @@ __metadata: languageName: node linkType: hard +"slice-ansi@npm:^3.0.0": + version: 3.0.0 + resolution: "slice-ansi@npm:3.0.0" + dependencies: + ansi-styles: ^4.0.0 + astral-regex: ^2.0.0 + is-fullwidth-code-point: ^3.0.0 + checksum: 5ec6d022d12e016347e9e3e98a7eb2a592213a43a65f1b61b74d2c78288da0aded781f665807a9f3876b9daa9ad94f64f77d7633a0458876c3a4fdc4eb223f24 + languageName: node + linkType: hard + +"slice-ansi@npm:^4.0.0": + version: 4.0.0 + resolution: "slice-ansi@npm:4.0.0" + dependencies: + ansi-styles: ^4.0.0 + astral-regex: ^2.0.0 + is-fullwidth-code-point: ^3.0.0 + checksum: 4a82d7f085b0e1b070e004941ada3c40d3818563ac44766cca4ceadd2080427d337554f9f99a13aaeb3b4a94d9964d9466c807b3d7b7541d1ec37ee32d308756 + languageName: node + linkType: hard + "slice-ansi@npm:^5.0.0": version: 5.0.0 resolution: "slice-ansi@npm:5.0.0" @@ -18733,6 +19393,27 @@ __metadata: languageName: node linkType: hard +"sshpk@npm:^1.14.1": + version: 1.18.0 + resolution: "sshpk@npm:1.18.0" + dependencies: + asn1: ~0.2.3 + assert-plus: ^1.0.0 + bcrypt-pbkdf: ^1.0.0 + dashdash: ^1.12.0 + ecc-jsbn: ~0.1.1 + getpass: ^0.1.1 + jsbn: ~0.1.0 + safer-buffer: ^2.0.2 + tweetnacl: ~0.14.0 + bin: + sshpk-conv: bin/sshpk-conv + sshpk-sign: bin/sshpk-sign + sshpk-verify: bin/sshpk-verify + checksum: 01d43374eee3a7e37b3b82fdbecd5518cbb2e47ccbed27d2ae30f9753f22bd6ffad31225cb8ef013bc3fb7785e686cea619203ee1439a228f965558c367c3cfa + languageName: node + linkType: hard + "ssri@npm:^10.0.0": version: 10.0.5 resolution: "ssri@npm:10.0.5" @@ -19159,7 +19840,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.0.0": +"supports-color@npm:^8.0.0, supports-color@npm:^8.1.1": version: 8.1.1 resolution: "supports-color@npm:8.1.1" dependencies: @@ -19312,11 +19993,12 @@ __metadata: "@chain-registry/utils": 1.18.0 "@cosmjs/amino": 0.32.2 "@cosmjs/cosmwasm-stargate": 0.32.2 - "@cosmjs/encoding": ^0.32.2 - "@cosmjs/math": ^0.32.2 + "@cosmjs/crypto": 0.32.2 + "@cosmjs/encoding": 0.32.2 + "@cosmjs/math": 0.32.2 "@cosmjs/proto-signing": 0.32.2 "@cosmjs/stargate": 0.32.2 - "@cosmjs/tendermint-rpc": ^0.32.2 + "@cosmjs/tendermint-rpc": 0.32.2 "@cosmology/core": 2.0.1 "@cosmwasm/ts-codegen": ^0.35.7 "@dotlottie/react-player": ^1.6.5 @@ -19331,7 +20013,10 @@ __metadata: "@ethersproject/providers": ^5.7.2 "@expo-google-fonts/exo": ^0.2.2 "@expo/metro-runtime": ~3.1.3 + "@faker-js/faker": ^8.4.1 "@gnolang/gno-js-client": ^1.3.0 + "@gnolang/tm2-js-client": ^1.2.1 + "@hookform/resolvers": ^3.6.0 "@improbable-eng/grpc-web": ^0.15.0 "@improbable-eng/grpc-web-node-http-transport": ^0.15.0 "@improbable-eng/grpc-web-react-native-transport": ^0.15.0 @@ -19347,6 +20032,7 @@ __metadata: "@react-native-clipboard/clipboard": ^1.13.2 "@react-native-community/slider": 4.4.2 "@react-native-masked-view/masked-view": 0.3.0 + "@react-native-picker/picker": 2.6.1 "@react-navigation/bottom-tabs": ^6.5.11 "@react-navigation/drawer": ^6.6.6 "@react-navigation/native": ^6.0.11 @@ -19377,6 +20063,7 @@ __metadata: cosmos-endpoints-scorer: ^0.1.0 cross-env: ^7.0.3 crypto-browserify: ^3.12.0 + cypress: ^13.9.0 depcheck: ^1.4.7 dotenv: ^16.3.1 draft-convert: ^2.1.13 @@ -19438,6 +20125,7 @@ __metadata: react-native-gesture-handler: ~2.14.0 react-native-get-random-values: ~1.8.0 react-native-heroicons: ^3.2.0 + react-native-hoverable: ^0.2.0 react-native-image-picker: ^7.1.0 react-native-keyboard-aware-scrollview: ^2.1.0 react-native-leaflet-view: ^0.1.2 @@ -19449,6 +20137,7 @@ __metadata: react-native-qrcode-svg: ^6.2.0 react-native-reanimated: ^3.6.2 react-native-reanimated-carousel: 4.0.0-alpha.9 + react-native-reanimated-table: ^0.0.2 react-native-redash: ^18.0.0 react-native-safe-area-context: 4.8.2 react-native-screens: ~3.29.0 @@ -19478,6 +20167,7 @@ __metadata: waveform-data: ^4.3.0 yaml: ^2.3.4 zod: ^3.22.4 + zustand: ^4.4.7 languageName: unknown linkType: soft @@ -19562,6 +20252,13 @@ __metadata: languageName: node linkType: hard +"throttleit@npm:^1.0.0": + version: 1.0.1 + resolution: "throttleit@npm:1.0.1" + checksum: 32e0b12ca5810cd34dfce0408c7cb658dfd039848a073466eaac667ce6e846cafa53ac518e4b01dc6f34e6652b66fd29a5c6b666718ec5086ef328a9d029dc75 + languageName: node + linkType: hard + "through2@npm:^2.0.1": version: 2.0.5 resolution: "through2@npm:2.0.5" @@ -19572,7 +20269,7 @@ __metadata: languageName: node linkType: hard -"through@npm:2, through@npm:>=2.2.7 <3, through@npm:^2.3.6": +"through@npm:2, through@npm:>=2.2.7 <3, through@npm:^2.3.6, through@npm:^2.3.8": version: 2.3.8 resolution: "through@npm:2.3.8" checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd @@ -19607,6 +20304,13 @@ __metadata: languageName: node linkType: hard +"tmp@npm:~0.2.1": + version: 0.2.3 + resolution: "tmp@npm:0.2.3" + checksum: 73b5c96b6e52da7e104d9d44afb5d106bb1e16d9fa7d00dbeb9e6522e61b571fbdb165c756c62164be9a3bbe192b9b268c236d370a2a0955c7689cd2ae377b95 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -19637,6 +20341,18 @@ __metadata: languageName: node linkType: hard +"tough-cookie@npm:^4.1.3": + version: 4.1.4 + resolution: "tough-cookie@npm:4.1.4" + dependencies: + psl: ^1.1.33 + punycode: ^2.1.1 + universalify: ^0.2.0 + url-parse: ^1.5.3 + checksum: 5815059f014c31179a303c673f753f7899a6fce94ac93712c88ea5f3c26e0c042b5f0c7a599a00f8e0feeca4615dba75c3dffc54f3c1a489978aa8205e09307c + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -19766,6 +20482,22 @@ __metadata: languageName: node linkType: hard +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: ^5.0.1 + checksum: 05f6510358f8afc62a057b8b692f05d70c1782b70db86d6a1e0d5e28a32389e52fa6e7707b6c5ecccacc031462e4bc35af85ecfe4bbc341767917b7cf6965711 + languageName: node + linkType: hard + +"tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": + version: 0.14.5 + resolution: "tweetnacl@npm:0.14.5" + checksum: 6061daba1724f59473d99a7bb82e13f211cdf6e31315510ae9656fefd4779851cb927adad90f3b488c8ed77c106adc0421ea8055f6f976ff21b27c5c4e918487 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -20164,6 +20896,13 @@ __metadata: languageName: node linkType: hard +"universalify@npm:^0.2.0": + version: 0.2.0 + resolution: "universalify@npm:0.2.0" + checksum: e86134cb12919d177c2353196a4cc09981524ee87abf621f7bc8d249dbbbebaec5e7d1314b96061497981350df786e4c5128dbf442eba104d6e765bc260678b5 + languageName: node + linkType: hard + "universalify@npm:^1.0.0": version: 1.0.0 resolution: "universalify@npm:1.0.0" @@ -20192,6 +20931,13 @@ __metadata: languageName: node linkType: hard +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.13": version: 1.0.13 resolution: "update-browserslist-db@npm:1.0.13" @@ -20222,6 +20968,16 @@ __metadata: languageName: node linkType: hard +"url-parse@npm:^1.5.3": + version: 1.5.10 + resolution: "url-parse@npm:1.5.10" + dependencies: + querystringify: ^2.1.1 + requires-port: ^1.0.0 + checksum: fbdba6b1d83336aca2216bbdc38ba658d9cfb8fc7f665eb8b17852de638ff7d1a162c198a8e4ed66001ddbf6c9888d41e4798912c62b4fd777a31657989f7bdf + languageName: node + linkType: hard + "use-latest-callback@npm:^0.1.7": version: 0.1.9 resolution: "use-latest-callback@npm:0.1.9" @@ -20231,7 +20987,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.0.0, use-sync-external-store@npm:^1.2.0": +"use-sync-external-store@npm:1.2.0, use-sync-external-store@npm:^1.0.0, use-sync-external-store@npm:^1.2.0": version: 1.2.0 resolution: "use-sync-external-store@npm:1.2.0" peerDependencies: @@ -20364,6 +21120,17 @@ __metadata: languageName: node linkType: hard +"verror@npm:1.10.0": + version: 1.10.0 + resolution: "verror@npm:1.10.0" + dependencies: + assert-plus: ^1.0.0 + core-util-is: 1.0.2 + extsprintf: ^1.2.0 + checksum: c431df0bedf2088b227a4e051e0ff4ca54df2c114096b0c01e1cbaadb021c30a04d7dd5b41ab277bcd51246ca135bf931d4c4c796ecae7a4fef6d744ecef36ea + languageName: node + linkType: hard + "vfile-message@npm:^3.0.0": version: 3.1.4 resolution: "vfile-message@npm:3.1.4" @@ -21449,3 +22216,23 @@ __metadata: checksum: 80bfd7f8039b24fddeb0718a2ec7c02aa9856e4838d6aa4864335a047b6b37a3273b191ef335bf0b2002e5c514ef261ffcda5a589fb084a48c336ffc4cdbab7f languageName: node linkType: hard + +"zustand@npm:^4.4.7": + version: 4.5.0 + resolution: "zustand@npm:4.5.0" + dependencies: + use-sync-external-store: 1.2.0 + peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0.6" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + checksum: 91685492ab33bb656b98e07d8fff2be1794d8e68ac5dc546ec457f4ae3d709f0c19de9e93045b9ee5d6b704f64503d9e085ffe1f600f6ade0459e572d1cf5c0d + languageName: node + linkType: hard