Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metaallocator support #115

Merged
merged 1 commit into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,118 changes: 2,229 additions & 1,889 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.12",
"@keyko-io/filecoin-verifier-tools": "^2.2.4",
"@glif/filecoin-address": "^3.0.4",
"@ledgerhq/hw-transport-webusb": "^6.27.19",
"@mui/material": "^5.15.14",
"@radix-ui/react-avatar": "^1.0.3",
Expand All @@ -36,6 +36,7 @@
"class-variance-authority": "^0.6.1",
"clsx": "^2.0.0",
"eslint-config-next": "^12.3.4",
"filecoin-verifier-tools": "^2.3.0",
"fuse.js": "^6.6.2",
"lucide-react": "^0.260.0",
"next": "13.5.6",
Expand All @@ -49,6 +50,7 @@
"tailwind-merge": "^1.14.0",
"tailwindcss": "3.3.2",
"tailwindcss-animate": "^1.0.6",
"viem": "^2.9.21",
"webpack-node-externals": "^3.0.0"
},
"devDependencies": {
Expand Down
10 changes: 8 additions & 2 deletions src/components/cards/AppInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const AppInfoCard: React.FC<ComponentProps> = ({
allowanceMultisig,
}) => {
const session = useSession()
const { allocators } = useAllocator()
const { allocators, setSelectedAllocator } = useAllocator()
const {
application,
isApiCalling,
Expand Down Expand Up @@ -233,12 +233,18 @@ const AppInfoCard: React.FC<ComponentProps> = ({

const ghUserName = session.data.user.githubUsername
const currentAllocator = allocators.find((e) => e.repo === repo)
setSelectedAllocator(currentAllocator)
if (
currentAllocator?.verifiers_gh_handles.includes(ghUserName.toLowerCase())
) {
setCurrentActorType(LDNActorType.Verifier)
}
}, [session.data?.user?.githubUsername, allocators, repo])
}, [
session.data?.user?.githubUsername,
allocators,
repo,
setSelectedAllocator,
])

/**
* Handles the mutation error event.
Expand Down
1 change: 0 additions & 1 deletion src/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
declare module '@zondax/ledger-filecoin'
declare module '@zondax/filecoin-signing-tools/js'
declare module '@keyko-io/filecoin-verifier-tools'
48 changes: 40 additions & 8 deletions src/hooks/useApplicationActions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import useWallet from '@/hooks/useWallet'
import { useAllocator } from '@/lib/AllocatorProvider'
import {
postAdditionalInfoRequest,
postApplicationApproval,
Expand All @@ -10,8 +11,8 @@ import {
postRequestKyc,
triggerSSA,
} from '@/lib/apiClient'
import { type Application, type RefillUnit } from '@/type'
import { useState } from 'react'
import { AllocatorTypeEnum, type Application, type RefillUnit } from '@/type'
import { useMemo, useState } from 'react'
import {
useMutation,
useQueryClient,
Expand Down Expand Up @@ -118,6 +119,22 @@ const useApplicationActions = (
accounts,
loadMoreAccounts,
} = useWallet()
const { selectedAllocator } = useAllocator()

const allocatorType: AllocatorTypeEnum = useMemo(() => {
if (
!!selectedAllocator &&
typeof selectedAllocator !== 'string' &&
selectedAllocator?.tooling
.split(', ')
.includes('smart_contract_allocator') &&
!!selectedAllocator?.address
) {
return AllocatorTypeEnum.CONTRACT
} else {
return AllocatorTypeEnum.DIRECT
}
}, [selectedAllocator])

/**
* Updates the application cache with the latest data from the API.
Expand Down Expand Up @@ -292,6 +309,13 @@ const useApplicationActions = (
},
)

const getClientAddress = (): string => {
return (
(process.env.NEXT_PUBLIC_MODE === 'development' ? 't' : 'f') +
initialApplication.Lifecycle['On Chain Address'].substring(1)
)
}

/**
* Mutation function to handle the triggering of an SSA.
* It makes an API call to trigger the SSA and updates the cache on success.
Expand Down Expand Up @@ -398,15 +422,21 @@ const useApplicationActions = (
const proposalTx = await getProposalTx(
clientAddress,
proposalAllocationAmount,
allocatorType,
)
if (proposalTx !== false) {
throw new Error('This datacap allocation is already proposed')
}

const messageCID = await sendProposal(
const messageCID = await sendProposal({
allocatorType,
contractAddress:
typeof selectedAllocator !== 'string'
? selectedAllocator?.address ?? ''
: '',
clientAddress,
proposalAllocationAmount,
)
})

if (messageCID == null) {
throw new Error(
Expand Down Expand Up @@ -451,16 +481,18 @@ const useApplicationActions = (
unknown
>(
async ({ requestId, userName }) => {
const clientAddress =
(process.env.NEXT_PUBLIC_MODE === 'development' ? 't' : 'f') +
initialApplication.Lifecycle['On Chain Address'].substring(1)
const clientAddress = getClientAddress()
const datacap = initialApplication['Allocation Requests'].find(
(alloc) => alloc.Active,
)?.['Allocation Amount']

if (datacap == null) throw new Error('No active allocation found')

const proposalTx = await getProposalTx(clientAddress, datacap)
const proposalTx = await getProposalTx(
clientAddress,
datacap,
allocatorType,
)

if (proposalTx === false) {
throw new Error(
Expand Down
110 changes: 94 additions & 16 deletions src/hooks/useWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { useState, useCallback } from 'react'
import { LedgerWallet } from '@/lib/wallet/LedgerWallet'
import { BurnerWallet } from '@/lib/wallet/BurnerWallet'
import { config } from '../config'
import { type IWallet } from '@/type'
import { AllocatorTypeEnum, type IWallet, type SendProposalProps } from '@/type'
import { anyToBytes } from '@/lib/utils'
import { newFromString } from '@glif/filecoin-address'
import { decodeFunctionData, encodeFunctionData, parseAbi } from 'viem'
import { type Hex } from 'viem/types/misc'

/**
* Registry that maps wallet class names to their respective classes.
Expand Down Expand Up @@ -33,8 +36,9 @@ interface WalletState {
getProposalTx: (
clientAddress: string,
datacap: string,
allocatorType: AllocatorTypeEnum,
) => Promise<string | boolean>
sendProposal: (clientAddress: string, datacap: string) => Promise<string>
sendProposal: (props: SendProposalProps) => Promise<string>
sendApproval: (txHash: string) => Promise<string>
sign: (message: string) => Promise<string>
initializeWallet: (multisigAddress?: string) => Promise<string[]>
Expand Down Expand Up @@ -181,6 +185,7 @@ const useWallet = (): WalletState => {
async (
clientAddress: string,
datacap: string,
allocatorType: AllocatorTypeEnum,
): Promise<string | boolean> => {
if (wallet == null) throw new Error('No wallet initialized.')
if (multisigAddress == null) throw new Error('Multisig address not set.')
Expand All @@ -195,16 +200,80 @@ const useWallet = (): WalletState => {
'An error with the lotus node occurred. Please reload. If the problem persists, contact support.',
)
}
const pendingForClient = pendingTxs?.filter(
(tx: any) =>
tx?.parsed?.params?.address === clientAddress &&
tx?.parsed?.params?.cap === BigInt(bytesDatacap),
)
let pendingForClient = null
if (allocatorType !== AllocatorTypeEnum.CONTRACT) {
pendingForClient = pendingTxs?.filter(
(tx: any) =>
tx?.parsed?.params?.address === clientAddress &&
tx?.parsed?.params?.cap === BigInt(bytesDatacap),
)
} else {
pendingForClient = pendingTxs?.filter((tx: any) => {
const abi = parseAbi([
'function addVerifiedClient(bytes clientAddress, uint256 amount)',
])

const paramsHex: string = tx.parsed.params.toString('hex')
const dataHex: Hex = `0x${paramsHex}`

const decodedData = decodeFunctionData({ abi, data: dataHex })
const [clientAddressData, amount] = decodedData.args
const address = newFromString(clientAddress)
const addressHex: Hex = `0x${Buffer.from(address.bytes).toString('hex')}`
return (
clientAddressData === addressHex && amount === BigInt(bytesDatacap)
)
})
}
return pendingForClient.length > 0 ? pendingForClient.at(-1) : false
},
[wallet, multisigAddress],
)

const sendProposalDirect = useCallback(
async (clientAddress: string, bytesDatacap: number) => {
if (wallet == null) throw new Error('No wallet initialized.')

return wallet.api.multisigVerifyClient(
multisigAddress,
clientAddress,
BigInt(bytesDatacap),
activeAccountIndex,
)
},
[wallet, multisigAddress, activeAccountIndex],
)

const sendProposalContract = useCallback(
async (
clientAddress: string,
bytesDatacap: number,
contractAddress: string,
) => {
if (wallet == null) throw new Error('No wallet initialized.')

const abi = parseAbi([
'function addVerifiedClient(bytes clientAddress, uint256 amount)',
])

const address = newFromString(clientAddress)
const addressHex: Hex = `0x${Buffer.from(address.bytes).toString('hex')}`
const calldataHex: Hex = encodeFunctionData({
abi,
args: [addressHex, BigInt(bytesDatacap)],
})
const calldata = Buffer.from(calldataHex.substring(2), 'hex')
return wallet.api.multisigEvmInvoke(
multisigAddress,
contractAddress,
calldata,
activeAccountIndex,
wallet,
)
},
[wallet, multisigAddress, activeAccountIndex],
)

/**
* Sends a proposal to a multisig wallet.
*
Expand All @@ -215,25 +284,34 @@ const useWallet = (): WalletState => {
* @throws {Error} - Throws an error if no wallet is initialized.
*/
const sendProposal = useCallback(
async (clientAddress: string, datacap: string): Promise<string> => {
async (props: SendProposalProps): Promise<string> => {
if (wallet == null) throw new Error('No wallet initialized.')
if (multisigAddress == null) throw new Error('Multisig address not set.')

const {
clientAddress,
proposalAllocationAmount,
allocatorType,
contractAddress,
} = props

setMessage('Sending proposal...')

const bytesDatacap = Math.floor(anyToBytes(datacap))
const messageCID = await wallet.api.multisigVerifyClient(
multisigAddress,
clientAddress,
BigInt(bytesDatacap),
activeAccountIndex,
)
const bytesDatacap = Math.floor(anyToBytes(proposalAllocationAmount))
const messageCID =
allocatorType === AllocatorTypeEnum.CONTRACT
? await sendProposalContract(
clientAddress,
bytesDatacap,
contractAddress,
)
: await sendProposalDirect(clientAddress, bytesDatacap)

setMessage(`Proposal sent correctly. CID: ${messageCID as string}`)

return messageCID
},
[wallet, multisigAddress, activeAccountIndex],
[wallet, multisigAddress, sendProposalContract, sendProposalDirect],
)

/**
Expand Down
4 changes: 2 additions & 2 deletions src/lib/wallet/BurnerWallet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { config } from '../../config'
import { VerifyAPI } from '@keyko-io/filecoin-verifier-tools'
import signer from '@zondax/filecoin-signing-tools/js'
import { VerifyAPI } from 'filecoin-verifier-tools'
import { config } from '../../config'
import { BaseWallet } from './BaseWallet'

/**
Expand Down
6 changes: 3 additions & 3 deletions src/lib/wallet/LedgerWallet.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { config } from '../../config'
import TransportWebUSB from '@ledgerhq/hw-transport-webusb'
import FilecoinApp from '@zondax/ledger-filecoin'
import signer from '@zondax/filecoin-signing-tools/js'
import { VerifyAPI } from '@keyko-io/filecoin-verifier-tools'
import FilecoinApp from '@zondax/ledger-filecoin'
import { VerifyAPI } from 'filecoin-verifier-tools'
import { config } from '../../config'
import { BaseWallet } from './BaseWallet'

/**
Expand Down
16 changes: 16 additions & 0 deletions src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ export interface Allocator {
repo: string
installation_id: string
multisig_address: string
multisig_threshold: number
allocation_amount_type: string | null
address: string
tooling: string
verifiers_gh_handles: string | string[]
}

Expand All @@ -145,3 +149,15 @@ export interface ByteConverterAutoscaleOptions {
// eslint-disable-next-line @typescript-eslint/ban-types
handler: (curDataFormat: string, isUppingDataFormat: boolean) => {}
}

export enum AllocatorTypeEnum {
DIRECT = 'direct',
CONTRACT = 'contract',
}

export interface SendProposalProps {
allocatorType: AllocatorTypeEnum
contractAddress: string
clientAddress: string
proposalAllocationAmount: string
}