Init splits contract helpers

This commit is contained in:
Serkan Reis 2023-03-18 20:47:39 +03:00
parent 8255c8dd92
commit 6914a40258
5 changed files with 363 additions and 0 deletions

View File

@ -0,0 +1,165 @@
import type { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import type { Coin } from '@cosmjs/proto-signing'
export interface InstantiateResponse {
readonly contractAddress: string
readonly transactionHash: string
}
export interface SplitsInstance {
readonly contractAddress: string
//Query
getAdmin: () => Promise<string>
getMemberWeight: (member: string) => Promise<string>
listMembers: (startAfter?: string, limit?: number) => Promise<string[]>
getGroup: () => Promise<string>
//Execute
updateAdmin: (admin: string) => Promise<string>
distribute: () => Promise<string>
}
export interface SplitsMessages {
updateAdmin: (admin: string) => UpdateAdminMessage
distribute: () => DistributeMessage
}
export interface UpdateAdminMessage {
sender: string
contract: string
msg: {
update_admin: { admin: string }
}
funds: Coin[]
}
export interface DistributeMessage {
sender: string
contract: string
msg: { distribute: Record<string, never> }
funds: Coin[]
}
export interface SplitsContract {
instantiate: (
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
) => Promise<InstantiateResponse>
use: (contractAddress: string) => SplitsInstance
messages: (contractAddress: string) => SplitsMessages
}
export const Splits = (client: SigningCosmWasmClient, txSigner: string): SplitsContract => {
const use = (contractAddress: string): SplitsInstance => {
///QUERY
const listMembers = async (startAfter?: string, limit?: number): Promise<string[]> => {
return client.queryContractSmart(contractAddress, {
list_members: { limit, start_after: startAfter },
})
}
const getMemberWeight = async (address: string): Promise<string> => {
return client.queryContractSmart(contractAddress, {
member: { address },
})
}
const getAdmin = async (): Promise<string> => {
return client.queryContractSmart(contractAddress, {
admin: {},
})
}
const getGroup = async (): Promise<string> => {
return client.queryContractSmart(contractAddress, {
group: {},
})
}
/// EXECUTE
const updateAdmin = async (admin: string): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
update_admin: {
admin,
},
},
'auto',
)
return res.transactionHash
}
const distribute = async (): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
distribute: {},
},
'auto',
)
return res.transactionHash
}
return {
contractAddress,
updateAdmin,
distribute,
getMemberWeight,
getAdmin,
listMembers,
getGroup,
}
}
const instantiate = async (
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
): Promise<InstantiateResponse> => {
const result = await client.instantiate(txSigner, codeId, initMsg, label, 'auto', {
admin,
})
return {
contractAddress: result.contractAddress,
transactionHash: result.transactionHash,
}
}
const messages = (contractAddress: string) => {
const updateAdmin = (admin: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
update_admin: { admin },
},
funds: [],
}
}
const distribute = () => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
distribute: {},
},
funds: [],
}
}
return {
updateAdmin,
distribute,
}
}
return { use, instantiate, messages }
}

View File

@ -0,0 +1,2 @@
export * from './contract'
export * from './useContract'

View File

@ -0,0 +1,77 @@
import type { SplitsInstance } from '../index'
import { useSplitsContract } from '../index'
export type ExecuteType = typeof EXECUTE_TYPES[number]
export const EXECUTE_TYPES = ['update_admin', 'distribute'] as const
export interface ExecuteListItem {
id: ExecuteType
name: string
description?: string
}
export const EXECUTE_LIST: ExecuteListItem[] = [
{
id: 'update_admin',
name: 'Update Admin',
description: `Update the splits contract admin`,
},
{
id: 'distribute',
name: 'Distribute',
description: `Distribute the revenue to the group members`,
},
]
export interface DispatchExecuteProps {
type: ExecuteType
[k: string]: unknown
}
type Select<T extends ExecuteType> = T
/** @see {@link SplitsInstance} */
export type DispatchExecuteArgs = {
contract: string
messages?: SplitsInstance
} & ({ type: Select<'update_admin'>; admin: string } | { type: Select<'distribute'> })
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 'update_admin': {
return messages.updateAdmin(args.admin)
}
case 'distribute': {
return messages.distribute()
}
default: {
throw new Error('Unknown execution type')
}
}
}
export const previewExecutePayload = (args: DispatchExecuteArgs) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const { messages } = useSplitsContract()
const { contract } = args
switch (args.type) {
case 'update_admin': {
return messages(contract)?.updateAdmin(args.admin)
}
case 'distribute': {
return messages(contract)?.distribute()
}
default: {
return {}
}
}
}
export const isEitherType = <T extends ExecuteType>(type: unknown, arr: T[]): type is T => {
return arr.some((val) => type === val)
}

