Skip to content

Commit

Permalink
ACI Form with various entrypoints
Browse files Browse the repository at this point in the history
  • Loading branch information
ashutoshpw committed Jun 18, 2024
1 parent 40cf3d1 commit b2ac545
Show file tree
Hide file tree
Showing 16 changed files with 1,948 additions and 54 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@mui/material": "^5.14.14",
"@mui/x-date-pickers": "^5.0.2",
"@taquito/beacon-wallet": "^17.3.1",
"@taquito/michel-codec": "^20.0.0",
"@taquito/signer": "^17.3.1",
"@taquito/taquito": "^17.3.1",
"@taquito/tzip12": "^17.3.1",
Expand All @@ -39,6 +40,7 @@
"@types/react-router-hash-link": "^2.4.5",
"@types/valid-url": "^1.0.4",
"assert": "^2.0.0",
"assert-never": "^1.2.1",
"bignumber.js": "^9.0.1",
"blockies-ts": "^1.0.0",
"crypto-browserify": "^3.12.0",
Expand Down
28 changes: 28 additions & 0 deletions src/components/ui/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { styled, Button as MaterialButton } from "@material-ui/core"

export const Button = styled(MaterialButton)(({ theme }) => ({
"fontSize": "14px",
"justifyItems": "center",
"color": "#000",
"boxShadow": "0px 0px 7px -2px rgba(0, 0, 0, 0.2)",
"transition": ".15s ease-in",
"background": theme.palette.secondary.main,
"textTransform": "none",
"borderRadius": 4,
"padding": "8px 15px",
"marginRight": "8px",

"&$disabled": {
boxShadow: "none"
},

"&:hover": {
boxShadow: "0px 0px 7px -2px rgba(0, 0, 0, 0.2)",
backgroundColor: "#62eda5 !important",
transition: ".15s ease-in"
},

["@media (max-width:1030px)"]: {
fontSize: "14px"
}
}))
143 changes: 120 additions & 23 deletions src/modules/explorer/components/ArbitraryContractInteractionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import { toShortAddress } from "services/contracts/utils"
import { useArbitraryContractData } from "services/aci/useArbitratyContractData"
import { useTezos } from "services/beacon/hooks/useTezos"
import { ArbitraryContract } from "models/Contract"
import { evalTaquitoParam, generateExecuteContractMichelson } from "services/aci"
import { emitMicheline, Parser } from "@taquito/michel-codec"
import type { ContractAbstraction } from "@taquito/taquito"
import ProposalExecuteForm from "./ProposalExecuteForm"
import { useLambdaExecutePropose } from "services/contracts/baseDAO/hooks/useLambdaExecutePropose"

