From d541fe7294e7dd7693c29d7d4d11333680523a31 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 08:53:18 +0300 Subject: [PATCH 01/13] Init Royalty Registry contract helpers --- contracts/royaltyRegistry/contract.ts | 397 ++++++++++++++++++++++++++ 1 file changed, 397 insertions(+) create mode 100644 contracts/royaltyRegistry/contract.ts diff --git a/contracts/royaltyRegistry/contract.ts b/contracts/royaltyRegistry/contract.ts new file mode 100644 index 0000000..c4688a5 --- /dev/null +++ b/contracts/royaltyRegistry/contract.ts @@ -0,0 +1,397 @@ +import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate' +import type { Coin } from '@cosmjs/proto-signing' +import type { logs } from '@cosmjs/stargate' + +export interface InstantiateResponse { + readonly contractAddress: string + readonly transactionHash: string +} + +export interface MigrateResponse { + readonly transactionHash: string + readonly logs: readonly logs.Log[] +} + +export interface RoyaltyRegistryInstance { + readonly contractAddress: string + //Query + config: () => Promise + collectionRoyaltyDefault: (collection: string) => Promise + collectionRoyaltyProtocol: (collection: string, protocol: string) => Promise + // RoyaltyProtocolByCollection: (collection: string, queryOptions: QqueryOptions) => Promise + royaltyPayment: (collection: string, protocol?: string) => Promise + + //Execute + initializeCollectionRoyalty: (collection: string) => Promise + setCollectionRoyaltyDefault: (collection: string, recipient: string, share: number) => Promise + updateCollectionRoyaltyDefault: ( + collection: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ) => Promise + setCollectionRoyaltyProtocol: ( + collection: string, + protocol: string, + recipient: string, + share: number, + ) => Promise + updateCollectionRoyaltyProtocol: ( + collection: string, + protocol?: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ) => Promise +} + +export interface RoyaltyRegistryMessages { + initializeCollectionRoyalty: (collection: string) => InitializeCollectionRoyaltyMessage + setCollectionRoyaltyDefault: ( + collection: string, + recipient: string, + share: number, + ) => SetCollectionRoyaltyDefaultMessage + updateCollectionRoyaltyDefault: ( + collection: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ) => UpdateCollectionRoyaltyDefaultMessage + setCollectionRoyaltyProtocol: ( + collection: string, + protocol: string, + recipient: string, + share: number, + ) => SetCollectionRoyaltyProtocolMessage + updateCollectionRoyaltyProtocol: ( + collection: string, + protocol?: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ) => UpdateCollectionRoyaltyProtocolMessage +} + +export interface InitializeCollectionRoyaltyMessage { + sender: string + contract: string + msg: { + initialize_collection_royalty: { collection: string } + } + funds: Coin[] +} + +export interface SetCollectionRoyaltyDefaultMessage { + sender: string + contract: string + msg: { + set_collection_royalty_default: { collection: string; recipient: string; share: number } + } + funds: Coin[] +} + +export interface UpdateCollectionRoyaltyDefaultMessage { + sender: string + contract: string + msg: { + update_collection_royalty_default: { + collection: string + recipient?: string + share_delta?: number + decrement?: boolean + } + } + funds: Coin[] +} + +export interface SetCollectionRoyaltyProtocolMessage { + sender: string + contract: string + msg: { + set_collection_royalty_protocol: { + collection: string + protocol: string + recipient: string + share: number + } + } + funds: Coin[] +} + +export interface UpdateCollectionRoyaltyProtocolMessage { + sender: string + contract: string + msg: { + update_collection_royalty_protocol: { + collection: string + protocol?: string + recipient?: string + share_delta?: number + decrement?: boolean + } + } + funds: Coin[] +} + +export interface RoyaltyRegistryContract { + instantiate: ( + codeId: number, + initMsg: Record, + label: string, + admin?: string, + ) => Promise + + use: (contractAddress: string) => RoyaltyRegistryInstance + + migrate: ( + senderAddress: string, + contractAddress: string, + codeId: number, + migrateMsg: Record, + ) => Promise + + messages: (contractAddress: string) => RoyaltyRegistryMessages +} + +export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string): RoyaltyRegistryContract => { + const use = (contractAddress: string): RoyaltyRegistryInstance => { + ///QUERY + const config = async (): Promise => { + return client.queryContractSmart(contractAddress, { + config: {}, + }) + } + + const collectionRoyaltyDefault = async (collection: string): Promise => { + return client.queryContractSmart(contractAddress, { + collection_royalty_default: { collection }, + }) + } + + const collectionRoyaltyProtocol = async (collection: string, protocol: string): Promise => { + return client.queryContractSmart(contractAddress, { + collection_royalty_protocol: { collection, protocol }, + }) + } + + const royaltyPayment = async (collection: string, protocol?: string): Promise => { + return client.queryContractSmart(contractAddress, { + royalty_payment: { collection, protocol }, + }) + } + + /// EXECUTE + const initializeCollectionRoyalty = async (collection: string): Promise => { + const res = await client.execute( + txSigner, + contractAddress, + { + initialize_collection_royalty: { collection }, + }, + 'auto', + ) + return res.transactionHash + } + + const setCollectionRoyaltyDefault = async ( + collection: string, + recipient: string, + share: number, + ): Promise => { + const res = await client.execute( + txSigner, + contractAddress, + { + set_collection_royalty_default: { collection, recipient, share }, + }, + 'auto', + ) + return res.transactionHash + } + + const updateCollectionRoyaltyDefault = async ( + collection: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ): Promise => { + const res = await client.execute( + txSigner, + contractAddress, + { + update_collection_royalty_default: { collection, recipient, share_delta: shareDelta, decrement }, + }, + 'auto', + ) + return res.transactionHash + } + + const setCollectionRoyaltyProtocol = async ( + collection: string, + protocol: string, + recipient: string, + share: number, + ): Promise => { + const res = await client.execute( + txSigner, + contractAddress, + { + set_collection_royalty_protocol: { collection, protocol, recipient, share }, + }, + 'auto', + ) + return res.transactionHash + } + + const updateCollectionRoyaltyProtocol = async ( + collection: string, + protocol?: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ): Promise => { + const res = await client.execute( + txSigner, + contractAddress, + { + update_collection_royalty_protocol: { + collection, + protocol, + recipient, + share_delta: shareDelta, + decrement, + }, + }, + 'auto', + ) + return res.transactionHash + } + + return { + contractAddress, + config, + collectionRoyaltyDefault, + collectionRoyaltyProtocol, + royaltyPayment, + initializeCollectionRoyalty, + setCollectionRoyaltyDefault, + updateCollectionRoyaltyDefault, + setCollectionRoyaltyProtocol, + updateCollectionRoyaltyProtocol, + } + } + + const instantiate = async ( + codeId: number, + initMsg: Record, + label: string, + admin?: string, + ): Promise => { + const result = await client.instantiate(txSigner, codeId, initMsg, label, 'auto', { + admin, + }) + + return { + contractAddress: result.contractAddress, + transactionHash: result.transactionHash, + } + } + + const migrate = async ( + senderAddress: string, + contractAddress: string, + codeId: number, + migrateMsg: Record, + ): Promise => { + const result = await client.migrate(senderAddress, contractAddress, codeId, migrateMsg, 'auto') + return { + transactionHash: result.transactionHash, + logs: result.logs, + } + } + + const messages = (contractAddress: string) => { + const initializeCollectionRoyalty = (collection: string) => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + initialize_collection_royalty: { collection }, + }, + funds: [], + } + } + + const setCollectionRoyaltyDefault = (collection: string, recipient: string, share: number) => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + set_collection_royalty_default: { collection, recipient, share }, + }, + funds: [], + } + } + + const updateCollectionRoyaltyDefault = ( + collection: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ) => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + update_collection_royalty_default: { collection, recipient, share_delta: shareDelta, decrement }, + }, + funds: [], + } + } + + const setCollectionRoyaltyProtocol = (collection: string, protocol: string, recipient: string, share: number) => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + set_collection_royalty_protocol: { collection, protocol, recipient, share }, + }, + funds: [], + } + } + + const updateCollectionRoyaltyProtocol = ( + collection: string, + protocol?: string, + recipient?: string, + shareDelta?: number, + decrement?: boolean, + ) => { + return { + sender: txSigner, + contract: contractAddress, + msg: { + update_collection_royalty_protocol: { + collection, + protocol, + recipient, + share_delta: shareDelta, + decrement, + }, + }, + funds: [], + } + } + + return { + initializeCollectionRoyalty, + setCollectionRoyaltyDefault, + updateCollectionRoyaltyDefault, + setCollectionRoyaltyProtocol, + updateCollectionRoyaltyProtocol, + } + } + + return { use, instantiate, migrate, messages } +} From 2710be69593333812cf50d42d5c8b8f7b8b3e5e2 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 08:59:28 +0300 Subject: [PATCH 02/13] Update env variables --- .env.example | 1 + env.d.ts | 1 + utils/constants.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/.env.example b/.env.example index 54d70c4..794cc92 100644 --- a/.env.example +++ b/.env.example @@ -39,6 +39,7 @@ NEXT_PUBLIC_OPEN_EDITION_IBC_FRNZ_FACTORY_ADDRESS="stars1vzffawsjhvspstu5lvtzz2x NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS="stars1tc09vlgdg8rqyapcxwm9qdq8naj4gym9px4ntue9cs0kse5rvess0nee3a" NEXT_PUBLIC_SG721_NAME_ADDRESS="stars1fx74nkqkw2748av8j7ew7r3xt9cgjqduwn8m0ur5lhe49uhlsasszc5fhr" +NEXT_PUBLIC_ROYALTY_REGISTRY_ADDRESS="stars1crgx0f70fzksa57hq87wtl8f04h0qyk5la0hk0fu8dyhl67ju80qaxzr5z" NEXT_PUBLIC_WHITELIST_CODE_ID=2602 NEXT_PUBLIC_WHITELIST_FLEX_CODE_ID=2603 NEXT_PUBLIC_BADGE_HUB_CODE_ID=1336 diff --git a/env.d.ts b/env.d.ts index 3d4984a..ac2bfb4 100644 --- a/env.d.ts +++ b/env.d.ts @@ -46,6 +46,7 @@ declare namespace NodeJS { readonly NEXT_PUBLIC_BASE_FACTORY_ADDRESS: string readonly NEXT_PUBLIC_BASE_FACTORY_UPDATABLE_ADDRESS: string readonly NEXT_PUBLIC_SG721_NAME_ADDRESS: string + readonly NEXT_PUBLIC_ROYALTY_REGISTRY_ADDRESS: string readonly NEXT_PUBLIC_BASE_MINTER_CODE_ID: string readonly NEXT_PUBLIC_BADGE_HUB_CODE_ID: string readonly NEXT_PUBLIC_BADGE_HUB_ADDRESS: string diff --git a/utils/constants.ts b/utils/constants.ts index 4911df1..a115208 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -40,6 +40,7 @@ export const OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS = process.env.NEXT_PUBLIC_OPEN_EDITION_UPDATABLE_IBC_FRNZ_FACTORY_ADDRESS export const OPEN_EDITION_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_OPEN_EDITION_MINTER_CODE_ID, 10) export const SG721_NAME_ADDRESS = process.env.NEXT_PUBLIC_SG721_NAME_ADDRESS +export const ROYALTY_REGISTRY_ADDRESS = process.env.NEXT_PUBLIC_ROYALTY_REGISTRY_ADDRESS export const BASE_MINTER_CODE_ID = parseInt(process.env.NEXT_PUBLIC_VENDING_MINTER_CODE_ID, 10) export const BADGE_HUB_CODE_ID = parseInt(process.env.NEXT_PUBLIC_BADGE_HUB_CODE_ID, 10) export const BADGE_HUB_ADDRESS = process.env.NEXT_PUBLIC_BADGE_HUB_ADDRESS From 9fcd5b82f43c91fa85cf889e9e2fe75696703fab Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 09:05:54 +0300 Subject: [PATCH 03/13] Init Royalty Registry contract hooks --- contracts/royaltyRegistry/index.ts | 2 + contracts/royaltyRegistry/useContract.ts | 99 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 contracts/royaltyRegistry/index.ts create mode 100644 contracts/royaltyRegistry/useContract.ts diff --git a/contracts/royaltyRegistry/index.ts b/contracts/royaltyRegistry/index.ts new file mode 100644 index 0000000..6dc6461 --- /dev/null +++ b/contracts/royaltyRegistry/index.ts @@ -0,0 +1,2 @@ +export * from './contract' +export * from './useContract' diff --git a/contracts/royaltyRegistry/useContract.ts b/contracts/royaltyRegistry/useContract.ts new file mode 100644 index 0000000..7d73394 --- /dev/null +++ b/contracts/royaltyRegistry/useContract.ts @@ -0,0 +1,99 @@ +/* eslint-disable eslint-comments/disable-enable-pair */ + +import { useWallet } from 'contexts/wallet' +import { useCallback, useEffect, useState } from 'react' + +import type { + InstantiateResponse, + MigrateResponse, + RoyaltyRegistryContract, + RoyaltyRegistryInstance, + RoyaltyRegistryMessages, +} from './contract' +import { RoyaltyRegistry as initContract } from './contract' + +export interface UseRoyaltyRegistryContractProps { + instantiate: ( + codeId: number, + initMsg: Record, + label: string, + admin?: string, + ) => Promise + + migrate: (contractAddress: string, codeId: number, migrateMsg: Record) => Promise + + use: (customAddress?: string) => RoyaltyRegistryInstance | undefined + + updateContractAddress: (contractAddress: string) => void + + messages: (contractAddress: string) => RoyaltyRegistryMessages | undefined +} + +export function useRoyaltyRegistryContract(): UseRoyaltyRegistryContractProps { + const wallet = useWallet() + + const [address, setAddress] = useState('') + const [royaltyRegistry, setRoyaltyRegistry] = useState() + + useEffect(() => { + setAddress(localStorage.getItem('contract_address') || '') + }, []) + + useEffect(() => { + const royaltyRegistryContract = initContract(wallet.getClient(), wallet.address) + setRoyaltyRegistry(royaltyRegistryContract) + }, [wallet]) + + const updateContractAddress = (contractAddress: string) => { + setAddress(contractAddress) + } + + const instantiate = useCallback( + (codeId: number, initMsg: Record, label: string, admin?: string): Promise => { + return new Promise((resolve, reject) => { + if (!royaltyRegistry) { + reject(new Error('Contract is not initialized.')) + return + } + royaltyRegistry.instantiate(codeId, initMsg, label, admin).then(resolve).catch(reject) + }) + }, + [royaltyRegistry], + ) + + const migrate = useCallback( + (contractAddress: string, codeId: number, migrateMsg: Record): Promise => { + return new Promise((resolve, reject) => { + if (!royaltyRegistry) { + reject(new Error('Contract is not initialized.')) + return + } + console.log(wallet.address, contractAddress, codeId) + royaltyRegistry.migrate(wallet.address, contractAddress, codeId, migrateMsg).then(resolve).catch(reject) + }) + }, + [royaltyRegistry, wallet], + ) + + const use = useCallback( + (customAddress = ''): RoyaltyRegistryInstance | undefined => { + return royaltyRegistry?.use(address || customAddress) + }, + [royaltyRegistry, address], + ) + + const messages = useCallback( + (customAddress = ''): RoyaltyRegistryMessages | undefined => { + return royaltyRegistry?.messages(address || customAddress) + }, + [royaltyRegistry, address], + ) + + return { + instantiate, + migrate, + use, + updateContractAddress, + messages, + } +} From ca8f5cca586d0be866a67b15a7a5449ca6efd599 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 10:41:10 +0300 Subject: [PATCH 04/13] Match list items with contract helpers --- contracts/royaltyRegistry/messages/execute.ts | 162 ++++++++++++++++++ contracts/royaltyRegistry/messages/query.ts | 64 +++++++ 2 files changed, 226 insertions(+) create mode 100644 contracts/royaltyRegistry/messages/execute.ts create mode 100644 contracts/royaltyRegistry/messages/query.ts diff --git a/contracts/royaltyRegistry/messages/execute.ts b/contracts/royaltyRegistry/messages/execute.ts new file mode 100644 index 0000000..a4c018d --- /dev/null +++ b/contracts/royaltyRegistry/messages/execute.ts @@ -0,0 +1,162 @@ +import type { RoyaltyRegistryInstance } from '../index' +import { useRoyaltyRegistryContract } from '../index' + +export type ExecuteType = typeof EXECUTE_TYPES[number] + +export const EXECUTE_TYPES = [ + 'initialize_collection_royalty', + 'set_collection_royalty_default', + 'update_collection_royalty_default', + 'set_collection_royalty_protocol', + 'update_collection_royalty_protocol', +] as const + +export interface ExecuteListItem { + id: ExecuteType + name: string + description?: string +} + +export const EXECUTE_LIST: ExecuteListItem[] = [ + { + id: 'initialize_collection_royalty', + name: 'Initialize Collection Royalty', + description: 'Initialize collection royalty', + }, + { + id: 'set_collection_royalty_default', + name: 'Set Collection Royalty Default', + description: 'Set collection royalty default', + }, + { + id: 'update_collection_royalty_default', + name: 'Update Collection Royalty Default', + description: 'Update collection royalty default', + }, + { + id: 'set_collection_royalty_protocol', + name: 'Set Collection Royalty Protocol', + description: 'Set collection royalty protocol', + }, + { + id: 'update_collection_royalty_protocol', + name: 'Update Collection Royalty Protocol', + description: 'Update collection royalty protocol', + }, +] + +export interface DispatchExecuteProps { + type: ExecuteType + [k: string]: unknown +} + +type Select = T +/** @see {@link RoyaltyRegistryInstance} */ +export type DispatchExecuteArgs = { + contract: string + messages?: RoyaltyRegistryInstance +} & ( + | { type: Select<'initialize_collection_royalty'>; collection: string } + | { type: Select<'set_collection_royalty_default'>; collection: string; recipient: string; royalty: number } + | { + type: Select<'update_collection_royalty_default'> + collection: string + recipient: string + shareDelta: number + decrement: boolean + } + | { + type: Select<'set_collection_royalty_protocol'> + collection: string + protocol: string + recipient: string + royalty: number + } + | { + type: Select<'update_collection_royalty_protocol'> + collection: string + protocol: string + recipient: string + shareDelta: number + decrement: boolean + } +) + +export const dispatchExecute = async (args: DispatchExecuteArgs) => { + const { messages } = args + if (!messages) { + throw new Error('Cannot dispatch execute, messages are not defined') + } + switch (args.type) { + case 'initialize_collection_royalty': { + return messages.initializeCollectionRoyalty(args.collection) + } + case 'set_collection_royalty_default': { + return messages.setCollectionRoyaltyDefault(args.collection, args.recipient, args.royalty) + } + case 'update_collection_royalty_default': { + return messages.updateCollectionRoyaltyDefault(args.collection, args.recipient, args.shareDelta, args.decrement) + } + case 'set_collection_royalty_protocol': { + return messages.setCollectionRoyaltyProtocol(args.collection, args.protocol, args.recipient, args.royalty) + } + case 'update_collection_royalty_protocol': { + return messages.updateCollectionRoyaltyProtocol( + args.collection, + args.protocol, + args.recipient, + args.shareDelta, + args.decrement, + ) + } + default: { + throw new Error('Unknown execution type') + } + } +} + +export const previewExecutePayload = (args: DispatchExecuteArgs) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const { messages } = useRoyaltyRegistryContract() + const { contract } = args + switch (args.type) { + case 'initialize_collection_royalty': { + return messages(contract)?.initializeCollectionRoyalty(args.collection) + } + case 'set_collection_royalty_default': { + return messages(contract)?.setCollectionRoyaltyDefault(args.collection, args.recipient, args.royalty) + } + case 'update_collection_royalty_default': { + return messages(contract)?.updateCollectionRoyaltyDefault( + args.collection, + args.recipient, + args.shareDelta, + args.decrement, + ) + } + case 'set_collection_royalty_protocol': { + return messages(contract)?.setCollectionRoyaltyProtocol( + args.collection, + args.protocol, + args.recipient, + args.royalty, + ) + } + case 'update_collection_royalty_protocol': { + return messages(contract)?.updateCollectionRoyaltyProtocol( + args.collection, + args.protocol, + args.recipient, + args.shareDelta, + args.decrement, + ) + } + default: { + return {} + } + } +} + +export const isEitherType = (type: unknown, arr: T[]): type is T => { + return arr.some((val) => type === val) +} diff --git a/contracts/royaltyRegistry/messages/query.ts b/contracts/royaltyRegistry/messages/query.ts new file mode 100644 index 0000000..07d6909 --- /dev/null +++ b/contracts/royaltyRegistry/messages/query.ts @@ -0,0 +1,64 @@ +import type { RoyaltyRegistryInstance } from '../contract' + +export type QueryType = typeof QUERY_TYPES[number] + +export const QUERY_TYPES = [ + 'config', + 'collection_royalty_default', + 'collection_royalty_protocol', + 'royalty_payment', +] as const +export interface QueryListItem { + id: QueryType + name: string + description?: string +} + +export const QUERY_LIST: QueryListItem[] = [ + { id: 'config', name: 'Query Config', description: 'View the contract config' }, + { + id: 'collection_royalty_default', + name: 'Query Collection Royalty Details', + description: 'View the collection royalty details', + }, + { + id: 'collection_royalty_protocol', + name: 'Query Collection Royalty Protocol', + description: 'View the collection royalty protocol', + }, + { + id: 'royalty_payment', + name: 'Query Royalty Payment', + description: 'View the royalty payment', + }, +] +/* + //Query + config: () => Promise + collectionRoyaltyDefault: (collection: string) => Promise + collectionRoyaltyProtocol: (collection: string, protocol: string) => Promise + // RoyaltyProtocolByCollection: (collection: string, queryOptions: QqueryOptions) => Promise + royaltyPayment: (collection: string, protocol?: string) => Promise + */ + +export interface DispatchQueryProps { + messages: RoyaltyRegistryInstance | undefined + type: QueryType + collection: string + protocol: string +} + +export const dispatchQuery = (props: DispatchQueryProps) => { + const { messages, type, collection, protocol } = props + switch (type) { + case 'config': + return messages?.config() + case 'collection_royalty_default': + return messages?.collectionRoyaltyDefault(collection) + case 'collection_royalty_protocol': + return messages?.collectionRoyaltyProtocol(collection, protocol) + default: { + throw new Error('unknown query type') + } + } +} From b697b1a8572c74c29c852dff51e951e4187bcdaf Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 10:46:37 +0300 Subject: [PATCH 05/13] Update contracts context --- contexts/contracts.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contexts/contracts.tsx b/contexts/contracts.tsx index b498185..13790ff 100644 --- a/contexts/contracts.tsx +++ b/contexts/contracts.tsx @@ -6,6 +6,8 @@ import type { UseBaseMinterContractProps } from 'contracts/baseMinter' import { useBaseMinterContract } from 'contracts/baseMinter' import { type UseOpenEditionFactoryContractProps, useOpenEditionFactoryContract } from 'contracts/openEditionFactory' import { type UseOpenEditionMinterContractProps, useOpenEditionMinterContract } from 'contracts/openEditionMinter' +import type { UseRoyaltyRegistryContractProps } from 'contracts/royaltyRegistry' +import { useRoyaltyRegistryContract } from 'contracts/royaltyRegistry' import type { UseSG721ContractProps } from 'contracts/sg721' import { useSG721Contract } from 'contracts/sg721' import type { UseVendingFactoryContractProps } from 'contracts/vendingFactory' @@ -36,6 +38,7 @@ export interface ContractsStore extends State { openEditionFactory: UseOpenEditionFactoryContractProps | null badgeHub: UseBadgeHubContractProps | null splits: UseSplitsContractProps | null + royaltyRegistry: UseRoyaltyRegistryContractProps | null } /** @@ -52,6 +55,7 @@ export const defaultValues: ContractsStore = { openEditionFactory: null, badgeHub: null, splits: null, + royaltyRegistry: null, } /** @@ -85,6 +89,7 @@ const ContractsSubscription: VFC = () => { const openEditionFactory = useOpenEditionFactoryContract() const badgeHub = useBadgeHubContract() const splits = useSplitsContract() + const royaltyRegistry = useRoyaltyRegistryContract() useEffect(() => { useContracts.setState({ @@ -98,8 +103,9 @@ const ContractsSubscription: VFC = () => { openEditionFactory, badgeHub, splits, + royaltyRegistry, }) - }, [sg721, vendingMinter, baseMinter, whitelist, vendingFactory, baseFactory, badgeHub, splits]) + }, [sg721, vendingMinter, baseMinter, whitelist, vendingFactory, baseFactory, badgeHub, splits, royaltyRegistry]) return null } From 2b893e6e604155034618bdab142d092b2b3ee67f Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 11:01:56 +0300 Subject: [PATCH 06/13] Add combobox for royalty registry / execute --- .../royaltyRegistry/ExecuteCombobox.hooks.ts | 7 ++ .../royaltyRegistry/ExecuteCombobox.tsx | 92 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 components/contracts/royaltyRegistry/ExecuteCombobox.hooks.ts create mode 100644 components/contracts/royaltyRegistry/ExecuteCombobox.tsx diff --git a/components/contracts/royaltyRegistry/ExecuteCombobox.hooks.ts b/components/contracts/royaltyRegistry/ExecuteCombobox.hooks.ts new file mode 100644 index 0000000..2bd614a --- /dev/null +++ b/components/contracts/royaltyRegistry/ExecuteCombobox.hooks.ts @@ -0,0 +1,7 @@ +import type { ExecuteListItem } from 'contracts/royaltyRegistry/messages/execute' +import { useState } from 'react' + +export const useExecuteComboboxState = () => { + const [value, setValue] = useState(null) + return { value, onChange: (item: ExecuteListItem) => setValue(item) } +} diff --git a/components/contracts/royaltyRegistry/ExecuteCombobox.tsx b/components/contracts/royaltyRegistry/ExecuteCombobox.tsx new file mode 100644 index 0000000..e28a2d6 --- /dev/null +++ b/components/contracts/royaltyRegistry/ExecuteCombobox.tsx @@ -0,0 +1,92 @@ +import { Combobox, Transition } from '@headlessui/react' +import clsx from 'clsx' +import { FormControl } from 'components/FormControl' +import type { ExecuteListItem } from 'contracts/royaltyRegistry/messages/execute' +import { EXECUTE_LIST } from 'contracts/royaltyRegistry/messages/execute' +import { matchSorter } from 'match-sorter' +import { Fragment, useState } from 'react' +import { FaChevronDown, FaInfoCircle } from 'react-icons/fa' + +export interface ExecuteComboboxProps { + value: ExecuteListItem | null + onChange: (item: ExecuteListItem) => void +} + +export const ExecuteCombobox = ({ value, onChange }: ExecuteComboboxProps) => { + const [search, setSearch] = useState('') + + const filtered = + search === '' ? EXECUTE_LIST : matchSorter(EXECUTE_LIST, search, { keys: ['id', 'name', 'description'] }) + + return ( + +
+ val?.name ?? ''} + id="message-type" + onChange={(event) => setSearch(event.target.value)} + placeholder="Select message type" + /> + + + {({ open }) => + + setSearch('')} as={Fragment}> + + {filtered.length < 1 && ( + + Message type not found. + + )} + {filtered.map((entry) => ( + + clsx('flex relative flex-col py-2 px-4 space-y-1 cursor-pointer', { 'bg-stargaze-80': active }) + } + value={entry} + > + {entry.name} + {entry.description} + + ))} + + +
+ + {value && ( +
+
+ +
+ {value.description} +
+ )} +
+ ) +} From 584a33c388e2992e6d0951592307baa2d6a7f05c Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 11:04:08 +0300 Subject: [PATCH 07/13] Add link tabs for royalty registry --- components/LinkTabs.data.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/components/LinkTabs.data.ts b/components/LinkTabs.data.ts index 381cb93..cfc268a 100644 --- a/components/LinkTabs.data.ts +++ b/components/LinkTabs.data.ts @@ -145,3 +145,16 @@ export const splitsLinkTabs: LinkTabProps[] = [ href: '/contracts/splits/migrate', }, ] + +export const royaltyRegistryLinkTabs: LinkTabProps[] = [ + { + title: 'Query', + description: `Dispatch queries for your Royalty Registry contract`, + href: '/contracts/royaltyRegistry/query', + }, + { + title: 'Execute', + description: `Execute Royalty Registry contract actions`, + href: '/contracts/royaltyRegistry/execute', + }, +] From 0595c4de253f643b7a9a1370dbff73b31dfbbdfa Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 11:53:10 +0300 Subject: [PATCH 08/13] Init Royalty Registry dashboard > Execute --- contracts/royaltyRegistry/messages/execute.ts | 44 ++--- pages/contracts/royaltyRegistry/execute.tsx | 177 ++++++++++++++++++ pages/contracts/royaltyRegistry/index.tsx | 1 + 3 files changed, 191 insertions(+), 31 deletions(-) create mode 100644 pages/contracts/royaltyRegistry/execute.tsx create mode 100644 pages/contracts/royaltyRegistry/index.tsx diff --git a/contracts/royaltyRegistry/messages/execute.ts b/contracts/royaltyRegistry/messages/execute.ts index a4c018d..4d37d3b 100644 --- a/contracts/royaltyRegistry/messages/execute.ts +++ b/contracts/royaltyRegistry/messages/execute.ts @@ -52,35 +52,17 @@ export interface DispatchExecuteProps { type Select = T /** @see {@link RoyaltyRegistryInstance} */ -export type DispatchExecuteArgs = { +export interface DispatchExecuteArgs { contract: string + collection: string + protocol: string + recipient: string + share: number + shareDelta: number + decrement: boolean messages?: RoyaltyRegistryInstance -} & ( - | { type: Select<'initialize_collection_royalty'>; collection: string } - | { type: Select<'set_collection_royalty_default'>; collection: string; recipient: string; royalty: number } - | { - type: Select<'update_collection_royalty_default'> - collection: string - recipient: string - shareDelta: number - decrement: boolean - } - | { - type: Select<'set_collection_royalty_protocol'> - collection: string - protocol: string - recipient: string - royalty: number - } - | { - type: Select<'update_collection_royalty_protocol'> - collection: string - protocol: string - recipient: string - shareDelta: number - decrement: boolean - } -) + type: string | undefined +} export const dispatchExecute = async (args: DispatchExecuteArgs) => { const { messages } = args @@ -92,13 +74,13 @@ export const dispatchExecute = async (args: DispatchExecuteArgs) => { return messages.initializeCollectionRoyalty(args.collection) } case 'set_collection_royalty_default': { - return messages.setCollectionRoyaltyDefault(args.collection, args.recipient, args.royalty) + return messages.setCollectionRoyaltyDefault(args.collection, args.recipient, args.share) } case 'update_collection_royalty_default': { return messages.updateCollectionRoyaltyDefault(args.collection, args.recipient, args.shareDelta, args.decrement) } case 'set_collection_royalty_protocol': { - return messages.setCollectionRoyaltyProtocol(args.collection, args.protocol, args.recipient, args.royalty) + return messages.setCollectionRoyaltyProtocol(args.collection, args.protocol, args.recipient, args.share) } case 'update_collection_royalty_protocol': { return messages.updateCollectionRoyaltyProtocol( @@ -124,7 +106,7 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { return messages(contract)?.initializeCollectionRoyalty(args.collection) } case 'set_collection_royalty_default': { - return messages(contract)?.setCollectionRoyaltyDefault(args.collection, args.recipient, args.royalty) + return messages(contract)?.setCollectionRoyaltyDefault(args.collection, args.recipient, args.share) } case 'update_collection_royalty_default': { return messages(contract)?.updateCollectionRoyaltyDefault( @@ -139,7 +121,7 @@ export const previewExecutePayload = (args: DispatchExecuteArgs) => { args.collection, args.protocol, args.recipient, - args.royalty, + args.share, ) } case 'update_collection_royalty_protocol': { diff --git a/pages/contracts/royaltyRegistry/execute.tsx b/pages/contracts/royaltyRegistry/execute.tsx new file mode 100644 index 0000000..0bd241c --- /dev/null +++ b/pages/contracts/royaltyRegistry/execute.tsx @@ -0,0 +1,177 @@ +import { Button } from 'components/Button' +import { Conditional } from 'components/Conditional' +import { ContractPageHeader } from 'components/ContractPageHeader' +import { ExecuteCombobox } from 'components/contracts/royaltyRegistry/ExecuteCombobox' +import { useExecuteComboboxState } from 'components/contracts/royaltyRegistry/ExecuteCombobox.hooks' +import { FormControl } from 'components/FormControl' +import { AddressInput } from 'components/forms/FormInput' +import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' +import { JsonPreview } from 'components/JsonPreview' +import { LinkTabs } from 'components/LinkTabs' +import { royaltyRegistryLinkTabs } from 'components/LinkTabs.data' +import { TransactionHash } from 'components/TransactionHash' +import { useContracts } from 'contexts/contracts' +import { useWallet } from 'contexts/wallet' +import type { DispatchExecuteArgs } from 'contracts/royaltyRegistry/messages/execute' +import { dispatchExecute, isEitherType, previewExecutePayload } from 'contracts/royaltyRegistry/messages/execute' +import type { NextPage } from 'next' +import { useRouter } from 'next/router' +import { NextSeo } from 'next-seo' +import type { FormEvent } from 'react' +import { useEffect, useMemo, useState } from 'react' +import { toast } from 'react-hot-toast' +import { FaArrowRight } from 'react-icons/fa' +import { useMutation } from 'react-query' +import { ROYALTY_REGISTRY_ADDRESS } from 'utils/constants' +import { withMetadata } from 'utils/layout' +import { links } from 'utils/links' + +const RoyaltyRegistryExecutePage: NextPage = () => { + const { royaltyRegistry: contract } = useContracts() + const wallet = useWallet() + + const [lastTx, setLastTx] = useState('') + + const comboboxState = useExecuteComboboxState() + const type = comboboxState.value?.id + + const contractState = useInputState({ + id: 'contract-address', + name: 'contract-address', + title: 'Royalty Registry Address', + subtitle: 'Address of the Royalty Registry contract', + defaultValue: ROYALTY_REGISTRY_ADDRESS, + }) + const contractAddress = contractState.value + + const collectionAddressState = useInputState({ + id: 'collection-address', + name: 'collection-address', + title: 'Collection Address', + subtitle: 'Address of the collection', + }) + + const protocolAddressState = useInputState({ + id: 'protocol-address', + name: 'protocol-address', + title: 'Protocol Address', + subtitle: 'Address of the protocol', + }) + + const recipientAddressState = useInputState({ + id: 'recipient-address', + name: 'recipient-address', + title: 'Recipient Address', + subtitle: 'Address of the recipient', + }) + + const shareState = useNumberInputState({ + id: 'share', + name: 'share', + title: 'Share', + subtitle: 'Share', + }) + + const shareDeltaState = useNumberInputState({ + id: 'share-delta', + name: 'share-delta', + title: 'Share Delta', + subtitle: 'Share delta', + }) + + const [decrement, setDecrement] = useState(false) + + const showRecipientAddress = isEitherType(type, [ + 'set_collection_royalty_default', + 'set_collection_royalty_protocol', + 'update_collection_royalty_default', + 'update_collection_royalty_protocol', + ]) + + const messages = useMemo(() => contract?.use(contractState.value), [contract, contractState.value]) + const payload: DispatchExecuteArgs = { + contract: contractState.value, + messages, + type, + collection: collectionAddressState.value, + protocol: protocolAddressState.value, + recipient: recipientAddressState.value, + share: shareState.value, + shareDelta: shareDeltaState.value, + decrement, + } + const { isLoading, mutate } = useMutation( + async (event: FormEvent) => { + event.preventDefault() + if (!type) { + throw new Error('Please select message type!') + } + if (!wallet.initialized) { + throw new Error('Please connect your wallet.') + } + const txHash = await toast.promise(dispatchExecute(payload), { + error: `${type.charAt(0).toUpperCase() + type.slice(1)} execute failed!`, + loading: 'Executing message...', + success: (tx) => `Transaction ${tx} success!`, + }) + if (txHash) { + setLastTx(txHash) + } + }, + { + onError: (error) => { + toast.error(String(error), { style: { maxWidth: 'none' } }) + }, + }, + ) + + const router = useRouter() + + useEffect(() => { + if (contractAddress.length > 0) { + void router.replace({ query: { contractAddress } }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [contractAddress]) + useEffect(() => { + const initial = new URL(document.URL).searchParams.get('contractAddress') + if (initial && initial.length > 0) contractState.onChange(initial) + }, []) + + return ( +
+ + + + +
+
+ + + + + +
+
+
+ + + + +
+ + + +
+
+
+ ) +} + +export default withMetadata(RoyaltyRegistryExecutePage, { center: false }) diff --git a/pages/contracts/royaltyRegistry/index.tsx b/pages/contracts/royaltyRegistry/index.tsx new file mode 100644 index 0000000..6d40435 --- /dev/null +++ b/pages/contracts/royaltyRegistry/index.tsx @@ -0,0 +1 @@ +export { default } from './execute' From f092d7d92638b68f3802993904f0e0e34d0e63d8 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 11:53:28 +0300 Subject: [PATCH 09/13] Init Royalty Registry dashboard > Query --- pages/contracts/royaltyRegistry/query.tsx | 140 ++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 pages/contracts/royaltyRegistry/query.tsx diff --git a/pages/contracts/royaltyRegistry/query.tsx b/pages/contracts/royaltyRegistry/query.tsx new file mode 100644 index 0000000..b292006 --- /dev/null +++ b/pages/contracts/royaltyRegistry/query.tsx @@ -0,0 +1,140 @@ +import clsx from 'clsx' +import { Conditional } from 'components/Conditional' +import { ContractPageHeader } from 'components/ContractPageHeader' +import { FormControl } from 'components/FormControl' +import { AddressInput } from 'components/forms/FormInput' +import { useInputState } from 'components/forms/FormInput.hooks' +import { JsonPreview } from 'components/JsonPreview' +import { LinkTabs } from 'components/LinkTabs' +import { royaltyRegistryLinkTabs } from 'components/LinkTabs.data' +import { useContracts } from 'contexts/contracts' +import { useWallet } from 'contexts/wallet' +import type { QueryType } from 'contracts/royaltyRegistry/messages/query' +import { dispatchQuery, QUERY_LIST } from 'contracts/royaltyRegistry/messages/query' +import type { NextPage } from 'next' +import { useRouter } from 'next/router' +import { NextSeo } from 'next-seo' +import { useEffect, useState } from 'react' +import { toast } from 'react-hot-toast' +import { useQuery } from 'react-query' +import { ROYALTY_REGISTRY_ADDRESS } from 'utils/constants' +import { withMetadata } from 'utils/layout' +import { links } from 'utils/links' +import { resolveAddress } from 'utils/resolveAddress' + +const RoyaltyRegistryQueryPage: NextPage = () => { + const { royaltyRegistry: contract } = useContracts() + const wallet = useWallet() + + const contractState = useInputState({ + id: 'contract-address', + name: 'contract-address', + title: 'Royalty Registry Address', + subtitle: 'Address of the Royalty Registry contract', + defaultValue: ROYALTY_REGISTRY_ADDRESS, + }) + const contractAddress = contractState.value + + const collectionAddressState = useInputState({ + id: 'collection-address', + name: 'collection-address', + title: 'Collection Address', + subtitle: 'Address of the collection', + }) + + const protocolAddressState = useInputState({ + id: 'protocol-address', + name: 'protocol-address', + title: 'Protocol Address', + subtitle: 'Address of the protocol', + }) + + const collectionAddress = collectionAddressState.value + const protocolAddress = protocolAddressState.value + + const [type, setType] = useState('config') + + const { data: response } = useQuery( + [contractAddress, type, contract, wallet, collectionAddress, protocolAddress] as const, + async ({ queryKey }) => { + const [_contractAddress, _type, _contract, _wallet, _collectionAddress, _protocolAddress] = queryKey + const messages = contract?.use(contractAddress) + const res = await resolveAddress(_collectionAddress, wallet).then(async (resolvedAddress) => { + const result = await dispatchQuery({ + messages, + type, + collection: resolvedAddress, + protocol: _protocolAddress, + }) + return result + }) + return res + }, + { + placeholderData: null, + onError: (error: any) => { + toast.error(error.message, { style: { maxWidth: 'none' } }) + }, + enabled: Boolean(contractAddress && contract && wallet), + }, + ) + + const router = useRouter() + + useEffect(() => { + if (contractAddress.length > 0) { + void router.replace({ query: { contractAddress } }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [contractAddress]) + useEffect(() => { + const initial = new URL(document.URL).searchParams.get('contractAddress') + if (initial && initial.length > 0) contractState.onChange(initial) + }, []) + + return ( +
+ + + + +
+
+ + + + + + + + + + +
+ +
+
+ ) +} + +export default withMetadata(RoyaltyRegistryQueryPage, { center: false }) From b398650794be8872bf16be52263bedc62b2d9d74 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 11:58:22 +0300 Subject: [PATCH 10/13] Update sidebar --- components/Sidebar.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx index e240af3..f5e228e 100644 --- a/components/Sidebar.tsx +++ b/components/Sidebar.tsx @@ -233,6 +233,15 @@ export const Sidebar = () => { > Splits Contract +
  • + Royalty Registry +
  • From 51acae6a78626324af525044c59faf80988203a4 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 12:30:54 +0300 Subject: [PATCH 11/13] Match inputs and message types for execute --- contracts/royaltyRegistry/contract.ts | 26 +++++++++----- pages/contracts/royaltyRegistry/execute.tsx | 40 +++++++++++++++++++-- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/contracts/royaltyRegistry/contract.ts b/contracts/royaltyRegistry/contract.ts index c4688a5..9ebe5fb 100644 --- a/contracts/royaltyRegistry/contract.ts +++ b/contracts/royaltyRegistry/contract.ts @@ -203,7 +203,7 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) txSigner, contractAddress, { - set_collection_royalty_default: { collection, recipient, share }, + set_collection_royalty_default: { collection, recipient, share: share / 100 }, }, 'auto', ) @@ -220,7 +220,12 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) txSigner, contractAddress, { - update_collection_royalty_default: { collection, recipient, share_delta: shareDelta, decrement }, + update_collection_royalty_default: { + collection, + recipient, + share_delta: shareDelta ? shareDelta / 100 : undefined, + decrement, + }, }, 'auto', ) @@ -237,7 +242,7 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) txSigner, contractAddress, { - set_collection_royalty_protocol: { collection, protocol, recipient, share }, + set_collection_royalty_protocol: { collection, protocol, recipient, share: share / 100 }, }, 'auto', ) @@ -259,7 +264,7 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) collection, protocol, recipient, - share_delta: shareDelta, + share_delta: shareDelta ? shareDelta / 100 : undefined, decrement, }, }, @@ -328,7 +333,7 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) sender: txSigner, contract: contractAddress, msg: { - set_collection_royalty_default: { collection, recipient, share }, + set_collection_royalty_default: { collection, recipient, share: share / 100 }, }, funds: [], } @@ -344,7 +349,12 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) sender: txSigner, contract: contractAddress, msg: { - update_collection_royalty_default: { collection, recipient, share_delta: shareDelta, decrement }, + update_collection_royalty_default: { + collection, + recipient, + share_delta: shareDelta ? shareDelta / 100 : undefined, + decrement, + }, }, funds: [], } @@ -355,7 +365,7 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) sender: txSigner, contract: contractAddress, msg: { - set_collection_royalty_protocol: { collection, protocol, recipient, share }, + set_collection_royalty_protocol: { collection, protocol, recipient, share: share / 100 }, }, funds: [], } @@ -376,7 +386,7 @@ export const RoyaltyRegistry = (client: SigningCosmWasmClient, txSigner: string) collection, protocol, recipient, - share_delta: shareDelta, + share_delta: shareDelta ? shareDelta / 100 : undefined, decrement, }, }, diff --git a/pages/contracts/royaltyRegistry/execute.tsx b/pages/contracts/royaltyRegistry/execute.tsx index 0bd241c..7c316d1 100644 --- a/pages/contracts/royaltyRegistry/execute.tsx +++ b/pages/contracts/royaltyRegistry/execute.tsx @@ -1,10 +1,11 @@ +import clsx from 'clsx' import { Button } from 'components/Button' import { Conditional } from 'components/Conditional' import { ContractPageHeader } from 'components/ContractPageHeader' import { ExecuteCombobox } from 'components/contracts/royaltyRegistry/ExecuteCombobox' import { useExecuteComboboxState } from 'components/contracts/royaltyRegistry/ExecuteCombobox.hooks' import { FormControl } from 'components/FormControl' -import { AddressInput } from 'components/forms/FormInput' +import { AddressInput, NumberInput } from 'components/forms/FormInput' import { useInputState, useNumberInputState } from 'components/forms/FormInput.hooks' import { JsonPreview } from 'components/JsonPreview' import { LinkTabs } from 'components/LinkTabs' @@ -69,14 +70,16 @@ const RoyaltyRegistryExecutePage: NextPage = () => { id: 'share', name: 'share', title: 'Share', - subtitle: 'Share', + subtitle: 'Share percentage', + placeholder: '4%', }) const shareDeltaState = useNumberInputState({ id: 'share-delta', name: 'share-delta', title: 'Share Delta', - subtitle: 'Share delta', + subtitle: 'The change of share percentage', + placeholder: '1%', }) const [decrement, setDecrement] = useState(false) @@ -152,10 +155,41 @@ const RoyaltyRegistryExecutePage: NextPage = () => {
    + + + + + + + + + +
    +
    + +
    + Decrement +
    +
    +
    From 5a80ad1587e0f6c60ddfcc11344182ba260e9572 Mon Sep 17 00:00:00 2001 From: Serkan Reis Date: Sat, 7 Oct 2023 12:51:37 +0300 Subject: [PATCH 13/13] Bump Studio version --- .env.example | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 794cc92..9b264f6 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -APP_VERSION=0.7.9 +APP_VERSION=0.7.10 NEXT_PUBLIC_PINATA_ENDPOINT_URL=https://api.pinata.cloud/pinning/pinFileToIPFS NEXT_PUBLIC_SG721_CODE_ID=2595 diff --git a/package.json b/package.json index 85365c0..0253cb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stargaze-studio", - "version": "0.7.9", + "version": "0.7.10", "workspaces": [ "packages/*" ],