Skip to content

Commit

Permalink
feat: new covalent requirements (#1603)
Browse files Browse the repository at this point in the history
* feat: `COVALENT_CONTRACT_CALL_COUNT` requirements

* fix(WalletActivityRequirements): update popover title

* fix(WalletActivityRequirement): table styling

* cleanup: remove unnecessary comments
  • Loading branch information
BrickheadJohnny authored Feb 24, 2025
1 parent a9d45ba commit 02fa42b
Show file tree
Hide file tree
Showing 15 changed files with 696 additions and 34 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@emotion/styled": "^11.11.0",
"@fuels/connectors": "^0.36.0",
"@fuels/react": "^0.36.0",
"@guildxyz/types": "^1.10.43",
"@guildxyz/types": "^1.10.45",
"@hcaptcha/react-hcaptcha": "^1.4.4",
"@hookform/resolvers": "^3.3.4",
"@lexical/code": "^0.12.0",
Expand Down
80 changes: 53 additions & 27 deletions src/requirements/WalletActivity/WalletActivityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { RequirementFormProps, RequirementType } from "requirements/types"
import { SelectOption } from "types"
import parseFromObject from "utils/parseFromObject"
import { Chain } from "wagmiConfig/chains"
import CovalentContractCallCount from "./components/CovalentContractCallCount"
import CovalentContractCallCountRelative from "./components/CovalentContractCallCountRelative"
import CovalentContractDeploy from "./components/CovalentContractDeploy"
import CovalentContractDeployRelative from "./components/CovalentContractDeployRelative"
import CovalentFirstTx from "./components/CovalentFirstTx"
Expand Down Expand Up @@ -87,6 +89,16 @@ const walletActivityRequirementTypes: SelectOption[] = [
value: "COVALENT_TX_COUNT_RELATIVE",
WalletActivityRequirement: CovalentTxCountRelative,
},
{
label: "Called a contract method",
value: "COVALENT_CONTRACT_CALL_COUNT",
WalletActivityRequirement: CovalentContractCallCount,
},
{
label: "Called a contract method (relative)",
value: "COVALENT_CONTRACT_CALL_COUNT_RELATIVE",
WalletActivityRequirement: CovalentContractCallCountRelative,
},
]

