Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staking UI #128

Merged
merged 10 commits into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react'
import TwitterLogo from 'components/TwitterLogo'
import GitHubLogo from 'components/GitHubLogo'

const POWERED_BY_URL = 'https://junonetwork.io'
const TWITTER_URL = 'https://twitter.com/da0_da0'
const GITHUB_URL = 'https://github.com/DA0-DA0'

export default function Footer() {
return (
<footer className="border-t w-full h-24 flex align-center justify-center flex-col">
<div className="flex items-center justify-center w-full">
Powered by{' '}
<a className="pl-1 link link-primary link-hover" href={POWERED_BY_URL}>
<div>Juno</div>
</a>
</div>
<div className="flex items-center justify-center w-full">
<a href={TWITTER_URL}>
<TwitterLogo />
</a>
<a href={GITHUB_URL}>
<GitHubLogo />
</a>
</div>
</footer>
)
}
27 changes: 2 additions & 25 deletions components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { ReactNode } from 'react'
import Head from 'next/head'
import Footer from './Footer'
import Nav from './Nav'
import TwitterLogo from 'components/TwitterLogo'
import GitHubLogo from 'components/GitHubLogo'

const PUBLIC_SITE_TITLE = process.env.NEXT_PUBLIC_SITE_TITLE

const POWERED_BY_URL = 'https://junonetwork.io'
const TWITTER_URL = 'https://twitter.com/da0_da0'
const GITHUB_URL = 'https://github.com/DA0-DA0'

export default function Layout({ children }: { children: ReactNode }) {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-base-100 text-base-content">
Expand All @@ -23,25 +18,7 @@ export default function Layout({ children }: { children: ReactNode }) {
<main className="flex flex-col items-center justify-center w-full flex-1 p-2 md:px-20 text-center">
{children}
</main>
<footer className="border-t w-full h-24 flex align-center justify-center flex-col">
<div className="flex items-center justify-center w-full">
Powered by{' '}
<a
className="pl-1 link link-primary link-hover"
href={POWERED_BY_URL}
>
<div>Juno</div>
</a>
</div>
<div className="flex items-center justify-center w-full">
<a href={TWITTER_URL}>
<TwitterLogo />
</a>
<a href={GITHUB_URL}>
<GitHubLogo />
</a>
</div>
</footer>
<Footer />
</div>
)
}
2 changes: 1 addition & 1 deletion components/TokenBalances.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Cw20CoinVerified } from '@dao-dao/types/contracts/cw3-dao'
import TokenBalance from 'components/TokenBalance'
import Cw20TokenBalance from 'components/Cw20TokenBalance'

