diff --git a/app/(general)/integration/allo/layout.tsx b/app/(general)/integration/allo/layout.tsx new file mode 100644 index 00000000..99a6cb01 --- /dev/null +++ b/app/(general)/integration/allo/layout.tsx @@ -0,0 +1,72 @@ +'use client' +import { ReactNode } from 'react' + +import { motion } from 'framer-motion' +import Image from 'next/image' +import { usePathname } from 'next/navigation' +import Balancer from 'react-wrap-balancer' + +import { IsDarkTheme } from '@/components/shared/is-dark-theme' +import { IsLightTheme } from '@/components/shared/is-light-theme' +import { LinkComponent } from '@/components/shared/link-component' +import { FADE_DOWN_ANIMATION_VARIANTS } from '@/config/design' +import { turboIntegrations } from '@/data/turbo-integrations' +import { cn } from '@/lib/utils' + +const integrationData = turboIntegrations.allo +const registryPath = '/integration/allo/registry' +const alloPath = '/integration/allo/pool' + +export default function LayoutIntegration({ children }: { children: ReactNode }) { + const pathname = usePathname() + + return ( + <> +
+ + + Starter logo + + + Starter logo + + + {integrationData.name} + + + {integrationData.description} + + + + Documentation + + + + + + + + + + + +
+ {children} + + ) +} diff --git a/app/(general)/integration/allo/opengraph-image.tsx b/app/(general)/integration/allo/opengraph-image.tsx new file mode 100644 index 00000000..935b53ae --- /dev/null +++ b/app/(general)/integration/allo/opengraph-image.tsx @@ -0,0 +1,9 @@ +import { IntegrationOgImage } from '@/components/ui/social/og-image-integrations' + +export const runtime = 'edge' +export const size = { + width: 1200, + height: 630, +} + +export default IntegrationOgImage('allo') diff --git a/app/(general)/integration/allo/page.tsx b/app/(general)/integration/allo/page.tsx new file mode 100644 index 00000000..0a784207 --- /dev/null +++ b/app/(general)/integration/allo/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from 'next/navigation' + +export default function PageIntegration() { + redirect('/integration/allo/registry') +} diff --git a/app/(general)/integration/allo/pool/page.tsx b/app/(general)/integration/allo/pool/page.tsx new file mode 100644 index 00000000..974da58d --- /dev/null +++ b/app/(general)/integration/allo/pool/page.tsx @@ -0,0 +1,27 @@ +'use client' + +import { WalletConnect } from '@/components/blockchain/wallet-connect' +import { IsWalletConnected } from '@/components/shared/is-wallet-connected' +import { IsWalletDisconnected } from '@/components/shared/is-wallet-disconnected' +import { AlloAllocate, AlloCreatePool, AlloDistribute, AlloFundPool, AlloRegisterRecipient } from '@/integrations/allo/components/pool' + +export default function PageAllo() { + return ( +
+
+ +
+ + + + + +
+
+ + + +
+
+ ) +} diff --git a/app/(general)/integration/allo/registry/page.tsx b/app/(general)/integration/allo/registry/page.tsx new file mode 100644 index 00000000..8619509e --- /dev/null +++ b/app/(general)/integration/allo/registry/page.tsx @@ -0,0 +1,25 @@ +'use client' + +import { WalletConnect } from '@/components/blockchain/wallet-connect' +import { IsWalletConnected } from '@/components/shared/is-wallet-connected' +import { IsWalletDisconnected } from '@/components/shared/is-wallet-disconnected' +import { RegistryCreateProfile, RegistryUpdateProfileMetadata, RegistryUpdateProfileName } from '@/integrations/allo/components/registry' + +export default function PageRegistry() { + return ( +
+
+ +
+ + + +
+
+ + + +
+
+ ) +} diff --git a/app/(general)/integration/allo/twitter-image.tsx b/app/(general)/integration/allo/twitter-image.tsx new file mode 100644 index 00000000..dbca541e --- /dev/null +++ b/app/(general)/integration/allo/twitter-image.tsx @@ -0,0 +1,9 @@ +import Image from './opengraph-image' + +export const runtime = 'edge' +export const size = { + width: 1200, + height: 630, +} + +export default Image diff --git a/app/(general)/page.tsx b/app/(general)/page.tsx index c9b5045e..68faf98e 100644 --- a/app/(general)/page.tsx +++ b/app/(general)/page.tsx @@ -349,6 +349,21 @@ const features = [ ), }, + { + title: turboIntegrations.allo.name, + description: turboIntegrations.allo.description, + href: turboIntegrations.allo.href, + demo: ( +
+ + Allo logo + + + Allo logo + +
+ ), + }, { title: turboIntegrations.starter.name, description: turboIntegrations.starter.description, diff --git a/data/turbo-integrations.ts b/data/turbo-integrations.ts index 9dc5668a..ba59db7f 100644 --- a/data/turbo-integrations.ts +++ b/data/turbo-integrations.ts @@ -96,6 +96,14 @@ export const turboIntegrations = { imgLight: '/integrations/gelato-light.svg', imgDark: '/integrations/gelato-light.svg', }, + allo: { + name: 'Allo Protocol', + href: '/integration/allo', + url: 'https://docs.allo.gitcoin.co/', + description: 'Allo Protocol is an open-source protocol that enables groups to efficiently and transparently allocate pooled capital.', + imgLight: '/integrations/allo.jpeg', + imgDark: '/integrations/allo.jpeg', + }, starter: { name: 'Starter Template', href: '/integration/starter', diff --git a/integrations/allo/README.md b/integrations/allo/README.md new file mode 100644 index 00000000..86d925d5 --- /dev/null +++ b/integrations/allo/README.md @@ -0,0 +1,62 @@ +# Starter TurboETH Integration + +Welcome to the Starter TurboETH Integration! This folder serves as a blueprint for creating new integrations in TurboETH. If you're looking to contribute a new integration, simply copy this directory, and also the starter page located at `app/integration/starter`, to begin your development. + +## Creating a new integration + +Below are the steps to create a new integration. + +1. Copy the integration folder template from `/integrations/starter` and add your integration code, adhering to the file structure patterns evident in this folder. + +2. Duplicate the integration page from `/app/(general)/integration/starter` and populate it with your integration pages' code. + +3. Locate any API endpoints associated with your integration in the `/api` folder within the page folder of your integration. An example API endpoint can be found at `/app/(general)/integration/starter/api/hello-world/route.ts`. These API endpoints should follow the new [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) patterns of Next.js 13. + +4. Enter the data related to your integration in `/data/turbo-integrations.ts`. Here, add a new object with the name, description, image, and URL of your integration. + +5. Update the OG image configuration of your integration page in the `opengraph-image.tsx` file. Do this by replacing the argument of the `IntegrationOgImage` function with the object key of your integration used in the previous step. + +## Understanding the Starter template + +Each component of the Starter TurboETH template is designed to help streamline your development process: + +- **abis/**: Put your contract's ABI here. Each ABI should be in its own TypeScript file. + +- **client/**: Any client initialization for your chosen module or SDK should be placed here. + +- **components/**: This is the home for your React components. 'Read' components, which display data from a contract, and 'write' components, that send transactions, should all be placed here. + +- **hooks/**: Place your custom React hooks in this folder. These hooks are intended to manage state updates and encapsulate the logic for interacting with Ethereum contracts. + +- **starter-wagmi.ts**: This is a generated file from [wagmi-cli](https://wagmi.sh/cli/getting-started). It includes hooks for your contracts . + +- **index.ts**: Consider this as the entry point for your integration. It should export all the hooks, components, and utility functions that your integration provides. + +- **wagmi.config.ts**: This file should hold the wagmi-cli configuration for your integration, which includes settings like compiler version and optimization. + +- **README.md**: Here, you should document your integration. Explain its purpose, its use, and any important information a new developer or user should know. + +Each of these elements plays a crucial role in making your integration functional and accessible. + +## File Structure + +``` +integrations/starter +├─ abis/ +│ ├─ starter-abi.ts +├─ client/ +│ ├─ index.ts +├─ components/ +│ ├─ starter-header.tsx +├─ generated/ +│ ├─ starter-wagmi.ts +├─ hooks/ +│ ├─ use-starter.ts +├─ utils/ +│ ├─ types.ts +├─ index.ts +├─ README.md +├─ wagmi.config.ts +``` + +By using this template, you'll create well-organized and understandable integrations that are easy for you and others to navigate. Happy coding! diff --git a/integrations/allo/abis/allo-abi.ts b/integrations/allo/abis/allo-abi.ts new file mode 100644 index 00000000..649e48cf --- /dev/null +++ b/integrations/allo/abis/allo-abi.ts @@ -0,0 +1,1279 @@ +/** + * Allo ABI. + * source code: https://github.com/allo-protocol/allo-v2/blob/main/contracts/core/Allo.sol + */ +export const alloABI = [ + { + inputs: [], + name: 'AMOUNT_MISMATCH', + type: 'error', + }, + { + inputs: [], + name: 'INVALID_FEE', + type: 'error', + }, + { + inputs: [], + name: 'IS_APPROVED_STRATEGY', + type: 'error', + }, + { + inputs: [], + name: 'MISMATCH', + type: 'error', + }, + { + inputs: [], + name: 'NOT_APPROVED_STRATEGY', + type: 'error', + }, + { + inputs: [], + name: 'NOT_CONTRACT', + type: 'error', + }, + { + inputs: [], + name: 'NOT_ENOUGH_FUNDS', + type: 'error', + }, + { + inputs: [], + name: 'NewOwnerIsZeroAddress', + type: 'error', + }, + { + inputs: [], + name: 'NoHandoverRequest', + type: 'error', + }, + { + inputs: [], + name: 'UNAUTHORIZED', + type: 'error', + }, + { + inputs: [], + name: 'Unauthorized', + type: 'error', + }, + { + inputs: [], + name: 'ZERO_ADDRESS', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'poolId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'BaseFeePaid', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'baseFee', + type: 'uint256', + }, + ], + name: 'BaseFeeUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'feePercentage', + type: 'uint256', + }, + ], + name: 'FeePercentageUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint8', + name: 'version', + type: 'uint8', + }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pendingOwner', + type: 'address', + }, + ], + name: 'OwnershipHandoverCanceled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pendingOwner', + type: 'address', + }, + ], + name: 'OwnershipHandoverRequested', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'oldOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'poolId', + type: 'uint256', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'contract IStrategy', + name: 'strategy', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + indexed: false, + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + ], + name: 'PoolCreated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'poolId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'fee', + type: 'uint256', + }, + ], + name: 'PoolFunded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'poolId', + type: 'uint256', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + indexed: false, + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + ], + name: 'PoolMetadataUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'registry', + type: 'address', + }, + ], + name: 'RegistryUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'previousAdminRole', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'newAdminRole', + type: 'bytes32', + }, + ], + name: 'RoleAdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleRevoked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'StrategyApproved', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'StrategyRemoved', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'treasury', + type: 'address', + }, + ], + name: 'TreasuryUpdated', + type: 'event', + }, + { + inputs: [], + name: 'DEFAULT_ADMIN_ROLE', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'FEE_DENOMINATOR', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'NATIVE', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_manager', + type: 'address', + }, + ], + name: 'addPoolManager', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + ], + name: 'addToCloneableStrategies', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + ], + name: 'allocate', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256[]', + name: '_poolIds', + type: 'uint256[]', + }, + { + internalType: 'bytes[]', + name: '_datas', + type: 'bytes[]', + }, + ], + name: 'batchAllocate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256[]', + name: '_poolIds', + type: 'uint256[]', + }, + { + internalType: 'bytes[]', + name: '_data', + type: 'bytes[]', + }, + ], + name: 'batchRegisterRecipient', + outputs: [ + { + internalType: 'address[]', + name: '', + type: 'address[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'cancelOwnershipHandover', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pendingOwner', + type: 'address', + }, + ], + name: 'completeOwnershipHandover', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + { + internalType: 'bytes', + name: '_initStrategyData', + type: 'bytes', + }, + { + internalType: 'address', + name: '_token', + type: 'address', + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: '_metadata', + type: 'tuple', + }, + { + internalType: 'address[]', + name: '_managers', + type: 'address[]', + }, + ], + name: 'createPool', + outputs: [ + { + internalType: 'uint256', + name: 'poolId', + type: 'uint256', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + { + internalType: 'bytes', + name: '_initStrategyData', + type: 'bytes', + }, + { + internalType: 'address', + name: '_token', + type: 'address', + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: '_metadata', + type: 'tuple', + }, + { + internalType: 'address[]', + name: '_managers', + type: 'address[]', + }, + ], + name: 'createPoolWithCustomStrategy', + outputs: [ + { + internalType: 'uint256', + name: 'poolId', + type: 'uint256', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'address[]', + name: '_recipientIds', + type: 'address[]', + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + ], + name: 'distribute', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: '_amount', + type: 'uint256', + }, + ], + name: 'fundPool', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [], + name: 'getBaseFee', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getFeePercentage', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + ], + name: 'getPool', + outputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + { + internalType: 'contract IStrategy', + name: 'strategy', + type: 'address', + }, + { + internalType: 'address', + name: 'token', + type: 'address', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + { + internalType: 'bytes32', + name: 'managerRole', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'adminRole', + type: 'bytes32', + }, + ], + internalType: 'struct IAllo.Pool', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getRegistry', + outputs: [ + { + internalType: 'contract IRegistry', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + ], + name: 'getRoleAdmin', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + ], + name: 'getStrategy', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getTreasury', + outputs: [ + { + internalType: 'address payable', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'grantRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'hasRole', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_registry', + type: 'address', + }, + { + internalType: 'address payable', + name: '_treasury', + type: 'address', + }, + { + internalType: 'uint256', + name: '_feePercentage', + type: 'uint256', + }, + { + internalType: 'uint256', + name: '_baseFee', + type: 'uint256', + }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + ], + name: 'isCloneableStrategy', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_address', + type: 'address', + }, + ], + name: 'isPoolAdmin', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_address', + type: 'address', + }, + ], + name: 'isPoolManager', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: 'result', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'pendingOwner', + type: 'address', + }, + ], + name: 'ownershipHandoverExpiresAt', + outputs: [ + { + internalType: 'uint256', + name: 'result', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address', + }, + { + internalType: 'address', + name: '_recipient', + type: 'address', + }, + ], + name: 'recoverFunds', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'bytes', + name: '_data', + type: 'bytes', + }, + ], + name: 'registerRecipient', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_strategy', + type: 'address', + }, + ], + name: 'removeFromCloneableStrategies', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + internalType: 'address', + name: '_manager', + type: 'address', + }, + ], + name: 'removePoolManager', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'renounceOwnership', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'renounceRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'requestOwnershipHandover', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'revokeRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes4', + name: 'interfaceId', + type: 'bytes4', + }, + ], + name: 'supportsInterface', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_baseFee', + type: 'uint256', + }, + ], + name: 'updateBaseFee', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_feePercentage', + type: 'uint256', + }, + ], + name: 'updateFeePercentage', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_poolId', + type: 'uint256', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: '_metadata', + type: 'tuple', + }, + ], + name: 'updatePoolMetadata', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_registry', + type: 'address', + }, + ], + name: 'updateRegistry', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address payable', + name: '_treasury', + type: 'address', + }, + ], + name: 'updateTreasury', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/integrations/allo/abis/index.ts b/integrations/allo/abis/index.ts new file mode 100644 index 00000000..25f18526 --- /dev/null +++ b/integrations/allo/abis/index.ts @@ -0,0 +1,2 @@ +export { alloABI } from './allo-abi' +export { registryABI } from './registry-abi' diff --git a/integrations/allo/abis/registry-abi.ts b/integrations/allo/abis/registry-abi.ts new file mode 100644 index 00000000..eb2a8b01 --- /dev/null +++ b/integrations/allo/abis/registry-abi.ts @@ -0,0 +1,903 @@ +/** + * Registry ABI. + * source code: https://github.com/allo-protocol/allo-v2/blob/main/contracts/core/Registry.sol + */ +export const registryABI = [ + { + inputs: [ + { + internalType: 'address', + name: '_owner', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [], + name: 'AMOUNT_MISMATCH', + type: 'error', + }, + { + inputs: [], + name: 'NONCE_NOT_AVAILABLE', + type: 'error', + }, + { + inputs: [], + name: 'NOT_PENDING_OWNER', + type: 'error', + }, + { + inputs: [], + name: 'UNAUTHORIZED', + type: 'error', + }, + { + inputs: [], + name: 'ZERO_ADDRESS', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + indexed: false, + internalType: 'string', + name: 'name', + type: 'string', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + indexed: false, + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + { + indexed: false, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'anchor', + type: 'address', + }, + ], + name: 'ProfileCreated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + indexed: false, + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + ], + name: 'ProfileMetadataUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'string', + name: 'name', + type: 'string', + }, + { + indexed: false, + internalType: 'address', + name: 'anchor', + type: 'address', + }, + ], + name: 'ProfileNameUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'ProfileOwnerUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'address', + name: 'pendingOwner', + type: 'address', + }, + ], + name: 'ProfilePendingOwnerUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'previousAdminRole', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'newAdminRole', + type: 'bytes32', + }, + ], + name: 'RoleAdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'RoleRevoked', + type: 'event', + }, + { + inputs: [], + name: 'ALLO_OWNER', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'DEFAULT_ADMIN_ROLE', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'NATIVE', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + ], + name: 'acceptProfileOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address[]', + name: '_members', + type: 'address[]', + }, + ], + name: 'addMembers', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'anchorToProfileId', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '_nonce', + type: 'uint256', + }, + { + internalType: 'string', + name: '_name', + type: 'string', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: '_metadata', + type: 'tuple', + }, + { + internalType: 'address', + name: '_owner', + type: 'address', + }, + { + internalType: 'address[]', + name: '_members', + type: 'address[]', + }, + ], + name: 'createProfile', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_anchor', + type: 'address', + }, + ], + name: 'getProfileByAnchor', + outputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'string', + name: 'name', + type: 'string', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'anchor', + type: 'address', + }, + ], + internalType: 'struct IRegistry.Profile', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'profileId', + type: 'bytes32', + }, + ], + name: 'getProfileById', + outputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'string', + name: 'name', + type: 'string', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'anchor', + type: 'address', + }, + ], + internalType: 'struct IRegistry.Profile', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + ], + name: 'getRoleAdmin', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'grantRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'hasRole', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address', + name: '_member', + type: 'address', + }, + ], + name: 'isMemberOfProfile', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address', + name: '_owner', + type: 'address', + }, + ], + name: 'isOwnerOfProfile', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address', + name: '_account', + type: 'address', + }, + ], + name: 'isOwnerOrMemberOfProfile', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + name: 'profileIdToPendingOwner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + name: 'profilesById', + outputs: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'string', + name: 'name', + type: 'string', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: 'metadata', + type: 'tuple', + }, + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'anchor', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address', + }, + { + internalType: 'address', + name: '_recipient', + type: 'address', + }, + ], + name: 'recoverFunds', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address[]', + name: '_members', + type: 'address[]', + }, + ], + name: 'removeMembers', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'renounceRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'role', + type: 'bytes32', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'revokeRole', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes4', + name: 'interfaceId', + type: 'bytes4', + }, + ], + name: 'supportsInterface', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + components: [ + { + internalType: 'uint256', + name: 'protocol', + type: 'uint256', + }, + { + internalType: 'string', + name: 'pointer', + type: 'string', + }, + ], + internalType: 'struct Metadata', + name: '_metadata', + type: 'tuple', + }, + ], + name: 'updateProfileMetadata', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'string', + name: '_name', + type: 'string', + }, + ], + name: 'updateProfileName', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '_profileId', + type: 'bytes32', + }, + { + internalType: 'address', + name: '_pendingOwner', + type: 'address', + }, + ], + name: 'updateProfilePendingOwner', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/integrations/allo/components/pool/allo-allocate.tsx b/integrations/allo/components/pool/allo-allocate.tsx new file mode 100644 index 00000000..6e9814e6 --- /dev/null +++ b/integrations/allo/components/pool/allo-allocate.tsx @@ -0,0 +1,68 @@ +import { useForm } from 'react-hook-form' +import { useDebounce } from 'usehooks-ts' +import type { Address, BaseError } from 'viem' +import { useWaitForTransaction } from 'wagmi' + +import { ContractWriteButton } from '@/components/blockchain/contract-write-button' +import { TransactionStatus } from '@/components/blockchain/transaction-status' + +import { useAlloAllocate, usePrepareAlloAllocate } from '../../generated/allo-wagmi' +import { ALLO_ADDRESS } from '../../utils/constants' + +interface FormSchema { + poolId: string + strategyData: Address +} + +export function AlloAllocate() { + const { register, watch, handleSubmit, reset } = useForm({ + defaultValues: { + strategyData: '0x', + }, + }) + + const poolId = useDebounce(watch('poolId'), 500) + const strategyData = useDebounce(watch('strategyData'), 500) + + const isValidInput = Number(poolId) && typeof strategyData === 'string' + + const { config, error, isError } = usePrepareAlloAllocate({ + address: ALLO_ADDRESS, + args: isValidInput ? [BigInt(poolId), strategyData] : undefined, + value: BigInt(0), + enabled: Boolean(isValidInput), + }) + + const { data, write, isLoading: isLoadingWrite } = useAlloAllocate(config) + + const { isLoading: isLoadingTx, isSuccess } = useWaitForTransaction({ + hash: data?.hash, + onSuccess: () => { + reset() + }, + }) + + const onSubmit = () => { + write?.() + } + + return ( +
+
+ + + + + + Allocate + + +
+
+

Allocate

+

Allocate funds with Allo.

+
+ +
+ ) +} diff --git a/integrations/allo/components/pool/allo-create-pool.tsx b/integrations/allo/components/pool/allo-create-pool.tsx new file mode 100644 index 00000000..ff90fe70 --- /dev/null +++ b/integrations/allo/components/pool/allo-create-pool.tsx @@ -0,0 +1,112 @@ +import { useMemo } from 'react' + +import { useForm } from 'react-hook-form' +import { useDebounce } from 'usehooks-ts' +import type { Address, BaseError } from 'viem' +import { useWaitForTransaction } from 'wagmi' + +import { ContractWriteButton } from '@/components/blockchain/contract-write-button' +import { TransactionStatus } from '@/components/blockchain/transaction-status' + +import { useAlloCreatePool, usePrepareAlloCreatePool } from '../../generated/allo-wagmi' +import { parseAddresses } from '../../utils' +import { ALLO_ADDRESS } from '../../utils/constants' + +interface FormSchema { + profileId: Address + strategy: Address + initStrategyData: Address + token: Address + amount: string + protocol: string + pointer: string + members: string +} + +export function AlloCreatePool() { + const { register, watch, handleSubmit, reset } = useForm({ + defaultValues: { + protocol: '1', + initStrategyData: '0x', + amount: '0', + }, + }) + + const profileId = useDebounce(watch('profileId'), 500) + const strategy = useDebounce(watch('strategy'), 500) + const initStrategyData = useDebounce(watch('initStrategyData'), 500) + const token = useDebounce(watch('token'), 500) + const amount = useDebounce(watch('amount'), 500) + const protocol = useDebounce(watch('protocol'), 500) + const pointer = useDebounce(watch('pointer'), 500) + const members = useDebounce(watch('members'), 500) + + const formattedMembers = useMemo(() => parseAddresses(members), [members]) + + const isValidInput = profileId && strategy && initStrategyData && token && pointer && Number(protocol) !== 0 && formattedMembers.length > 0 + + const { config, error, isError } = usePrepareAlloCreatePool({ + address: ALLO_ADDRESS, + args: isValidInput + ? [ + profileId, + strategy, + initStrategyData, + token, + BigInt(amount), + { + protocol: BigInt(Number(protocol)), + pointer, + }, + formattedMembers, + ] + : undefined, + value: BigInt(0), + enabled: Boolean(isValidInput), + }) + + const { data, write, isLoading: isLoadingWrite } = useAlloCreatePool(config) + + const { isLoading: isLoadingTx, isSuccess } = useWaitForTransaction({ + hash: data?.hash, + onSuccess: () => { + reset() + }, + }) + + const onSubmit = () => { + write?.() + } + + return ( +
+
+ + + + + + + + + + + + + + + +