Skip to content
This repository was archived by the owner on Jul 1, 2025. It is now read-only.

Commit 0b84694

Browse files
wtfsayodan13ram
authored andcommitted
add escrow proposal type to list
add escrow icon add proto-escrow-form save: progress escrow form WIP: form mechanics add temp logger Update .gitignore minor improvements add upload feature progress: prepare data for ipfs upload save: improve form UX feat: ipfs upload of escrow data form error ux ready to tx feat: create escrow proposal improve add txn to queue button UX switch to Smart Input update ipfs metadata format ipfs bytes convert fix type issue with value add better summary fix precision pt errors add formstore and minor improvements update escrow detail display and treasury amt alert implement CR feedback minor updates with date add display `deployEscrow` display escrow txn nicer wip: show escrow milestones section WIP: Milestone display display milestones!!!! improve milestone display wip: release button fix safetyvalve helpertext add test for client and recipient address being diff update subgraph-queries with executionTransactionHash lint issues cleanup progress: allow release milestones add milestone txn decoding fix: auto decode release(_milestone) Update EscrowUtils.ts feat: add daoMultiSig via EAS Update SmartContracts.tsx fix: fetch multisig + feat: conditional redirect for milestones added daoGoverner or multiSig as default hide input field if multiSig found refactor Milestone details component Update apps/web/src/modules/create-proposal/components/TransactionForm/Escrow/EscrowForm.tsx Co-authored-by: Dan OneTree <dan13ram@users.noreply.github.com> use lowerCase compare/ apply CR comments remove validation on change + allow button to validate on click fix:the bundler address update: timestamp in seconds + fundAmount val format fix due date and update gitIgnore use existing ipfs gateway update escrow params + bundler params Update MilestoneDetails.tsx Update pnpm-lock.yaml lint fixes Update EscrowDetailsDisplay.tsx Update EscrowDetailsDisplay.tsx remove `removeTransactions()` try/catch `addTransaction` fix type/await Update EscrowForm.tsx implement ux feedback change validation issue Update ProposalDescription.tsx log decoding test new etherscan api fix escrow display v1 cleaner escrow format better move milestone details up! refactor milestone display and wait method prod buildl fix pre: add invoice address from execTxnHash add clear escrow form add priority order to get multisig use treasury instead of governor call it `Escrow Delegate` reverted minor changes reduce changes
1 parent 6f144ab commit 0b84694

File tree

12 files changed

+605
-42
lines changed

12 files changed

+605
-42
lines changed

apps/web/src/data/contract/requests/getDAOAddresses.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { AddressType, CHAIN_ID } from 'src/typings'
55
import { unpackOptionalArray } from 'src/utils/helpers'
66

77
import { managerAbi } from '../abis'
8+
import { getEscrowDelegate } from './getEscrowDelegate'
89