View File

@ -0,0 +1,43 @@
import type { SplitsInstance } from '../contract'
export type QueryType = typeof QUERY_TYPES[number]
export const QUERY_TYPES = ['admin', 'group', 'member', 'list_members'] as const
export interface QueryListItem {
id: QueryType
name: string
description?: string
}
export const QUERY_LIST: QueryListItem[] = [
{ id: 'admin', name: 'Query Admin', description: 'View the splits contract admin' },
{ id: 'member', name: 'Query Member Weight', description: 'Check the weight of a member in the group' },
{ id: 'list_members', name: 'Query Members', description: 'View the group members' },
{ id: 'group', name: 'Query Group Contract Address', description: 'View the group contract address' },
]
export interface DispatchQueryProps {
messages: SplitsInstance | undefined
type: QueryType
address: string
startAfter: string
limit: number
}
export const dispatchQuery = (props: DispatchQueryProps) => {
const { messages, type, address, startAfter, limit } = props
switch (type) {
case 'list_members':
return messages?.listMembers(startAfter, limit)
case 'admin':
return messages?.getAdmin()
case 'member':
return messages?.getMemberWeight(address)
case 'group':
return messages?.getGroup()
default: {
throw new Error('unknown query type')
}
}
}

View File

@ -0,0 +1,76 @@
/* eslint-disable eslint-comments/disable-enable-pair */
import { useWallet } from 'contexts/wallet'
import { useCallback, useEffect, useState } from 'react'
import type { InstantiateResponse, SplitsContract, SplitsInstance, SplitsMessages } from './contract'
import { Splits as initContract } from './contract'
export interface UseSplitsContractProps {
instantiate: (
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
) => Promise<InstantiateResponse>
use: (customAddress?: string) => SplitsInstance | undefined
updateContractAddress: (contractAddress: string) => void
messages: (contractAddress: string) => SplitsMessages | undefined
}
export function useSplitsContract(): UseSplitsContractProps {
const wallet = useWallet()
const [address, setAddress] = useState<string>('')
const [splits, setSplits] = useState<SplitsContract>()
useEffect(() => {
setAddress(localStorage.getItem('contract_address') || '')
}, [])
useEffect(() => {
const splitsContract = initContract(wallet.getClient(), wallet.address)
setSplits(splitsContract)
}, [wallet])
const updateContractAddress = (contractAddress: string) => {
setAddress(contractAddress)
}
const instantiate = useCallback(
(codeId: number, initMsg: Record<string, unknown>, label: string, admin?: string): Promise<InstantiateResponse> => {
return new Promise((resolve, reject) => {
if (!splits) {
reject(new Error('Contract is not initialized.'))
return
}
splits.instantiate(codeId, initMsg, label, admin).then(resolve).catch(reject)
})
},
[splits],
)
const use = useCallback(
(customAddress = ''): SplitsInstance | undefined => {
return splits?.use(address || customAddress)
},
[splits, address],
)
const messages = useCallback(
(customAddress = ''): SplitsMessages | undefined => {
return splits?.messages(address || customAddress)
},
[splits, address],
)
return {
instantiate,
use,
updateContractAddress,
messages,
}
}