interface Parameter {
key: string
Expand Down Expand Up @@ -79,7 +84,8 @@ const BackButtonIcon = styled(ArrowBackIos)(({ theme }) => ({
}))

type ACIValues = {
destination_contract: string
destination_contract: any
destination_contract_address: string
amount: number
target_endpoint: string
parameters: Parameter[]
Expand Down Expand Up @@ -124,15 +130,17 @@ const ContractInteractionForm = ({
showHeader
}: any) => {
const [state, setState] = useState<Status>(Status.NEW_INTERACTION)
const [formState, setFormState] = useState<any>({ address: "", amount: 0, shape: {} })
const [endpoint, setEndpoint] = useState<ContractEndpoint | undefined>(undefined)
const theme = useTheme()
const isMobileSmall = useMediaQuery(theme.breakpoints.down("sm"))
const { mutate: fetchContractData, data } = useArbitraryContractData()
const { network } = useTezos()
// console.log("FormData", data)
const { tezos, network } = useTezos()
const [isLoading, setIsLoading] = useState(false)

const shouldContinue = useMemo(() => {
if (values.destination_contract !== "" && !errors.destination_contract) {
if (values.destination_contract_address !== "" && !errors.destination_contract_address) {
return false
}
return true
Expand All @@ -144,7 +152,7 @@ const ContractInteractionForm = ({
}
setIsLoading(true)
fetchContractData({
contract: getIn(values, "destination_contract"),
contract: getIn(values, "destination_contract_address"),
network: network,
handleContinue: () => setState(Status.CONTRACT_VALIDATED),
finishLoad: () => setIsLoading(false),
Expand All @@ -171,22 +179,32 @@ const ContractInteractionForm = ({
<Grid item xs={isMobileSmall ? 12 : 6}>
<ProposalFormInput label="Destination Contract Address">
<Field
id="destination_contract"
id="destination_contract_address"
placeholder="Enter Address"
name="destination_contract"
name="destination_contract_address"
component={CustomFormikTextField}
onClick={() => setFieldTouched("destination_contract")}
onClick={() => setFieldTouched("destination_contract_address")}
onChange={(newValue: any) => {
setFieldValue("destination_contract", newValue.target.value)
const contractAddress = newValue.target.value.trim()
console.log("Destination Contract Address", contractAddress)
setFieldValue("destination_contract_address", contractAddress)

if (validateContractAddress(contractAddress) === 3) {
tezos.contract.at(contractAddress).then((contract: any) => {
setFieldValue("destination_contract", contract)
})
} else {
console.log("invalid address", contractAddress)
}
}}
value={getIn(values, "destination_contract")}
value={getIn(values, "destination_contract_address")}
inputProps={{
maxLength: 36
}}
/>
</ProposalFormInput>
{errors.destination_contract && touched.destination_contract ? (
<ErrorText>{errors.destination_contract}</ErrorText>
{errors.destination_contract_address && touched.destination_contract_address ? (
<ErrorText>{errors.destination_contract_address}</ErrorText>
) : null}
</Grid>
<Grid item xs={isMobileSmall ? 12 : 6}>
Expand Down Expand Up @@ -219,8 +237,8 @@ const ContractInteractionForm = ({
<Title color="textPrimary">Calling Contract</Title>
<Value>
{isMobileSmall
? toShortAddress(getIn(values, "destination_contract"))
: getIn(values, "destination_contract")}
? toShortAddress(getIn(values, "destination_contract_address"))
: getIn(values, "destination_contract_address")}
</Value>
</SubContainer>
<SubContainer item>
Expand All @@ -229,10 +247,36 @@ const ContractInteractionForm = ({
</SubContainer>
<SubContainer item>
<Title color="textPrimary">Contract Endpoint</Title>
<ProposalFormInput>
<SearchEndpoints endpoints={data ? data.children : []} handleChange={processParameters} />
</ProposalFormInput>
<ProposalExecuteForm
address={values.destination_contract_address}
amount={values.amount}
shape={formState.shape}
reset={() => setFormState({ address: "", amount: 0, shape: {} })}
setField={(lambda: string, metadata: string) => {
// debugger
console.log("SetField", lambda, metadata)
}}
setLoading={() => {}}
setState={shape => {
// debugger
console.log("New Shape", shape)
setFormState((v: any) => ({ ...v, shape }))
}}
onReset={() => {
setFormState({ address: "", amount: 0, shape: {} })
// props.onReset()
}}
loading={false}
onShapeChange={shapeInitValue => {
setFormState((v: any) => ({
...v,
shape: { ...v?.shape, ...shapeInitValue }
}))
}}
/>
</SubContainer>

{/* ACI: Endpoint list */}
{endpoint && (
<SubContainer item>
<FieldArray
Expand Down Expand Up @@ -303,8 +347,57 @@ const ContractInteractionForm = ({
</BackButton>
</Grid>
<Grid item xs={6} container justifyContent="flex-end">
<SmallButtonDialog variant="contained" disabled={!isValid}>
Submit
<SmallButtonDialog
onClick={() => {
console.log({ formState })
// debugger
let entrypoint = formState.shape.token.initValue // accept_ownership | default etc
let taquitoParam

const execContract = formState.shape.contract
const taquitoFullParam = evalTaquitoParam(formState.shape.token, formState.shape.init)
if (execContract?.parameterSchema.isMultipleEntryPoint) {
const p = Object.entries(taquitoFullParam)
if (p.length !== 1) {
throw new Error("should only one entrypoint is selected")
}
;[entrypoint, taquitoParam] = p[0]
} else {
taquitoParam = taquitoFullParam
}
const param = emitMicheline(
execContract?.methodsObject[entrypoint](taquitoParam).toTransferParams()?.parameter?.value
)

const micheline_type = execContract?.parameterSchema.isMultipleEntryPoint
? execContract?.entrypoints.entrypoints[entrypoint]
: execContract?.parameterSchema.root.val

// const micheline_type = values.destination_contract?.parameterSchema.isMultipleEntryPoint
// ? values.destination_contract.entrypoints.entrypoints[entrypoint]
// : values.destination_contract?.parameterSchema.root.val

const p = new Parser()
const type = emitMicheline(p.parseJSON(micheline_type), {
indent: "",
newline: ""
})

const lambda = generateExecuteContractMichelson("1.0.0", {
address: values.destination_contract_address,
entrypoint,
type,
amount: values.amount,
param
})

// TODO: Deploy this to DAO
console.log({ lambda })
}}
variant="contained"
disabled={!isValid}
>
Submit Form
</SmallButtonDialog>
</Grid>
</Grid>
Expand All @@ -316,22 +409,26 @@ const ContractInteractionForm = ({
export const ArbitraryContractInteractionForm: React.FC<{ showHeader: (state: boolean) => void }> = ({
showHeader
}) => {
const { mutate: executeProposeLambda } = useLambdaExecutePropose()
const isInvalidKtOrTzAddress = (address: string) => validateContractAddress(address) !== 3

const initialValue: ACIValues = {
destination_contract: "",
destination_contract: {} as ArbitraryContract,
destination_contract_address: "",
amount: 0,
target_endpoint: "",
parameters: []
}

const validateForm = (values: ACIValues) => {
console.log("validateFormValues", values)
return {}
const errors: FormikErrors<ACIValues> = {}
if (!values.destination_contract) {
errors.destination_contract = "Required"
if (!values.destination_contract_address) {
errors.destination_contract_address = "Required"
}
if (values.destination_contract && isInvalidKtOrTzAddress(values.destination_contract)) {
errors.destination_contract = "Invalid contract address"
if (values.destination_contract_address && isInvalidKtOrTzAddress(values.destination_contract_address)) {
errors.destination_contract_address = "Invalid contract address"
}
if (!values.target_endpoint) {
errors.target_endpoint = "Required"
Expand Down
17 changes: 8 additions & 9 deletions src/modules/explorer/components/ConfigProposalForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable react/display-name */
import { Grid, Typography, TextField, styled } from "@material-ui/core"
import React, { useCallback } from "react"
import React, { useCallback, useMemo } from "react"
import { useDAO } from "services/services/dao/hooks/useDAO"
import { SendButton } from "./ProposalFormSendButton"
import { Controller, FormProvider, useForm } from "react-hook-form"
Expand All @@ -10,6 +10,7 @@ import { useProposeConfigChange } from "../../../services/contracts/baseDAO/hook
import { ResponsiveDialog } from "./ResponsiveDialog"
import * as yup from "yup"
import { yupResolver } from "@hookform/resolvers/yup"
import { code } from "services/contracts/metadataCarrier/code"

const ErrorText = styled(Typography)({
fontSize: 14,
Expand All @@ -27,13 +28,6 @@ type Values = {

export type ProposalFormDefaultValues = RecursivePartial<Values>

interface Props {
open: boolean
handleClose: () => void
defaultValues?: ProposalFormDefaultValues
defaultTab?: number
}

const validationSchema = yup.object({
frozen_extra_value: yup.number().typeError("Amount must be a number"),
returnedPercentage: yup
Expand All @@ -43,7 +37,12 @@ const validationSchema = yup.object({
.typeError("Amount must be a number")
})

export const ConfigProposalForm: React.FC<Props> = ({ open, handleClose }) => {
export const ConfigProposalForm: React.FC<{
open: boolean
handleClose: () => void
defaultValues?: ProposalFormDefaultValues
defaultTab?: number
}> = ({ open, handleClose }) => {
const daoId = useDAOID()
const { data: dao } = useDAO(daoId)

Expand Down
Loading

0 comments on commit b2ac545

Please sign in to comment.