Skip to content

Commit

Permalink
feat: v4 edit cycle tx (#4448)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyd-eth authored Sep 9, 2024
1 parent d9a8b89 commit e655d8e
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 26 deletions.
86 changes: 86 additions & 0 deletions src/packages/v4/hooks/useEditRulesetTx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { TxHistoryContext } from 'contexts/Transaction/TxHistoryContext'
import { useWallet } from 'hooks/Wallet'
import { NATIVE_TOKEN } from 'juice-sdk-core'
import { useJBContractContext, useWriteJbControllerLaunchRulesetsFor } from 'juice-sdk-react'
import { useCallback, useContext } from 'react'
import { transformEditCycleFormFieldsToTxArgs } from '../utils/editRuleset'
import { EditCycleFormFields } from '../views/V4ProjectSettings/EditCyclePage/EditCycleFormFields'

export interface EditRulesetTxOpts {
onTransactionPending: (hash: `0x${string}`) => void
onTransactionConfirmed: () => void
onTransactionError: (error: Error) => void
}

/**
* Takes data in EditCycleFormFields format, converts it to Edit Ruleset tx format and passes it to `writeEditRuleset`
* @returns A function that deploys a project.
*/
export function useEditRulesetTx() {
const { writeContractAsync: writeEditRuleset } = useWriteJbControllerLaunchRulesetsFor()
const { contracts } = useJBContractContext()

const { addTransaction } = useContext(TxHistoryContext)

const { userAddress } = useWallet()

return useCallback(
async (formValues: EditCycleFormFields,
{
onTransactionPending: onTransactionPendingCallback,
onTransactionConfirmed: onTransactionConfirmedCallback,
onTransactionError: onTransactionErrorCallback,
}: EditRulesetTxOpts
) => {
if (
!contracts.controller.data ||
!contracts.primaryNativeTerminal.data ||
!userAddress
) {
return
}

const args = transformEditCycleFormFieldsToTxArgs({
formValues,
primaryNativeTerminal: contracts.primaryNativeTerminal.data,
tokenAddress: NATIVE_TOKEN
})

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

const hash = await writeEditRuleset({
address: contracts.controller.data,
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'),
)
}
},
[
contracts.controller.data,
userAddress,
writeEditRuleset,
contracts.primaryNativeTerminal.data,
addTransaction,
],
)
}
115 changes: 115 additions & 0 deletions src/packages/v4/utils/editRuleset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import round from "lodash/round";
import { otherUnitToSeconds } from "utils/format/formatTime";
import { EditCycleFormFields } from "../views/V4ProjectSettings/EditCyclePage/EditCycleFormFields";

export function transformEditCycleFormFieldsToTxArgs({
formValues,
primaryNativeTerminal,
tokenAddress,
}: {
formValues: EditCycleFormFields;
primaryNativeTerminal: `0x${string}`;
tokenAddress: `0x${string}`;
}) {
const now = round(new Date().getTime() / 1000);
const mustStartAtOrAfter = now;

const duration = otherUnitToSeconds({
duration: formValues.duration,
unit: formValues.durationUnit.value,
})
const weight = BigInt(formValues.issuanceRate);
const decayPercent = formValues.decayPercent;
const approvalHook = formValues.approvalHook;

const rulesetConfigurations = [
{
mustStartAtOrAfter,
duration,
weight,
decayPercent,
approvalHook,

metadata: {
reservedPercent: formValues.reservedPercent,
redemptionRate: formValues.redemptionRate,
baseCurrency: 1, // Assuming base currency is a constant value, typically USD
pausePay: formValues.pausePay,
pauseRedeem: false, // Defaulting this value since it's not in formValues
pauseCreditTransfers: !formValues.tokenTransfers,
allowOwnerMinting: formValues.allowOwnerMinting,
allowSetCustomToken: false, // Defaulting to false as it's not in formValues
allowTerminalMigration: formValues.allowTerminalMigration,
allowSetTerminals: formValues.allowSetTerminals,
allowSetController: formValues.allowSetController,
allowAddAccountingContext: false, // Defaulting to false as it's not in formValues
allowAddPriceFeed: false, // Defaulting to false as it's not in formValues
ownerMustSendPayouts: false, // Defaulting to false as it's not in formValues
holdFees: formValues.holdFees,
useTotalSurplusForRedemptions: false, // Defaulting to false as it's not in formValues
useDataHookForPay: false, // Defaulting to false as it's not in formValues
useDataHookForRedeem: false, // Defaulting to false as it's not in formValues
dataHook: "0x0000000000000000000000000000000000000000" as `0x${string}`, // Defaulting to a null address
metadata: 0, // Assuming no additional metadata is provided
},

splitGroups: [
{
groupId: BigInt(1), // Assuming 1 for payout splits
splits: formValues.payoutSplits.map((split) => ({
preferAddToBalance: Boolean(split.preferAddToBalance),
percent: Number(split.percent.value),
projectId: BigInt(split.projectId),
beneficiary: split.beneficiary as `0x${string}`,
lockedUntil: split.lockedUntil ?? 0,
hook: split.hook as `0x${string}`,
})),
},
{
groupId: BigInt(2), // Assuming 2 for reserved tokens splits
splits: formValues.reservedTokensSplits.map((split) => ({
preferAddToBalance: Boolean(split.preferAddToBalance),
percent: Number(split.percent.value),
projectId: BigInt(split.projectId),
beneficiary: split.beneficiary as `0x${string}`,
lockedUntil: split.lockedUntil ?? 0,
hook: split.hook as `0x${string}`,
})),
},
],

fundAccessLimitGroups: [
{
terminal: primaryNativeTerminal,
token: tokenAddress,
payoutLimits: [
{
amount: BigInt(formValues.payoutLimit ?? "0"),
currency: 1, // Assuming currency is constant (e.g., USD)
},
],
surplusAllowances: [
{
amount: BigInt(0), // Assuming no surplus allowances for now
currency: 1, // Assuming currency is constant (e.g., USD)
},
],
},
],
},
];

const terminalConfigurations = [
{
terminal: primaryNativeTerminal,
accountingContextsToAccept: [] as const,
},
];

return [
BigInt(now), // Convert the current timestamp to bigint for the first argument
rulesetConfigurations,
terminalConfigurations,
formValues.memo ?? "",
] as const;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { useState } from 'react'
// import { useReconfigureFundingCycle } from '../../../hooks/useReconfigureFundingCycle'
import { useEditCycleFormContext } from '../EditCycleFormContext'
// import { usePrepareSaveEditCycleData } from '../hooks/usePrepareSaveEditCycleData'
import { useEditRulesetTx } from 'packages/v4/hooks/useEditRulesetTx'
import { emitErrorNotification } from 'utils/notifications'
import { TransactionSuccessModal } from '../TransactionSuccessModal'
import { DetailsSectionDiff } from './DetailsSectionDiff'
import { PayoutsSectionDiff } from './PayoutsSectionDiff'
Expand All @@ -26,6 +28,7 @@ export function ReviewConfirmModal({
}) {
const [editCycleSuccessModalOpen, setEditCycleSuccessModalOpen] =
useState<boolean>(false)
const [confirmLoading, setConfirmLoading] = useState<boolean>(false)

const { editCycleForm } = useEditCycleFormContext()

Expand All @@ -38,17 +41,26 @@ export function ReviewConfirmModal({

const memo = useWatch('memo', editCycleForm)
// const { editingFundingCycleConfig } = usePrepareSaveEditCycleData()
const editRulesetTx = useEditRulesetTx()

// const { reconfigureLoading, reconfigureFundingCycle } =
// useReconfigureFundingCycle({
// editingFundingCycleConfig,
// memo: memo ?? '',
// onComplete: () => {
// editCycleForm?.resetFields()
// setEditCycleSuccessModalOpen(true)
// onClose()
// },
// })

const handleConfirm = () => {
setConfirmLoading(true)
editRulesetTx(editCycleForm?.getFieldsValue(true), {
onTransactionPending: () => null,
onTransactionConfirmed: () => {
editCycleForm?.resetFields()
setConfirmLoading(false)
setEditCycleSuccessModalOpen(true)
onClose()
},
onTransactionError: error => {
console.error(error)
setConfirmLoading(false)
emitErrorNotification(`Error launching ruleset: ${error}`)
},
})
}

const panelProps = { className: 'text-lg' }

Expand All @@ -58,12 +70,12 @@ export function ReviewConfirmModal({
open={open}
title={<Trans>Review & confirm</Trans>}
destroyOnClose
onOk={() => null}//reconfigureFundingCycle()}
onOk={handleConfirm}
okText={<Trans>Deploy changes</Trans>}
okButtonProps={{ disabled: !formHasChanges }}
cancelButtonProps={{ hidden: true }}
onCancel={onClose}
confirmLoading={false}//reconfigureLoading}
confirmLoading={confirmLoading}
>
<p className="text-secondary text-sm">
<Trans>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function TokensSectionDiff() {
<DiffSection
content={
<div className="mb-5 flex flex-col gap-3 text-sm">
{mintRateHasDiff && currentMintRateAfterDiscountRateApplied && (
{mintRateHasDiff && currentMintRateAfterDiscountRateApplied ? (
<FundingCycleListItem
name={t`Total issuance rate`}
value={
Expand All @@ -77,22 +77,25 @@ export function TokensSectionDiff() {
/>
}
/>
)}
{discountRateHasDiff && currentDiscountRate && (
) : null}

{discountRateHasDiff && currentDiscountRate ? (
<FundingCycleListItem
name={t`Decay rate`}
value={`${newDiscountRate}%`}
oldValue={`${currentDiscountRate}%`}
/>
)}
{redemptionHasDiff && currentRedemptionRate && (
) : null}

{redemptionHasDiff && currentRedemptionRate ? (
<FundingCycleListItem
name={t`Redemption rate`}
value={`${newRedemptionRate}%`}
oldValue={`${currentRedemptionRate}%`}
/>
)}
{allowMintingHasDiff && (
) : null}

{allowMintingHasDiff ? (
<FundingCycleListItem
name={t`Owner token minting`}
value={
Expand All @@ -102,8 +105,9 @@ export function TokensSectionDiff() {
<AllowedValue value={currentAllowMinting} />
}
/>
)}
{tokenTransfersHasDiff && (
) : null}

{tokenTransfersHasDiff ? (
<FundingCycleListItem
name={t`Token transfers`}
value={
Expand All @@ -113,17 +117,19 @@ export function TokensSectionDiff() {
<AllowedValue value={currentTokenTransfers} />
}
/>
)}
{reservedRateHasDiff && currentReservedRate && (
) : null}

{reservedRateHasDiff && currentReservedRate ? (
<FundingCycleListItem
name={t`Reserved rate`}
value={
<span>{newReservedRate}%</span>
}
oldValue={<span>{currentReservedRate}%</span>}
/>
)}
{reservedSplitsHasDiff && (
) : null}

{reservedSplitsHasDiff ? (
<div className="pb-4">
<div className="mb-3 text-sm font-semibold">
<Trans>Reserved recipients:</Trans>
Expand All @@ -136,7 +142,7 @@ export function TokensSectionDiff() {
showDiffs
/>
</div>
)}
) : null}
</div>
}
/>
Expand Down

0 comments on commit e655d8e

Please sign in to comment.