910
const getDAOAddresses = async (chainId: CHAIN_ID, tokenAddress: AddressType) => {
1011
const addresses = await readContract({
@@ -17,15 +18,27 @@ const getDAOAddresses = async (chainId: CHAIN_ID, tokenAddress: AddressType) =>
1718

1819
const [metadata, auction, treasury, governor] = unpackOptionalArray(addresses, 4)
1920

21+
const escrowDelegate = await getEscrowDelegate(treasury as AddressType, chainId)
22+
2023
const hasMissingAddresses = Object.values(addresses).includes(NULL_ADDRESS)
2124
if (hasMissingAddresses) return null
2225

26+
console.log({
27+
token: tokenAddress,
28+
auction,
29+
governor,
30+
metadata,
31+
treasury,
32+
escrowDelegate,
33+
})
34+
2335
return {
2436
token: tokenAddress,
2537
auction,
2638
governor,
2739
metadata,
2840
treasury,
41+
escrowDelegate,
2942
}
3043
}
3144

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import axios from 'axios'
2+
import { checksumAddress, isAddress } from 'viem'
3+
4+
import { CHAIN_ID } from 'src/typings'
5+
6+
interface AttestationResponse {
7+
data: {
8+
attestations: Array<{
9+
attester: string
10+
recipient: string
11+
decodedDataJson: string
12+
}>
13+
}
14+
}
15+
16+
interface DecodedData {
17+
name: string
18+
type: string
19+
value: {
20+
type: string
21+
value: string
22+
}
23+
}
24+
25+
const ATTESTATION_SCHEMA_UID = `0x1289c5f988998891af7416d83820c40ba1c6f5ba31467f2e611172334dc53a0e`
26+
const SMART_INVOICE_MULTISIG = `0x503a5161D1c5D9d82BF35a4c80DA0C3Ad72d9244` // TODO: replace with actual multisig address
27+
const BUILDER_DAO_TREASURY = `0xcf325a4c78912216249b818521b0798a0f904c10`
28+
const BUILDER_DAO_OPS_MULTISIG = `0x58eAEfBEd9EEFbC564E302D0AfAE0B113E42eAb3`
29+
30+
const ATTESTATION_URL: Record<CHAIN_ID, string> = {
31+
[CHAIN_ID.ETHEREUM]: 'https://easscan.org/graphql',
32+
[CHAIN_ID.OPTIMISM]: 'https://optimism.easscan.org/graphql',
33+
[CHAIN_ID.SEPOLIA]: 'https://sepolia.easscan.org/graphql',
34+
[CHAIN_ID.OPTIMISM_SEPOLIA]: 'https://optimism-sepolia.easscan.org/graphql',
35+
[CHAIN_ID.BASE]: 'https://base.easscan.org/graphql',
36+
[CHAIN_ID.BASE_SEPOLIA]: 'https://base-sepolia.easscan.org/graphql',
37+
[CHAIN_ID.ZORA]: '',
38+
[CHAIN_ID.ZORA_SEPOLIA]: '',
39+
[CHAIN_ID.FOUNDRY]: '',
40+
}
41+
42+
export async function getEscrowDelegate(
43+
daoTreasuryAddress: string,
44+
chainId: CHAIN_ID
45+
): Promise<string | null> {
46+
// Input validation
47+
if (!daoTreasuryAddress || !isAddress(daoTreasuryAddress)) {
48+
return null
49+
}
50+
51+
const attestationUrl = ATTESTATION_URL[chainId]
52+
if (!attestationUrl) {
53+
return null
54+
}
55+
56+
const attestationIssuerPriorityOrder = [
57+
checksumAddress(daoTreasuryAddress),
58+
checksumAddress(BUILDER_DAO_TREASURY),
59+
checksumAddress(BUILDER_DAO_OPS_MULTISIG),
60+
checksumAddress(SMART_INVOICE_MULTISIG),
61+
]
62+
63+
const query = `
64+
query Attestations {
65+
attestations(
66+
where: {
67+
schemaId: { equals: "${ATTESTATION_SCHEMA_UID}" }
68+
attester: { in: ["${attestationIssuerPriorityOrder.join('","')}"] }
69+
recipient: { equals: "${checksumAddress(daoTreasuryAddress)}" }
70+
}
71+
) {
72+
attester
73+
recipient
74+
decodedDataJson
75+
}
76+
}
77+
`
78+
79+
try {
80+
const response = await axios.post<AttestationResponse>(
81+
attestationUrl,
82+
{ query },
83+
{
84+
headers: {
85+
'Content-Type': 'application/json',
86+
},
87+
}
88+
)
89+
90+
const attestations = response?.data?.data?.attestations
91+
92+
// Sort attestations based on priority order
93+
const sortedAttestations = attestations.sort((a, b) => {
94+
const indexA = attestationIssuerPriorityOrder.indexOf(a.attester as `0x${string}`)
95+
const indexB = attestationIssuerPriorityOrder.indexOf(b.attester as `0x${string}`)
96+
return indexA - indexB
97+
})
98+
99+
if (!attestations?.length) {
100+
return null
101+
}
102+
103+
try {
104+
// Get the first attestation from priority
105+
const decodedData = JSON.parse(
106+
sortedAttestations[0].decodedDataJson
107+
) as DecodedData[]
108+
109+
const escrowDelegateAddress = decodedData[0]?.value?.value
110+
if (!escrowDelegateAddress || !isAddress(escrowDelegateAddress)) {
111+
return null
112+
}
113+
114+
return escrowDelegateAddress
115+
} catch (parseError) {
116+
console.error('Error parsing attestation data:', parseError)
117+
return null
118+
}
119+
} catch (error) {
120+
console.error('Error fetching attestations:', error)
121+
return null
122+
}
123+
}

apps/web/src/modules/create-proposal/components/TransactionForm/Escrow/EscrowForm.tsx

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ const EscrowForm: React.FC<EscrowFormProps> = ({
119119

120120
const { formValues, setFormValues } = useEscrowFormStore()
121121
const {
122-
addresses: { treasury },
122+
addresses: { escrowDelegate, treasury },
123123
} = useDaoStore()
124124

125125
const handleSubmit = useCallback(
@@ -156,7 +156,7 @@ const EscrowForm: React.FC<EscrowFormProps> = ({
156156
<Formik
157157
initialValues={{
158158
...formValues,
159-
clientAddress: treasury || '',
159+
clientAddress: escrowDelegate || treasury || '',
160160
}}
161161
validationSchema={EscrowFormSchema}
162162
onSubmit={handleSubmit}
@@ -191,23 +191,25 @@ const EscrowForm: React.FC<EscrowFormProps> = ({
191191
'The wallet address to which funds will be released on milestone completions.'
192192
}
193193
/>
194-
<SmartInput
195-
type={TEXT}
196-
formik={formik}
197-
{...formik.getFieldProps('clientAddress')}
198-
id="clientAddress"
199-
inputLabel={'Client'}
200-
placeholder={'0x... or .eth'}
201-
isAddress={true}
202-
errorMessage={
203-
formik.touched.clientAddress && formik.errors.clientAddress
204-
? formik.errors.clientAddress
205-
: undefined
206-
}
207-
helperText={
208-
'This is the wallet address that will control the escrow for releasing funds. This can be DAO Governer Address or multisig of working group within the DAO.'
209-
}
210-
/>
194+
{!escrowDelegate && (
195+
<SmartInput
196+
type={TEXT}
197+
formik={formik}
198+
{...formik.getFieldProps('clientAddress')}
199+
id="clientAddress"
200+
inputLabel={'Client'}
201+
placeholder={'0x... or .eth'}
202+
isAddress={true}
203+
errorMessage={
204+
formik.touched.clientAddress && formik.errors.clientAddress
205+
? formik.errors.clientAddress
206+
: undefined
207+
}
208+
helperText={
209+
'This is the wallet address that will control the escrow for releasing funds. This can be DAO Governer Address or multisig of working group within the DAO.'
210+
}
211+
/>
212+
)}
211213

212214
<DatePicker
213215
{...formik.getFieldProps('safetyValveDate')}

apps/web/src/modules/create-proposal/constants/transactionType.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export enum TransactionType {
1414
UPDATE_MINTER = 'update-minter',
1515
REPLACE_ARTWORK = 'replace-artwork',
1616
MIGRATION = 'migration',
17+
RELEASE_ESCROW_MILESTONE = 'release-milestone',
1718
}
1819

1920
export interface TransactionTypeProps {

apps/web/src/modules/dao/components/SmartContracts.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ export const SmartContracts = () => {
8989
<ContractLink title="Governor" address={addresses.governor} />
9090
<ContractLink title="Treasury" address={addresses.treasury} />
9191
<ContractLink title="Metadata" address={addresses.metadata} />
92+
{addresses?.escrowDelegate && (
93+
<ContractLink title="Escrow Delegate" address={addresses.escrowDelegate} />
94+
)}
9295
</Flex>
9396
</Flex>
9497
</Box>

apps/web/src/modules/dao/stores/useDaoStore.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface DaoContractAddresses {
1616
auction?: AddressType
1717
treasury?: AddressType
1818
governor?: AddressType
19+
escrowDelegate?: AddressType
1920
}
2021

2122
export interface DaoContracts {
@@ -38,6 +39,7 @@ export const useDaoStore = create<DaoStoreProps>((set) => ({
3839
auction: undefined,
3940
treasury: undefined,
4041
governor: undefined,
42+
escrowDelegate: undefined,
4143
},
4244
setAddresses: (addresses: DaoContractAddresses) => set({ addresses }),
4345
}))

0 commit comments

Comments
 (0)