Skip to content

Commit

Permalink
feat: v4 deploy erc20 (#4453)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyd-eth committed Sep 9, 2024
1 parent 34032bf commit 779b765
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/locales/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,9 @@ msgstr ""
msgid "Migrate payment terminal"
msgstr ""

msgid "Failed to create ERC20 token: {0}"
msgstr ""

msgid "Payments"
msgstr ""

Expand Down
8 changes: 8 additions & 0 deletions src/packages/v4/hooks/useProjectHasErc20Token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useReadJbTokensTokenOf } from 'juice-sdk-react'
import { isZeroAddress } from 'utils/address'

export const useProjectHasErc20Token = () => {
const { data: tokenAddress } = useReadJbTokensTokenOf()

return Boolean(tokenAddress && !isZeroAddress(tokenAddress))
}
67 changes: 67 additions & 0 deletions src/packages/v4/hooks/useV4IssueErc20TokenTx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useCallback, useContext } from 'react'

import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext'
import { useJBContractContext, useWriteJbTokensDeployErc20For } from 'juice-sdk-react'
import { zeroAddress } from 'viem'
import { BaseTxOpts } from '../models/transactions'

export function useV4IssueErc20TokenTx() {
const { addTransaction } = useContext(TxHistoryContext)
const { projectId, contracts } = useJBContractContext()

const { writeContractAsync: deployErc20 } = useWriteJbTokensDeployErc20For()

return useCallback (
async ({ name, symbol }: {
name: string
symbol: string
},
{
onTransactionPending: onTransactionPendingCallback,
onTransactionConfirmed: onTransactionConfirmedCallback,
onTransactionError: onTransactionErrorCallback,
}: BaseTxOpts
) => {
if (
!projectId || !name || !symbol
) {
return
}

const args = [projectId, name, symbol, `${zeroAddress}000000000000000000000000`] as const

try {
// SIMULATE TX:
// const encodedData = encodeFunctionData({
// abi: jbTokensAbi, // ABI of the contract
// functionName: 'deployErc20For',
// args,
// })

const hash = await deployErc20({
args,
})

onTransactionPendingCallback(hash)
addTransaction?.('Edit Ruleset', { hash })
// const transactionReceipt: WaitForTransactionReceiptReturnType = await waitForTransactionReceipt(
// wagmiConfig,
// {
// hash,
// },
// )

onTransactionConfirmedCallback()
} catch (e) {
onTransactionErrorCallback(
(e as Error) ?? new Error('Transaction failed'),
)
}
},
[
deployErc20,
projectId,
addTransaction,
],
)
}
5 changes: 5 additions & 0 deletions src/packages/v4/models/transactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface BaseTxOpts {
onTransactionPending: (hash?: `0x${string}`) => void
onTransactionConfirmed: (hash?: `0x${string}`) => void
onTransactionError: (error: Error) => void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Trans, t } from '@lingui/macro'
import { Button, Form, Input } from 'antd'
import { IssueErc20TokenTxArgs } from 'components/buttons/IssueErc20TokenButton'
import TransactionModal from 'components/modals/TransactionModal'
import { ISSUE_ERC20_EXPLANATION } from 'components/strings'
import { useProjectHasErc20Token } from 'packages/v4/hooks/useProjectHasErc20Token'
import { useV4IssueErc20TokenTx } from 'packages/v4/hooks/useV4IssueErc20TokenTx'
import { useV4WalletHasPermission } from 'packages/v4/hooks/useV4WalletHasPermission'
import { V4OperatorPermission } from 'packages/v4/models/v4Permissions'
import { useState } from 'react'
import { emitErrorNotification } from 'utils/notifications'

export function CreateErc20TokenSettingsPage() {
const [form] = Form.useForm<IssueErc20TokenTxArgs>()
const [loading, setLoading] = useState<boolean>()
const [transactionModalOpen, setTransactionModalOpen] =
useState<boolean>(false)
const [transactionPending, setTransactionPending] = useState<boolean>(false)
const issueErc20TokenTx = useV4IssueErc20TokenTx()
const projectHasErc20Token = useProjectHasErc20Token()
const hasIssueTicketsPermission = useV4WalletHasPermission(
V4OperatorPermission.DEPLOY_ERC20,
)

const canCreateErc20Token = !projectHasErc20Token && hasIssueTicketsPermission

async function onSetENSNameFormSaved(values: IssueErc20TokenTxArgs) {
await form.validateFields()

if (!issueErc20TokenTx) {
emitErrorNotification(t`ERC20 transaction not ready. Try again.`)
return
}

setLoading(true)

issueErc20TokenTx(
{ name: values.name, symbol: values.symbol },
{
onTransactionPending: () => {
setTransactionPending(true)
setTransactionModalOpen(true)
},
onTransactionConfirmed: () => {
setTransactionPending(false)
setTransactionModalOpen(false)
setLoading(false)
setTimeout(() => {
window.location.reload()
}, 1000)
},
onTransactionError: (e: Error) => {
setTransactionPending(false)
setTransactionModalOpen(false)
setLoading(false)
emitErrorNotification(e.message)
emitErrorNotification(
t`Failed to create ERC20 token: ${e.message}`,
)
},
},
)
}

if (!canCreateErc20Token) {
return (
<div>
<p>
Token is already created or you do not have permission to create it.
</p>
</div>
)
}

return (
<>
<p>{ISSUE_ERC20_EXPLANATION}</p>
<Form
className="mt-5 w-full md:max-w-sm"
form={form}
onFinish={onSetENSNameFormSaved}
>
<Form.Item
name="name"
label={t`Token name`}
rules={[{ required: true, message: t`Token name is required` }]}
>
<Input placeholder={t`Project Token`} />
</Form.Item>
<Form.Item
name="symbol"
label={t`Token ticker`}
rules={[{ required: true, message: t`Token ticker is required` }]}
>
<Input
placeholder="PRJ"
onChange={e =>
form.setFieldsValue({ symbol: e.target.value.toUpperCase() })
}
/>
</Form.Item>
<Button
className="mt-3"
htmlType="submit"
loading={loading}
type="primary"
>
<span>
<Trans>Create ERC-20 Token</Trans>
</span>
</Button>
</Form>

<TransactionModal
transactionPending={transactionPending}
title={t`Create ERC-20 Token`}
open={transactionModalOpen}
onCancel={() => setTransactionModalOpen(false)}
onOk={() => setTransactionModalOpen(false)}
confirmLoading={loading}
centered
/>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Button, Layout } from 'antd'
import Link from 'next/link'
import { useMemo } from 'react'
import { twJoin } from 'tailwind-merge'
import { CreateErc20TokenSettingsPage } from './CreateErc20TokenSettingsPage'
import { EditCyclePage } from './EditCyclePage/EditCyclePage'
import { useSettingsPagePath } from './hooks/useSettingsPagePath'
import { ProjectDetailsSettingsPage } from './ProjectDetailsSettingsPage/ProjectDetailsSettingsPage'
Expand All @@ -23,7 +24,7 @@ const SettingsPageComponents: {
transferownership: () => null, //TransferOwnershipSettingsPage,
archiveproject: () => null, //ArchiveProjectSettingsPage,
heldfees: () => null, //ProcessHeldFeesPage,
createerc20: () => null, //CreateErc20TokenSettingsPage,
createerc20: CreateErc20TokenSettingsPage,
}

const V4SettingsPageKeyTitleMap = (
Expand Down

0 comments on commit 779b765

Please sign in to comment.