function TokenBalances({
export function TokenBalances({
native,
cw20Balances,
}: {
Expand Down
48 changes: 48 additions & 0 deletions hooks/cw20.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useState, useEffect } from 'react'
import { useSigningClient } from 'contexts/cosmwasm'
import { BalanceResponse } from '@dao-dao/types/contracts/cw20-gov/balance_response'
import { TokenInfoResponse } from '@dao-dao/types/contracts/cw20-gov/token_info_response'

// Returns cw20 balance info for the connected wallet
export function useCw20WalletBalance(contractAddress: string) {
const [error, setError] = useState<Error | null>(null)
const [loading, setLoading] = useState(false)
const { walletAddress, signingClient } = useSigningClient()
const [balance, setBalance] = useState<BalanceResponse | null>(null)
const [tokenInfo, setTokenInfo] = useState<TokenInfoResponse | null>(null)

useEffect(() => {
if (walletAddress.length === 0 || !signingClient) {
return
}
setLoading(true)
Promise.all([
signingClient?.queryContractSmart(contractAddress, {
balance: {
address: walletAddress,
},
}),
signingClient.queryContractSmart(contractAddress, {
token_info: {},
}),
])
.then((values) => {
const [balance, tokenInfo] = values
setBalance(balance)
setTokenInfo(tokenInfo)
setLoading(false)
})
.catch((e) => {
setError(e.msg)
setLoading(false)
})
}, [contractAddress, signingClient, walletAddress])

return {
walletAddress,
loading,
error,
balance,
tokenInfo,
}
}
12 changes: 9 additions & 3 deletions hooks/proposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import { memoForProposal, Proposal } from 'models/proposal/proposal'
import { messageForProposal } from 'models/proposal/proposalSelectors'
import { defaultExecuteFee } from 'util/fee'

// Returns a list of proposals and pagination methods
export function useProposals(contractAddress: string) {
const { walletAddress, signingClient } = useSigningClient()
const [error, setError] = useState<Error | null>(null)
const [loading, setLoading] = useState(false)
const [proposals, setProposals] = useState<ProposalResponse[]>([])
const [hideLoadMore, setHideLoadMore] = useState(false)
const [loading, setLoading] = useState(false)
const [startBefore, setStartBefore] = useState<number | null>(null)

useEffect(() => {
Expand All @@ -42,21 +44,23 @@ export function useProposals(contractAddress: string) {
}
setProposals((p) => p.concat(response.proposals))
} catch (err) {
setError(err)
setLoading(false)
}
}
sign(signingClient)
}, [walletAddress, signingClient, startBefore, contractAddress])
return { proposals, hideLoadMore, loading, setStartBefore }
return { proposals, hideLoadMore, loading, error, setStartBefore }
}

// Returns proposal info and associated contract methods
export function useProposal(contractAddress: string, proposalId: string) {
const { walletAddress, signingClient } = useSigningClient()
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const [votes, setVotes] = useState<VoteInfo[]>([])
const [tally, setTally] = useState<ProposalTallyResponse>()
const [proposal, setProposal] = useState<ProposalResponse | null>(null)
const [error, setError] = useState('')
const [timestamp, setTimestamp] = useState(new Date())
const [transactionHash, setTransactionHash] = useState('')

Expand Down Expand Up @@ -166,6 +170,8 @@ export function useProposal(contractAddress: string, proposalId: string) {
}
}

// Returns an excute method used to create a proposal
// When a proposal is created, it returns a proposalID
export function useCreateProposal(contractAddress: string) {
const { walletAddress, signingClient } = useSigningClient()
const [error, setError] = useState('')
Expand Down
158 changes: 158 additions & 0 deletions hooks/staking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { useState, useEffect } from 'react'
import { useSigningClient } from 'contexts/cosmwasm'
import { defaultExecuteFee } from 'util/fee'

// Returns staking info and methods for a cw20-stakable contract
export function useStaking(contractAddress: string) {
const { walletAddress, signingClient } = useSigningClient()
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)

const [stakedBalance, setStakedBalance] = useState({ balance: '0' })
const [claims, setClaims] = useState()
const [delegation, setDelegation] = useState()
const [totalStaked, setTotalStaked] = useState({ total: '0' })

useEffect(() => {
if (walletAddress.length === 0 || !signingClient) {
return
}
setLoading(true)
Promise.all([
signingClient?.queryContractSmart(contractAddress, {
staked_balance_at_height: {
address: walletAddress,
},
}),
signingClient?.queryContractSmart(contractAddress, {
claims: {
address: walletAddress,
},
}),
signingClient?.queryContractSmart(contractAddress, {
delegation: {
address: walletAddress,
},
}),
signingClient?.queryContractSmart(contractAddress, {
total_staked_at_height: {},
}),
])
.then((values) => {
const [stakedBalance, claims, delegation, totalStaked] = values
setStakedBalance(stakedBalance)
setClaims(claims)
setTotalStaked(totalStaked)
setDelegation(delegation)
setLoading(false)
})
.catch((e) => {
setError(e.msg)
setLoading(false)
})
}, [contractAddress, signingClient, walletAddress])

const claim = async () => {
setError('')
await signingClient
?.execute(
walletAddress,
contractAddress,
{
claim: {},
},
defaultExecuteFee
)
.then((response) => {
console.log(response)
setLoading(false)
})
.catch((err) => {
setLoading(false)
setError(err.message)
})
}

const delegateVotes = async (recipient: string) => {
setError('')
await signingClient
?.execute(
walletAddress,
contractAddress,
{
delegate_votes: {
recipient,
},
},
defaultExecuteFee
)
.then((response) => {
console.log(response)
setLoading(false)
})
.catch((err) => {
setLoading(false)
setError(err.message)
})
}

const stake = async (amount: string) => {
setError('')
await signingClient
?.execute(
walletAddress,
contractAddress,
{
stake: {
amount,
},
},
defaultExecuteFee
)
.then((response) => {
console.log(response)
setLoading(false)
})
.catch((err) => {
setLoading(false)
setError(err.message)
})
}

const unstake = async (amount: string) => {
setError('')
await signingClient
?.execute(
walletAddress,
contractAddress,
{
unstake: {
amount,
},
},
defaultExecuteFee
)
.then((response) => {
console.log(response)
setLoading(false)
})
.catch((err) => {
setLoading(false)
setError(err.message)
})
}

return {
walletAddress,
loading,
error,
claim,
claims,
delegateVotes,
delegation,
stake,
stakedBalance,
totalStaked,
unstake,
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"dependencies": {
"@cosmjs/cosmwasm-stargate": "^0.27.0-rc2",
"@cosmjs/stargate": "^0.27.0-rc2",
"@dao-dao/types": "^0.0.2",
"@dao-dao/types": "^0.0.3",
"@heroicons/react": "^1.0.5",
"@types/react-json-editor-ajrm": "^2.5.2",
"daisyui": "^1.14.0",
Expand Down
14 changes: 10 additions & 4 deletions pages/dao/[contractAddress]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import React, { useEffect, useState } from 'react'
import React, { useState } from 'react'
import { ChevronRightIcon } from '@heroicons/react/solid'
import WalletLoader from 'components/WalletLoader'
import { useSigningClient } from 'contexts/cosmwasm'
import { useDaoConfig } from 'hooks/dao'
import type { NextPage } from 'next'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { ConfigResponse } from '@dao-dao/types/contracts/cw3-dao'
import LinkCard from 'components/LinkCard'
import ClipboardText from 'components/ClipboardText'
import TrueFalseIndicator from 'components/TrueFalseIndicator'
import { useTokenConfig } from 'hooks/govToken'
import { convertMicroDenomToDenom } from 'util/conversion'

Expand Down Expand Up @@ -37,6 +34,15 @@ function actions(contractAddress: string) {
</h3>
<p className="mt-4 text-xl">Manage DAO finances.</p>
</LinkCard>
<LinkCard href={`/dao/${contractAddress}/staking`}>
<h3 className="text-2xl font-bold">
Staking{' '}
<ChevronRightIcon className="inline-block w-6 h-6 ml-2 stroke-current" />
</h3>
<p className="mt-4 text-xl">
Stake your tokens to vote and earn rewards.
</p>
</LinkCard>
</>
)
}
Expand Down
Loading