diff --git a/src/pages/daos/[daoAddress]/proposals/new/CustomProposalSubmitter.tsx b/src/pages/daos/[daoAddress]/proposals/new/CustomProposalSubmitter.tsx index ab18b12ba2..b8b6f8445d 100644 --- a/src/pages/daos/[daoAddress]/proposals/new/CustomProposalSubmitter.tsx +++ b/src/pages/daos/[daoAddress]/proposals/new/CustomProposalSubmitter.tsx @@ -1,8 +1,11 @@ import { Button } from '@chakra-ui/react'; +import { BigNumber } from 'ethers'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { mainnet, sepolia } from 'viem/chains'; import { DAO_ROUTES } from '../../../../../constants/routes'; import useSubmitProposal from '../../../../../hooks/DAO/proposal/useSubmitProposal'; +import { useNetworkConfig } from '../../../../../providers/NetworkConfig/NetworkConfigProvider'; import { ProposalExecuteData } from '../../../../../types'; import { encodeProposalTransaction } from '../../../../../utils'; @@ -13,6 +16,7 @@ export default function CustomProposalSubmitter({ values: any; daoAddress?: string; }) { + const { chainId } = useNetworkConfig(); const { submitProposal } = useSubmitProposal(); const navigate = useNavigate(); const { t } = useTranslation(['proposal', 'common', 'breadcrumbs']); @@ -23,58 +27,130 @@ export default function CustomProposalSubmitter({ } }; const handleSubmitProposal = () => { - const target = '0x7CC7e125d83A581ff438608490Cc0f7bDff79127'; // SablierV2LockupDynamic - const functionName = 'createWithMilestones'; - const signature = - 'function createWithMilestones(address,address,(address,uint40,bool,bool,address,uint128,(address,uint256),(uint128,uint64,uint40)[])[])'; - const data = [ - '0x7CC7e125d83A581ff438608490Cc0f7bDff79127', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - [ - [ - '0x36bD3044ab68f600f6d3e081056F34f2a58432c4', - 1711980240, - true, - false, - '0x36bD3044ab68f600f6d3e081056F34f2a58432c4', - 25000000000, - ['0x0000000000000000000000000000000000000000', 0], - [ - [0, '1000000000000000000', 1714572239], - [5000000000, '1000000000000000000', 1714572240], - [0, '1000000000000000000', 1717250639], - [5000000000, '1000000000000000000', 1717250640], - [0, '1000000000000000000', 1719842639], - [5000000000, '1000000000000000000', 1719842640], - [0, '1000000000000000000', 1722521039], - [5000000000, '1000000000000000000', 1722521040], - [0, '1000000000000000000', 1725199439], - [5000000000, '1000000000000000000', 1725199440], - ], - ], - ], - ]; - const { nonce, proposalMetadata } = values; - const calldata = encodeProposalTransaction(functionName, signature, data); - const proposalData: ProposalExecuteData = { - targets: [target], - values: [0], - calldatas: [calldata], - metaData: { - title: proposalMetadata.title, - description: proposalMetadata.description, - documentationUrl: proposalMetadata.documentationUrl, - }, - }; - submitProposal({ - proposalData, + const submitProposalBasicProps = { nonce, pendingToastMessage: t('proposalCreatePendingToastMessage'), successToastMessage: t('proposalCreateSuccessToastMessage'), failedToastMessage: t('proposalCreateFailureToastMessage'), successCallback, - }); + }; + const functionName = 'createWithMilestones'; + const signature = + 'function createWithMilestones(address,address,(address,address,uint128,uint40,bool,bool,(uint128,uint64,uint40)[], (address,uint256))[])'; + const approveAmount = BigNumber.from(10).pow(18).mul(10000000); // Approve spending for 1M tokens + + if (chainId === sepolia.id) { + const receiver = '0x2884b7Bf17Ca966bB2e4099bf384734a48885Df0'; + const sablierBatchAddress = '0xd2569DC4A58dfE85d807Dffb976dbC0a3bf0B0Fb'; // SablierV2Batch + const sablierLockupDynamicAddress = '0xc9940AD8F43aAD8e8f33A4D5dbBf0a8F7FF4429A'; // SablierV2LockupDynamic + const tokenAddress = '0xa60196673256ae375c8ce2bb6b404c07b6b4a56a'; // Test DAO token address + const startDate = Math.round(Date.now() / 1000); + console.log(startDate, startDate + (1000 * 60 * 10)) + const sablierBatchData = [ + sablierLockupDynamicAddress, // Address of LockupDynamic contract. Will be called by batch internally to create streams + tokenAddress, // Address of token to stream + [ + [ + daoAddress, // Tokens sender. This address will be able to cancel the stream + receiver, // Receiver of tokens through stream + approveAmount.div(5), // total amount of tokens sent + startDate, // Start time + true, // Cancelable - is it possible to cancel this stream + false, // Transferable - is receiver able to transfer receiving rights to someone else + [ + [0, '1000000000000000000', startDate], // First number represents amount of tokens denoted in units of token's decimals. Second number represents exponent, denoted as a fixed-point number. Third value is a Unix timestamp till when amount set in first value will be fully streamed + [approveAmount.div(5), '1000000000000000000', startDate + (60 * 10)], // For testing purpose - make the whole amount streamed at the start date + 10 minutes + ], // Array of segments, see explanation above. Additional explanation: https://github.com/sablier-labs/v2-core/blob/main/src/types/DataTypes.sol#L131-L140 + ['0x0000000000000000000000000000000000000000', 0], // Optional broker + ], + ], + ]; + const sablierBatchCalldata = encodeProposalTransaction( + functionName, + signature, + sablierBatchData, + ); + const tokenApproveData = [sablierBatchAddress, approveAmount]; // Batch contract will be sending token to itself, then routing that to the LockupDynamic contract and approving spend for that contract + const tokenCalldata = encodeProposalTransaction( + 'approve', + 'function approve(address,uint256)', + tokenApproveData, + ); + const proposalData: ProposalExecuteData = { + targets: [tokenAddress, sablierBatchAddress], + values: [0, 0], + calldatas: [tokenCalldata, sablierBatchCalldata], + metaData: { + title: proposalMetadata.title, + description: proposalMetadata.description, + documentationUrl: proposalMetadata.documentationUrl, + }, + }; + submitProposal({ + ...submitProposalBasicProps, + proposalData, + }); + } else if (chainId === mainnet.id) { + const receiver = daoAddress; // TODO: Change this. this will lead to stream tokens to itself + const sablierBatchAddress = '0xEa07DdBBeA804E7fe66b958329F8Fa5cDA95Bd55'; + const sablierLockupDynamicAddress = '0x7CC7e125d83A581ff438608490Cc0f7bDff79127'; // SablierV2LockupDynamic + const tokenAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; // Mainnet USDC address + const startTime = 1711980240; // Unix timestamp + const sablierBatchData = [ + sablierLockupDynamicAddress, + tokenAddress, + [ + [ + // Note - the order of inputs while using SablierV2Batch is different from order when using SablierV2LockupDynamic + daoAddress, // Tokens sender. This address will be able to cancel the stream + receiver, // Receiver of tokens through stream + approveAmount.div(5), // total amount of tokens sent + startTime, // Start time + true, // Cancelable - is it possible to cancel this stream + false, // Transferable - is receiver able to transfer receiving rights to someone else + [ + [0, '1000000000000000000', 1714572239], + [5000000000, '1000000000000000000', 1714572240], + [0, '1000000000000000000', 1717250639], + [5000000000, '1000000000000000000', 1717250640], + [0, '1000000000000000000', 1719842639], + [5000000000, '1000000000000000000', 1719842640], + [0, '1000000000000000000', 1722521039], + [5000000000, '1000000000000000000', 1722521040], + [0, '1000000000000000000', 1725199439], + [5000000000, '1000000000000000000', 1725199440], + ], + ['0x0000000000000000000000000000000000000000', 0], + ], + ], + ]; + const tokenApproveData = [sablierBatchAddress, approveAmount]; + const tokenCalldata = encodeProposalTransaction( + 'approve', + 'function approve(address,uint256)', + tokenApproveData, + ); + const sablierBatchCalldata = encodeProposalTransaction( + functionName, + signature, + sablierBatchData, + ); + const proposalData: ProposalExecuteData = { + targets: [tokenAddress, sablierBatchAddress], + values: [0, 0], + calldatas: [tokenCalldata, sablierBatchCalldata], + metaData: { + title: proposalMetadata.title, + description: proposalMetadata.description, + documentationUrl: proposalMetadata.documentationUrl, + }, + }; + submitProposal({ + ...submitProposalBasicProps, + proposalData, + }); + } }; return ; } diff --git a/src/pages/daos/[daoAddress]/proposals/new/index.tsx b/src/pages/daos/[daoAddress]/proposals/new/index.tsx index 1199b27e8e..50251f7de8 100644 --- a/src/pages/daos/[daoAddress]/proposals/new/index.tsx +++ b/src/pages/daos/[daoAddress]/proposals/new/index.tsx @@ -24,7 +24,7 @@ const templateAreaTwoCol = '"content details"'; const templateAreaSingleCol = `"content" "details"`; -const SHUTTER_DAO_ADDRESS = '0x1CEB4eB34A0AA9D439Dd0A4bab0f6830f715ddF0'; +const TARGET_DAO_ADDRESSES = ['0xbB4f275578d038e0D3C5A38E79dCdC0cf5b8c9Cd']; export default function ProposalCreatePage() { const { @@ -99,7 +99,7 @@ export default function ProposalCreatePage() { buttonClick={() => navigate(DAO_ROUTES.proposals.relative(daoAddress))} isButtonDisabled={pendingCreateTx} /> - {daoAddress === SHUTTER_DAO_ADDRESS && } + {TARGET_DAO_ADDRESSES.includes(daoAddress) && }