const WalletActivityForm = ({
Expand All @@ -100,7 +112,6 @@ const WalletActivityForm = ({
} = useFormContext()

const type = useWatch({ name: `${baseFieldPath}.type` })
const chain = useWatch({ name: `${baseFieldPath}.chain` })
const isEditMode = !!field?.id

const supportedRequirementTypes = walletActivityRequirementTypes
Expand Down Expand Up @@ -156,10 +167,20 @@ const WalletActivityForm = ({

const resetFields = () => {
resetField(`${baseFieldPath}.address`, { defaultValue: "" })
resetField(`${baseFieldPath}.data.timestamps.minAmount`, { defaultValue: "" })
resetField(`${baseFieldPath}.data.timestamps.maxAmount`, { defaultValue: "" })
resetField(`${baseFieldPath}.data.txCount`, { defaultValue: "" })
resetField(`${baseFieldPath}.data.txValue`, { defaultValue: "" })

resetField(`${baseFieldPath}.data.method`, {
defaultValue: "",
})
resetField(`${baseFieldPath}.data.inputs`, {
defaultValue: [],
})
resetField(`${baseFieldPath}.data.txCount`, {
defaultValue: 1,
})
resetField(`${baseFieldPath}.data.timestamps`, {
defaultValue: {},
})
}

const options = walletActivityRequirementTypes.filter((el) =>
Expand All @@ -168,30 +189,35 @@ const WalletActivityForm = ({

return (
<Stack spacing={4} alignItems="start">
<ChainPicker
controlName={`${baseFieldPath}.chain`}
supportedChains={walletActivitySupportedChains}
/>

{chain && (
<FormControl
isInvalid={!!parseFromObject(errors, baseFieldPath)?.type?.message}
>
<FormLabel>Type</FormLabel>

<ControlledSelect
name={`${baseFieldPath}.type`}
rules={{ required: "It's required to select a type" }}
options={options}
beforeOnChange={resetFields}
isDisabled={isEditMode}
/>

<FormErrorMessage>
{parseFromObject(errors, baseFieldPath)?.type?.message}
</FormErrorMessage>
</FormControl>

{selected && (
<>
<FormControl
isInvalid={!!parseFromObject(errors, baseFieldPath)?.type?.message}
>
<FormLabel>Type</FormLabel>

<ControlledSelect
name={`${baseFieldPath}.type`}
rules={{ required: "It's required to select a type" }}
options={options}
beforeOnChange={resetFields}
isDisabled={isEditMode}
/>

<FormErrorMessage>
{parseFromObject(errors, baseFieldPath)?.type?.message}
</FormErrorMessage>
</FormControl>
<ChainPicker
controlName={`${baseFieldPath}.chain`}
supportedChains={
// We only support INK with these two requirement types
selected.value.startsWith("COVALENT_CONTRACT_CALL_COUNT")
? ["INK", "INK_SEPOLIA"]
: walletActivitySupportedChains
}
/>

{selected?.WalletActivityRequirement && (
<selected.WalletActivityRequirement baseFieldPath={baseFieldPath} />
Expand Down
116 changes: 116 additions & 0 deletions src/requirements/WalletActivity/WalletActivityRequirement.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { anchorVariants } from "@/components/ui/Anchor"
import { Button } from "@/components/ui/Button"
import {
Popover,
PopoverContent,
PopoverPortal,
PopoverTrigger,
} from "@/components/ui/Popover"
import { IconProps } from "@phosphor-icons/react/dist/lib/types"
import {
ArrowSquareOut,
ArrowsLeftRight,
Coins,
FileText,
Function,
Wallet,
} from "@phosphor-icons/react/dist/ssr"
import { BeforeAfterDates } from "components/[guild]/Requirements/components/DataBlockWithDate"
Expand All @@ -15,6 +25,7 @@ import { useRequirementContext } from "components/[guild]/Requirements/component
import { DataBlock } from "components/common/DataBlock"
import { DataBlockWithCopy } from "components/common/DataBlockWithCopy"
import { ForwardRefExoticComponent, RefAttributes } from "react"
import { Requirement as RequirementType } from "types"
import formatRelativeTimeFromNow from "utils/formatRelativeTimeFromNow"
import pluralize from "utils/pluralize"
import shortenHex from "utils/shortenHex"
Expand All @@ -31,6 +42,8 @@ const requirementIcons: Record<
COVALENT_TX_COUNT_RELATIVE: ArrowsLeftRight,
COVALENT_TX_VALUE: Coins,
COVALENT_TX_VALUE_RELATIVE: Coins,
COVALENT_CONTRACT_CALL_COUNT: Function,
COVALENT_CONTRACT_CALL_COUNT_RELATIVE: Function,
}

type CovalentRequirementType =
Expand All @@ -42,6 +55,8 @@ type CovalentRequirementType =
| "COVALENT_TX_COUNT_RELATIVE"
| "COVALENT_TX_VALUE"
| "COVALENT_TX_VALUE_RELATIVE"
| "COVALENT_CONTRACT_CALL_COUNT"
| "COVALENT_CONTRACT_CALL_COUNT_RELATIVE"

const WalletActivityRequirement = (props: RequirementProps): JSX.Element => {
const requirement = useRequirementContext()
Expand Down Expand Up @@ -207,6 +222,107 @@ const WalletActivityRequirement = (props: RequirementProps): JSX.Element => {
</>
)
}
case "COVALENT_CONTRACT_CALL_COUNT":
case "COVALENT_CONTRACT_CALL_COUNT_RELATIVE": {
const formattedMinAmount = formatRelativeTimeFromNow(
reqData.timestamps.minAmount
)

const formattedMaxAmount = formatRelativeTimeFromNow(
reqData.timestamps.maxAmount
)

const req = requirement as Extract<
RequirementType,
{
type:
| "COVALENT_CONTRACT_CALL_COUNT"
| "COVALENT_CONTRACT_CALL_COUNT_RELATIVE"
}
>
return (
<>
<span>{`Call the `}</span>
<DataBlockWithCopy text={req.address}>
{shortenHex(req.address, 3)}
</DataBlockWithCopy>
<span>{" contract's "}</span>
<DataBlock>{req.data.method}</DataBlock>
<span>{" method"}</span>

{req.data.txCount > 1 && <span>{` ${req.data.txCount} times`}</span>}

{req.data.inputs.length > 0 && (
<>
<span>{" with "}</span>
<Popover>
<PopoverTrigger asChild>
<Button
variant="unstyled"
className={anchorVariants({
className: "h-auto p-0",
})}
rightIcon={<ArrowSquareOut weight="bold" />}
>
specific inputs
</Button>
</PopoverTrigger>

<PopoverPortal>
<PopoverContent side="bottom" className="p-0">
<div className="border-border border-b p-1.5 font-bold text-xs uppercase">
Inputs
</div>

<table className="w-full table-fixed rounded-b-xl bg-card dark:bg-blackAlpha">
<thead className="text-xs">
<tr className="border-border border-b [&>th]:p-1.5 [&>th]:text-left">
<th>Input param</th>
<th>Operation</th>
<th>Value</th>
</tr>
</thead>
<tbody className="text-xs">
{req.data.inputs?.map((input) => (
<tr
key={input.index}
className="border-border border-b last:border-b-0 [&>td]:p-1.5"
>
<td>{`${input.index + 1}. param`}</td>
<td>{input.operator}</td>
<td>{input.value}</td>
</tr>
))}
</tbody>
</table>
</PopoverContent>
</PopoverPortal>
</Popover>
</>
)}

{req.type === "COVALENT_CONTRACT_CALL_COUNT" ? (
<BeforeAfterDates minTs={minAmount} maxTs={maxAmount} />
) : (
<>
{formattedMaxAmount && formattedMinAmount ? (
<>
<span>{" between the last "}</span>
<DataBlock>{formattedMinAmount}</DataBlock>
<span>{" - "}</span>
<DataBlock>{formattedMaxAmount}</DataBlock>
</>
) : formattedMinAmount ? (
<>
<span>{" in the last "}</span>
<DataBlock>{formattedMinAmount}</DataBlock>
</>
) : null}
</>
)}
</>
)
}
}
})()}
</Requirement>
Expand Down
Loading

0 comments on commit 02fa42b

Please sign in to comment.