diff --git a/package-lock.json b/package-lock.json index 9d627f09f..1230a5942 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "@radix-ui/react-tooltip": "^1.1.8", "@rainbow-me/rainbowkit": "2.2.8", "@react-spring/web": "^9.7.1", - "@reserve-protocol/dtf-rebalance-lib": "2.6.3", + "@reserve-protocol/dtf-rebalance-lib": "3.1.0", "@reserve-protocol/react-zapper": "^1.5.6", "@reserve-protocol/rtokens": "^1.1.23", "@sentry/react": "^9.16.0", @@ -10542,9 +10542,10 @@ } }, "node_modules/@reserve-protocol/dtf-rebalance-lib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/@reserve-protocol/dtf-rebalance-lib/-/dtf-rebalance-lib-2.6.3.tgz", - "integrity": "sha512-/c/SQ/VeyIUV9OA1pfnDILk6yIsKYnKnzPY5tcnwfeAarZPQdK370F4t9uGUv8LGuCkn+dEGmpifhmNiyMueCw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@reserve-protocol/dtf-rebalance-lib/-/dtf-rebalance-lib-3.1.0.tgz", + "integrity": "sha512-qG+4aWSCpbC0wITNZwWVoKNEfbbxIIM1ftW7ve+fRuUIrwuF+fmchPIjIesZOEMDESVvmnnrywHTjQZ14JqfBw==", + "license": "BlueOak-1.0.0", "peerDependencies": { "decimal.js-light": "^2.5.1" } diff --git a/package.json b/package.json index 16fa38cea..dc84e8a36 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@radix-ui/react-tooltip": "^1.1.8", "@rainbow-me/rainbowkit": "2.2.8", "@react-spring/web": "^9.7.1", - "@reserve-protocol/dtf-rebalance-lib": "2.6.3", + "@reserve-protocol/dtf-rebalance-lib": "3.1.0", "@reserve-protocol/react-zapper": "^1.5.6", "@reserve-protocol/rtokens": "^1.1.23", "@sentry/react": "^9.16.0", diff --git a/src/abis/dtf-index-abi-v1.ts b/src/abis/dtf-index-abi-v1.ts new file mode 100644 index 000000000..a1203e2d1 --- /dev/null +++ b/src/abis/dtf-index-abi-v1.ts @@ -0,0 +1,2421 @@ +export default [ + { + type: 'constructor', + inputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'AUCTION_APPROVER', + inputs: [], + outputs: [ + { + name: '', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'AUCTION_LAUNCHER', + inputs: [], + outputs: [ + { + name: '', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'BRAND_MANAGER', + inputs: [], + outputs: [ + { + name: '', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'DEFAULT_ADMIN_ROLE', + inputs: [], + outputs: [ + { + name: '', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'addToBasket', + inputs: [ + { + name: 'token', + type: 'address', + internalType: 'contract IERC20', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'allowance', + inputs: [ + { + name: 'owner', + type: 'address', + internalType: 'address', + }, + { + name: 'spender', + type: 'address', + internalType: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'approve', + inputs: [ + { + name: 'spender', + type: 'address', + internalType: 'address', + }, + { + name: 'value', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + internalType: 'bool', + }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'approveAuction', + inputs: [ + { + name: 'sell', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'buy', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'sellLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'buyLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'prices', + type: 'tuple', + internalType: 'struct IFolio.Prices', + components: [ + { + name: 'start', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'end', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'ttl', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'approveAuction', + inputs: [ + { name: 'sell', type: 'address', internalType: 'contract IERC20' }, + { name: 'buy', type: 'address', internalType: 'contract IERC20' }, + { + name: 'sellLimit', + type: 'tuple', + internalType: 'struct IFolio.BasketRange', + components: [ + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], + }, + { + name: 'buyLimit', + type: 'tuple', + internalType: 'struct IFolio.BasketRange', + components: [ + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], + }, + { + name: 'prices', + type: 'tuple', + internalType: 'struct IFolio.Prices', + components: [ + { name: 'start', type: 'uint256', internalType: 'uint256' }, + { name: 'end', type: 'uint256', internalType: 'uint256' }, + ], + }, + { name: 'ttl', type: 'uint256', internalType: 'uint256' }, + { name: 'runs', type: 'uint256', internalType: 'uint256' }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'auctionDelay', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'auctionLength', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'auctions', + inputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: 'id', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'sell', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'buy', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'sellLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'buyLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'prices', + type: 'tuple', + internalType: 'struct IFolio.Prices', + components: [ + { + name: 'start', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'end', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'availableAt', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'launchTimeout', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'start', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'end', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'k', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'balanceOf', + inputs: [ + { + name: 'account', + type: 'address', + internalType: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'bid', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'sellAmount', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'maxBuyAmount', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'withCallback', + type: 'bool', + internalType: 'bool', + }, + { + name: 'data', + type: 'bytes', + internalType: 'bytes', + }, + ], + outputs: [ + { + name: 'boughtAmt', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'buyEnds', + inputs: [ + { + name: '', + type: 'address', + internalType: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'closeAuction', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'daoFeeRegistry', + inputs: [], + outputs: [ + { + name: '', + type: 'address', + internalType: 'contract IFolioDAOFeeRegistry', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'daoPendingFeeShares', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'decimals', + inputs: [], + outputs: [ + { + name: '', + type: 'uint8', + internalType: 'uint8', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'distributeFees', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'feeRecipients', + inputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: 'recipient', + type: 'address', + internalType: 'address', + }, + { + name: 'portion', + type: 'uint96', + internalType: 'uint96', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'feeRecipientsPendingFeeShares', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'folio', + inputs: [], + outputs: [ + { + name: '_assets', + type: 'address[]', + internalType: 'address[]', + }, + { + name: '_amounts', + type: 'uint256[]', + internalType: 'uint256[]', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getBid', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'timestamp', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'sellAmount', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: 'bidAmount', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getPendingFeeShares', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getPrice', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'timestamp', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleAdmin', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + outputs: [ + { + name: '', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleMember', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + { + name: 'index', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'address', + internalType: 'address', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleMemberCount', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleMembers', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + outputs: [ + { + name: '', + type: 'address[]', + internalType: 'address[]', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'grantRole', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + { + name: 'account', + type: 'address', + internalType: 'address', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'hasRole', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + { + name: 'account', + type: 'address', + internalType: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + internalType: 'bool', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'initialize', + inputs: [ + { + name: '_basicDetails', + type: 'tuple', + internalType: 'struct IFolio.FolioBasicDetails', + components: [ + { + name: 'name', + type: 'string', + internalType: 'string', + }, + { + name: 'symbol', + type: 'string', + internalType: 'string', + }, + { + name: 'assets', + type: 'address[]', + internalType: 'address[]', + }, + { + name: 'amounts', + type: 'uint256[]', + internalType: 'uint256[]', + }, + { + name: 'initialShares', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: '_additionalDetails', + type: 'tuple', + internalType: 'struct IFolio.FolioAdditionalDetails', + components: [ + { + name: 'auctionDelay', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'auctionLength', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'feeRecipients', + type: 'tuple[]', + internalType: 'struct IFolio.FeeRecipient[]', + components: [ + { + name: 'recipient', + type: 'address', + internalType: 'address', + }, + { + name: 'portion', + type: 'uint96', + internalType: 'uint96', + }, + ], + }, + { + name: 'tvlFee', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'mintFee', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'mandate', + type: 'string', + internalType: 'string', + }, + ], + }, + { + name: '_creator', + type: 'address', + internalType: 'address', + }, + { + name: '_daoFeeRegistry', + type: 'address', + internalType: 'address', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'isKilled', + inputs: [], + outputs: [ + { + name: '', + type: 'bool', + internalType: 'bool', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'killFolio', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'lastPoke', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'lot', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'timestamp', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: 'sellAmount', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'mandate', + inputs: [], + outputs: [ + { + name: '', + type: 'string', + internalType: 'string', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'mint', + inputs: [ + { + name: 'shares', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'receiver', + type: 'address', + internalType: 'address', + }, + ], + outputs: [ + { + name: '_assets', + type: 'address[]', + internalType: 'address[]', + }, + { + name: '_amounts', + type: 'uint256[]', + internalType: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'mintFee', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'name', + inputs: [], + outputs: [ + { + name: '', + type: 'string', + internalType: 'string', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'nextAuctionId', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'openAuction', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'sellLimit', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'buyLimit', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'startPrice', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'endPrice', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + + { + type: 'function', + name: 'openAuctionPermissionlessly', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'poke', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'redeem', + inputs: [ + { + name: 'shares', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'receiver', + type: 'address', + internalType: 'address', + }, + { + name: 'assets', + type: 'address[]', + internalType: 'address[]', + }, + { + name: 'minAmountsOut', + type: 'uint256[]', + internalType: 'uint256[]', + }, + ], + outputs: [ + { + name: '_amounts', + type: 'uint256[]', + internalType: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'removeFromBasket', + inputs: [ + { + name: 'token', + type: 'address', + internalType: 'contract IERC20', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'renounceRole', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + { + name: 'callerConfirmation', + type: 'address', + internalType: 'address', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'revokeRole', + inputs: [ + { + name: 'role', + type: 'bytes32', + internalType: 'bytes32', + }, + { + name: 'account', + type: 'address', + internalType: 'address', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'sellEnds', + inputs: [ + { + name: '', + type: 'address', + internalType: 'address', + }, + ], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'setAuctionDelay', + inputs: [ + { + name: '_newDelay', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setAuctionLength', + inputs: [ + { + name: '_newLength', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setFeeRecipients', + inputs: [ + { + name: '_newRecipients', + type: 'tuple[]', + internalType: 'struct IFolio.FeeRecipient[]', + components: [ + { + name: 'recipient', + type: 'address', + internalType: 'address', + }, + { + name: 'portion', + type: 'uint96', + internalType: 'uint96', + }, + ], + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setMandate', + inputs: [ + { + name: '_newMandate', + type: 'string', + internalType: 'string', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setMintFee', + inputs: [ + { + name: '_newFee', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setTVLFee', + inputs: [ + { + name: '_newFee', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'supportsInterface', + inputs: [ + { + name: 'interfaceId', + type: 'bytes4', + internalType: 'bytes4', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + internalType: 'bool', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'symbol', + inputs: [], + outputs: [ + { + name: '', + type: 'string', + internalType: 'string', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'toAssets', + inputs: [ + { + name: 'shares', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'rounding', + type: 'uint8', + internalType: 'enum Math.Rounding', + }, + ], + outputs: [ + { + name: '_assets', + type: 'address[]', + internalType: 'address[]', + }, + { + name: '_amounts', + type: 'uint256[]', + internalType: 'uint256[]', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'totalAssets', + inputs: [], + outputs: [ + { + name: '_assets', + type: 'address[]', + internalType: 'address[]', + }, + { + name: '_amounts', + type: 'uint256[]', + internalType: 'uint256[]', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'totalSupply', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'transfer', + inputs: [ + { + name: 'to', + type: 'address', + internalType: 'address', + }, + { + name: 'value', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + internalType: 'bool', + }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'transferFrom', + inputs: [ + { + name: 'from', + type: 'address', + internalType: 'address', + }, + { + name: 'to', + type: 'address', + internalType: 'address', + }, + { + name: 'value', + type: 'uint256', + internalType: 'uint256', + }, + ], + outputs: [ + { + name: '', + type: 'bool', + internalType: 'bool', + }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'tvlFee', + inputs: [], + outputs: [ + { + name: '', + type: 'uint256', + internalType: 'uint256', + }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'version', + inputs: [], + outputs: [ + { + name: '', + type: 'string', + internalType: 'string', + }, + ], + stateMutability: 'pure', + }, + { + type: 'event', + name: 'Approval', + inputs: [ + { + name: 'owner', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'spender', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'value', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'AuctionApproved', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + indexed: true, + internalType: 'uint256', + }, + { + name: 'from', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'to', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'auction', + type: 'tuple', + indexed: false, + internalType: 'struct IFolio.Auction', + components: [ + { + name: 'id', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'sell', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'buy', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'sellLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'buyLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'prices', + type: 'tuple', + internalType: 'struct IFolio.Prices', + components: [ + { + name: 'start', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'end', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'availableAt', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'launchTimeout', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'start', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'end', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'k', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'AuctionBid', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + indexed: true, + internalType: 'uint256', + }, + { + name: 'sellAmount', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + { + name: 'buyAmount', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'AuctionClosed', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + indexed: true, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'AuctionDelaySet', + inputs: [ + { + name: 'newAuctionDelay', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'AuctionLengthSet', + inputs: [ + { + name: 'newAuctionLength', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'AuctionOpened', + inputs: [ + { + name: 'auctionId', + type: 'uint256', + indexed: true, + internalType: 'uint256', + }, + { + name: 'auction', + type: 'tuple', + indexed: false, + internalType: 'struct IFolio.Auction', + components: [ + { + name: 'id', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'sell', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'buy', + type: 'address', + internalType: 'contract IERC20', + }, + { + name: 'sellLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'buyLimit', + type: 'tuple', + internalType: 'struct IFolio.Range', + components: [ + { + name: 'spot', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'low', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'high', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'prices', + type: 'tuple', + internalType: 'struct IFolio.Prices', + components: [ + { + name: 'start', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'end', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + name: 'availableAt', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'launchTimeout', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'start', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'end', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'k', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'BasketTokenAdded', + inputs: [ + { + name: 'token', + type: 'address', + indexed: true, + internalType: 'address', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'BasketTokenRemoved', + inputs: [ + { + name: 'token', + type: 'address', + indexed: true, + internalType: 'address', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'FeeRecipientSet', + inputs: [ + { + name: 'recipient', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'portion', + type: 'uint96', + indexed: false, + internalType: 'uint96', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'FolioFeePaid', + inputs: [ + { + name: 'recipient', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'amount', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'FolioKilled', + inputs: [], + anonymous: false, + }, + { + type: 'event', + name: 'Initialized', + inputs: [ + { + name: 'version', + type: 'uint64', + indexed: false, + internalType: 'uint64', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'MandateSet', + inputs: [ + { + name: 'newMandate', + type: 'string', + indexed: false, + internalType: 'string', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'MintFeeSet', + inputs: [ + { + name: 'newFee', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'ProtocolFeePaid', + inputs: [ + { + name: 'recipient', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'amount', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'RoleAdminChanged', + inputs: [ + { + name: 'role', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', + }, + { + name: 'previousAdminRole', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', + }, + { + name: 'newAdminRole', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'RoleGranted', + inputs: [ + { + name: 'role', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', + }, + { + name: 'account', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'sender', + type: 'address', + indexed: true, + internalType: 'address', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'RoleRevoked', + inputs: [ + { + name: 'role', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', + }, + { + name: 'account', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'sender', + type: 'address', + indexed: true, + internalType: 'address', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'TVLFeeSet', + inputs: [ + { + name: 'newFee', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + { + name: 'feeAnnually', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'Transfer', + inputs: [ + { + name: 'from', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'to', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'value', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, + ], + anonymous: false, + }, + { + type: 'error', + name: 'AccessControlBadConfirmation', + inputs: [], + }, + { + type: 'error', + name: 'AccessControlUnauthorizedAccount', + inputs: [ + { + name: 'account', + type: 'address', + internalType: 'address', + }, + { + name: 'neededRole', + type: 'bytes32', + internalType: 'bytes32', + }, + ], + }, + { + type: 'error', + name: 'ERC20InsufficientAllowance', + inputs: [ + { + name: 'spender', + type: 'address', + internalType: 'address', + }, + { + name: 'allowance', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'needed', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + type: 'error', + name: 'ERC20InsufficientBalance', + inputs: [ + { + name: 'sender', + type: 'address', + internalType: 'address', + }, + { + name: 'balance', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'needed', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + type: 'error', + name: 'ERC20InvalidApprover', + inputs: [ + { + name: 'approver', + type: 'address', + internalType: 'address', + }, + ], + }, + { + type: 'error', + name: 'ERC20InvalidReceiver', + inputs: [ + { + name: 'receiver', + type: 'address', + internalType: 'address', + }, + ], + }, + { + type: 'error', + name: 'ERC20InvalidSender', + inputs: [ + { + name: 'sender', + type: 'address', + internalType: 'address', + }, + ], + }, + { + type: 'error', + name: 'ERC20InvalidSpender', + inputs: [ + { + name: 'spender', + type: 'address', + internalType: 'address', + }, + ], + }, + { + type: 'error', + name: 'Folio__AuctionCannotBeOpened', + inputs: [], + }, + { + type: 'error', + name: 'Folio__AuctionCannotBeOpenedPermissionlesslyYet', + inputs: [], + }, + { + type: 'error', + name: 'Folio__AuctionCollision', + inputs: [], + }, + { + type: 'error', + name: 'Folio__AuctionNotOngoing', + inputs: [], + }, + { + type: 'error', + name: 'Folio__AuctionTimeout', + inputs: [], + }, + { + type: 'error', + name: 'Folio__BadFeeTotal', + inputs: [], + }, + { + type: 'error', + name: 'Folio__BasketModificationFailed', + inputs: [], + }, + { + type: 'error', + name: 'Folio__EmptyAssets', + inputs: [], + }, + { + type: 'error', + name: 'Folio__ExcessiveBid', + inputs: [], + }, + { + type: 'error', + name: 'Folio__FeeRecipientInvalidAddress', + inputs: [], + }, + { + type: 'error', + name: 'Folio__FeeRecipientInvalidFeeShare', + inputs: [], + }, + { + type: 'error', + name: 'Folio__FolioKilled', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InsufficientBalance', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InsufficientBid', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidArrayLengths', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidAsset', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidAssetAmount', + inputs: [ + { + name: 'asset', + type: 'address', + internalType: 'address', + }, + ], + }, + { + type: 'error', + name: 'Folio__InvalidAuctionDelay', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidAuctionLength', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidAuctionTTL', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidAuctionTokens', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidBuyLimit', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidPrices', + inputs: [], + }, + { + type: 'error', + name: 'Folio__InvalidSellLimit', + inputs: [], + }, + { + type: 'error', + name: 'Folio__MintFeeTooHigh', + inputs: [], + }, + { + type: 'error', + name: 'Folio__SlippageExceeded', + inputs: [], + }, + { + type: 'error', + name: 'Folio__TVLFeeTooHigh', + inputs: [], + }, + { + type: 'error', + name: 'Folio__TVLFeeTooLow', + inputs: [], + }, + { + type: 'error', + name: 'Folio__TooManyFeeRecipients', + inputs: [], + }, + { + type: 'error', + name: 'Folio__Unauthorized', + inputs: [], + }, + { + type: 'error', + name: 'Folio__ZeroInitialShares', + inputs: [], + }, + { + type: 'error', + name: 'InvalidInitialization', + inputs: [], + }, + { + type: 'error', + name: 'NotInitializing', + inputs: [], + }, + { + type: 'error', + name: 'PRBMath_MulDiv18_Overflow', + inputs: [ + { + name: 'x', + type: 'uint256', + internalType: 'uint256', + }, + { + name: 'y', + type: 'uint256', + internalType: 'uint256', + }, + ], + }, + { + type: 'error', + name: 'PRBMath_SD59x18_Exp2_InputTooBig', + inputs: [ + { + name: 'x', + type: 'int256', + internalType: 'SD59x18', + }, + ], + }, + { + type: 'error', + name: 'PRBMath_SD59x18_Exp_InputTooBig', + inputs: [ + { + name: 'x', + type: 'int256', + internalType: 'SD59x18', + }, + ], + }, + { + type: 'error', + name: 'PRBMath_SD59x18_IntoUint256_Underflow', + inputs: [ + { + name: 'x', + type: 'int256', + internalType: 'SD59x18', + }, + ], + }, + { + type: 'error', + name: 'PRBMath_UD60x18_Exp2_InputTooBig', + inputs: [ + { + name: 'x', + type: 'uint256', + internalType: 'UD60x18', + }, + ], + }, + { + type: 'error', + name: 'PRBMath_UD60x18_Log_InputTooSmall', + inputs: [ + { + name: 'x', + type: 'uint256', + internalType: 'UD60x18', + }, + ], + }, + { + type: 'error', + name: 'ReentrancyGuardReentrantCall', + inputs: [], + }, + { + type: 'error', + name: 'SafeERC20FailedOperation', + inputs: [ + { + name: 'token', + type: 'address', + internalType: 'address', + }, + ], + }, + { + inputs: [ + { internalType: 'address[]', name: 'tokens', type: 'address[]' }, + { + components: [ + { internalType: 'uint256', name: 'low', type: 'uint256' }, + { internalType: 'uint256', name: 'spot', type: 'uint256' }, + { internalType: 'uint256', name: 'high', type: 'uint256' }, + ], + internalType: 'struct IFolio.WeightRange[]', + name: 'weights', + type: 'tuple[]', + }, + { + components: [ + { internalType: 'uint256', name: 'low', type: 'uint256' }, + { internalType: 'uint256', name: 'high', type: 'uint256' }, + ], + internalType: 'struct IFolio.PriceRange[]', + name: 'prices', + type: 'tuple[]', + }, + { + components: [ + { internalType: 'uint256', name: 'low', type: 'uint256' }, + { internalType: 'uint256', name: 'spot', type: 'uint256' }, + { internalType: 'uint256', name: 'high', type: 'uint256' }, + ], + internalType: 'struct IFolio.RebalanceLimits', + name: 'limits', + type: 'tuple', + }, + { + internalType: 'uint256', + name: 'auctionLauncherWindow', + type: 'uint256', + }, + { internalType: 'uint256', name: 'ttl', type: 'uint256' }, + ], + name: 'startRebalance', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const diff --git a/src/abis/dtf-index-abi.ts b/src/abis/dtf-index-abi.ts index a1203e2d1..a09a5c128 100644 --- a/src/abis/dtf-index-abi.ts +++ b/src/abis/dtf-index-abi.ts @@ -1,2421 +1,1481 @@ export default [ + { type: 'constructor', inputs: [], stateMutability: 'nonpayable' }, { - type: 'constructor', + type: 'function', + name: 'DEFAULT_ADMIN_ROLE', inputs: [], + outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'addToBasket', + inputs: [ + { name: 'token', type: 'address', internalType: 'contract IERC20' }, + ], + outputs: [], stateMutability: 'nonpayable', }, { type: 'function', - name: 'AUCTION_APPROVER', - inputs: [], - outputs: [ - { - name: '', - type: 'bytes32', - internalType: 'bytes32', - }, + name: 'allowance', + inputs: [ + { name: 'owner', type: 'address', internalType: 'address' }, + { name: 'spender', type: 'address', internalType: 'address' }, ], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], stateMutability: 'view', }, { type: 'function', - name: 'AUCTION_LAUNCHER', + name: 'approve', + inputs: [ + { name: 'spender', type: 'address', internalType: 'address' }, + { name: 'value', type: 'uint256', internalType: 'uint256' }, + ], + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'auctionLength', inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'auctions', + inputs: [{ name: 'id', type: 'uint256', internalType: 'uint256' }], outputs: [ - { - name: '', - type: 'bytes32', - internalType: 'bytes32', - }, + { name: 'rebalanceNonce', type: 'uint256', internalType: 'uint256' }, + { name: 'startTime', type: 'uint256', internalType: 'uint256' }, + { name: 'endTime', type: 'uint256', internalType: 'uint256' }, ], stateMutability: 'view', }, { type: 'function', - name: 'BRAND_MANAGER', + name: 'balanceOf', + inputs: [{ name: 'account', type: 'address', internalType: 'address' }], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'bid', + inputs: [ + { name: 'auctionId', type: 'uint256', internalType: 'uint256' }, + { name: 'sellToken', type: 'address', internalType: 'contract IERC20' }, + { name: 'buyToken', type: 'address', internalType: 'contract IERC20' }, + { name: 'sellAmount', type: 'uint256', internalType: 'uint256' }, + { name: 'maxBuyAmount', type: 'uint256', internalType: 'uint256' }, + { name: 'withCallback', type: 'bool', internalType: 'bool' }, + { name: 'data', type: 'bytes', internalType: 'bytes' }, + ], + outputs: [{ name: 'boughtAmt', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'bidsEnabled', inputs: [], + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'closeAuction', + inputs: [{ name: 'auctionId', type: 'uint256', internalType: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'createTrustedFill', + inputs: [ + { name: 'auctionId', type: 'uint256', internalType: 'uint256' }, + { name: 'sellToken', type: 'address', internalType: 'contract IERC20' }, + { name: 'buyToken', type: 'address', internalType: 'contract IERC20' }, + { name: 'targetFiller', type: 'address', internalType: 'address' }, + { name: 'deploymentSalt', type: 'bytes32', internalType: 'bytes32' }, + ], outputs: [ { - name: '', - type: 'bytes32', - internalType: 'bytes32', + name: 'filler', + type: 'address', + internalType: 'contract IBaseTrustedFiller', }, ], - stateMutability: 'view', + stateMutability: 'nonpayable', }, { type: 'function', - name: 'DEFAULT_ADMIN_ROLE', + name: 'daoFeeRegistry', inputs: [], outputs: [ { name: '', - type: 'bytes32', - internalType: 'bytes32', + type: 'address', + internalType: 'contract IFolioDAOFeeRegistry', }, ], stateMutability: 'view', }, { type: 'function', - name: 'addToBasket', - inputs: [ - { - name: 'token', - type: 'address', - internalType: 'contract IERC20', - }, - ], + name: 'daoPendingFeeShares', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'decimals', + inputs: [], + outputs: [{ name: '', type: 'uint8', internalType: 'uint8' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'deprecateFolio', + inputs: [], outputs: [], stateMutability: 'nonpayable', }, { type: 'function', - name: 'allowance', + name: 'distributeFees', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'endRebalance', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'feeRecipients', + inputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + outputs: [ + { name: 'recipient', type: 'address', internalType: 'address' }, + { name: 'portion', type: 'uint96', internalType: 'uint96' }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'feeRecipientsPendingFeeShares', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getAuctionPrice', inputs: [ - { - name: 'owner', - type: 'address', - internalType: 'address', - }, - { - name: 'spender', - type: 'address', - internalType: 'address', - }, + { name: 'auctionId', type: 'uint256', internalType: 'uint256' }, + { name: 'token', type: 'address', internalType: 'address' }, ], outputs: [ { - name: '', - type: 'uint256', - internalType: 'uint256', + name: 'range', + type: 'tuple', + internalType: 'struct IFolio.PriceRange', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], }, ], stateMutability: 'view', }, { type: 'function', - name: 'approve', + name: 'getBid', inputs: [ - { - name: 'spender', - type: 'address', - internalType: 'address', - }, - { - name: 'value', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'auctionId', type: 'uint256', internalType: 'uint256' }, + { name: 'sellToken', type: 'address', internalType: 'contract IERC20' }, + { name: 'buyToken', type: 'address', internalType: 'contract IERC20' }, + { name: 'maxSellAmount', type: 'uint256', internalType: 'uint256' }, ], outputs: [ - { - name: '', - type: 'bool', - internalType: 'bool', - }, + { name: 'sellAmount', type: 'uint256', internalType: 'uint256' }, + { name: 'bidAmount', type: 'uint256', internalType: 'uint256' }, + { name: 'price', type: 'uint256', internalType: 'uint256' }, ], - stateMutability: 'nonpayable', + stateMutability: 'view', }, { type: 'function', - name: 'approveAuction', - inputs: [ - { - name: 'sell', - type: 'address', - internalType: 'contract IERC20', - }, + name: 'getPendingFeeShares', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRebalance', + inputs: [], + outputs: [ + { name: 'nonce', type: 'uint256', internalType: 'uint256' }, { - name: 'buy', - type: 'address', - internalType: 'contract IERC20', + name: 'priceControl', + type: 'uint8', + internalType: 'enum IFolio.PriceControl', }, { - name: 'sellLimit', - type: 'tuple', - internalType: 'struct IFolio.Range', + name: 'tokens', + type: 'tuple[]', + internalType: 'struct IFolio.TokenRebalanceParams[]', components: [ + { name: 'token', type: 'address', internalType: 'address' }, { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', + name: 'weight', + type: 'tuple', + internalType: 'struct IFolio.WeightRange', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], }, { - name: 'high', - type: 'uint256', - internalType: 'uint256', + name: 'price', + type: 'tuple', + internalType: 'struct IFolio.PriceRange', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], }, + { name: 'maxAuctionSize', type: 'uint256', internalType: 'uint256' }, + { name: 'inRebalance', type: 'bool', internalType: 'bool' }, ], }, { - name: 'buyLimit', + name: 'limits', type: 'tuple', - internalType: 'struct IFolio.Range', + internalType: 'struct IFolio.RebalanceLimits', components: [ - { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'high', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, ], }, { - name: 'prices', + name: 'timestamps', type: 'tuple', - internalType: 'struct IFolio.Prices', + internalType: 'struct Folio.RebalanceTimestamps', components: [ - { - name: 'start', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'end', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'startedAt', type: 'uint256', internalType: 'uint256' }, + { name: 'restrictedUntil', type: 'uint256', internalType: 'uint256' }, + { name: 'availableUntil', type: 'uint256', internalType: 'uint256' }, ], }, - { - name: 'ttl', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'bidsEnabled_', type: 'bool', internalType: 'bool' }, + ], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleAdmin', + inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], + outputs: [{ name: '', type: 'bytes32', internalType: 'bytes32' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleMember', + inputs: [ + { name: 'role', type: 'bytes32', internalType: 'bytes32' }, + { name: 'index', type: 'uint256', internalType: 'uint256' }, + ], + outputs: [{ name: '', type: 'address', internalType: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleMemberCount', + inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'getRoleMembers', + inputs: [{ name: 'role', type: 'bytes32', internalType: 'bytes32' }], + outputs: [{ name: '', type: 'address[]', internalType: 'address[]' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'grantRole', + inputs: [ + { name: 'role', type: 'bytes32', internalType: 'bytes32' }, + { name: 'account', type: 'address', internalType: 'address' }, ], outputs: [], stateMutability: 'nonpayable', }, { type: 'function', - name: 'approveAuction', + name: 'hasRole', + inputs: [ + { name: 'role', type: 'bytes32', internalType: 'bytes32' }, + { name: 'account', type: 'address', internalType: 'address' }, + ], + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'initialize', inputs: [ - { name: 'sell', type: 'address', internalType: 'contract IERC20' }, - { name: 'buy', type: 'address', internalType: 'contract IERC20' }, { - name: 'sellLimit', + name: '_basicDetails', type: 'tuple', - internalType: 'struct IFolio.BasketRange', + internalType: 'struct IFolio.FolioBasicDetails', components: [ - { name: 'spot', type: 'uint256', internalType: 'uint256' }, - { name: 'low', type: 'uint256', internalType: 'uint256' }, - { name: 'high', type: 'uint256', internalType: 'uint256' }, + { name: 'name', type: 'string', internalType: 'string' }, + { name: 'symbol', type: 'string', internalType: 'string' }, + { name: 'assets', type: 'address[]', internalType: 'address[]' }, + { name: 'amounts', type: 'uint256[]', internalType: 'uint256[]' }, + { name: 'initialShares', type: 'uint256', internalType: 'uint256' }, ], }, { - name: 'buyLimit', + name: '_additionalDetails', type: 'tuple', - internalType: 'struct IFolio.BasketRange', + internalType: 'struct IFolio.FolioAdditionalDetails', components: [ - { name: 'spot', type: 'uint256', internalType: 'uint256' }, - { name: 'low', type: 'uint256', internalType: 'uint256' }, - { name: 'high', type: 'uint256', internalType: 'uint256' }, + { name: 'auctionLength', type: 'uint256', internalType: 'uint256' }, + { + name: 'feeRecipients', + type: 'tuple[]', + internalType: 'struct IFolio.FeeRecipient[]', + components: [ + { name: 'recipient', type: 'address', internalType: 'address' }, + { name: 'portion', type: 'uint96', internalType: 'uint96' }, + ], + }, + { name: 'tvlFee', type: 'uint256', internalType: 'uint256' }, + { name: 'mintFee', type: 'uint256', internalType: 'uint256' }, + { name: 'mandate', type: 'string', internalType: 'string' }, ], }, { - name: 'prices', + name: '_folioRegistries', type: 'tuple', - internalType: 'struct IFolio.Prices', + internalType: 'struct IFolio.FolioRegistryIndex', components: [ - { name: 'start', type: 'uint256', internalType: 'uint256' }, - { name: 'end', type: 'uint256', internalType: 'uint256' }, + { name: 'daoFeeRegistry', type: 'address', internalType: 'address' }, + { + name: 'trustedFillerRegistry', + type: 'address', + internalType: 'address', + }, ], }, - { name: 'ttl', type: 'uint256', internalType: 'uint256' }, - { name: 'runs', type: 'uint256', internalType: 'uint256' }, + { + name: '_folioFlags', + type: 'tuple', + internalType: 'struct IFolio.FolioFlags', + components: [ + { name: 'trustedFillerEnabled', type: 'bool', internalType: 'bool' }, + { + name: 'rebalanceControl', + type: 'tuple', + internalType: 'struct IFolio.RebalanceControl', + components: [ + { name: 'weightControl', type: 'bool', internalType: 'bool' }, + { + name: 'priceControl', + type: 'uint8', + internalType: 'enum IFolio.PriceControl', + }, + ], + }, + { name: 'bidsEnabled', type: 'bool', internalType: 'bool' }, + ], + }, + { name: '_creator', type: 'address', internalType: 'address' }, ], outputs: [], stateMutability: 'nonpayable', }, { type: 'function', - name: 'auctionDelay', + name: 'isDeprecated', inputs: [], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], stateMutability: 'view', }, { type: 'function', - name: 'auctionLength', + name: 'lastPoke', inputs: [], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], stateMutability: 'view', }, { type: 'function', - name: 'auctions', + name: 'mandate', + inputs: [], + outputs: [{ name: '', type: 'string', internalType: 'string' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'mint', inputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'shares', type: 'uint256', internalType: 'uint256' }, + { name: 'receiver', type: 'address', internalType: 'address' }, + { name: 'minSharesOut', type: 'uint256', internalType: 'uint256' }, ], outputs: [ + { name: '_assets', type: 'address[]', internalType: 'address[]' }, + { name: '_amounts', type: 'uint256[]', internalType: 'uint256[]' }, + ], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'mintFee', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'name', + inputs: [], + outputs: [{ name: '', type: 'string', internalType: 'string' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'nextAuctionId', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'openAuction', + inputs: [ + { name: 'rebalanceNonce', type: 'uint256', internalType: 'uint256' }, + { name: 'tokens', type: 'address[]', internalType: 'address[]' }, { - name: 'id', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'sell', - type: 'address', - internalType: 'contract IERC20', - }, - { - name: 'buy', - type: 'address', - internalType: 'contract IERC20', - }, - { - name: 'sellLimit', - type: 'tuple', - internalType: 'struct IFolio.Range', + name: 'newWeights', + type: 'tuple[]', + internalType: 'struct IFolio.WeightRange[]', components: [ - { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'high', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, ], }, { - name: 'buyLimit', - type: 'tuple', - internalType: 'struct IFolio.Range', + name: 'newPrices', + type: 'tuple[]', + internalType: 'struct IFolio.PriceRange[]', components: [ - { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'high', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, ], }, { - name: 'prices', + name: 'newLimits', type: 'tuple', - internalType: 'struct IFolio.Prices', + internalType: 'struct IFolio.RebalanceLimits', components: [ - { - name: 'start', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'end', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, ], }, - { - name: 'availableAt', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'launchTimeout', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'start', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'end', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'k', - type: 'uint256', - internalType: 'uint256', - }, ], - stateMutability: 'view', + outputs: [{ name: 'auctionId', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'balanceOf', + name: 'openAuctionUnrestricted', inputs: [ - { - name: 'account', - type: 'address', - internalType: 'address', - }, + { name: 'rebalanceNonce', type: 'uint256', internalType: 'uint256' }, ], + outputs: [{ name: 'auctionId', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'poke', + inputs: [], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'rebalanceControl', + inputs: [], outputs: [ + { name: 'weightControl', type: 'bool', internalType: 'bool' }, { - name: '', - type: 'uint256', - internalType: 'uint256', + name: 'priceControl', + type: 'uint8', + internalType: 'enum IFolio.PriceControl', }, ], stateMutability: 'view', }, { type: 'function', - name: 'bid', + name: 'redeem', inputs: [ - { - name: 'auctionId', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'sellAmount', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'maxBuyAmount', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'withCallback', - type: 'bool', - internalType: 'bool', - }, - { - name: 'data', - type: 'bytes', - internalType: 'bytes', - }, + { name: 'shares', type: 'uint256', internalType: 'uint256' }, + { name: 'receiver', type: 'address', internalType: 'address' }, + { name: 'assets', type: 'address[]', internalType: 'address[]' }, + { name: 'minAmountsOut', type: 'uint256[]', internalType: 'uint256[]' }, ], outputs: [ - { - name: 'boughtAmt', - type: 'uint256', - internalType: 'uint256', - }, + { name: '_amounts', type: 'uint256[]', internalType: 'uint256[]' }, ], stateMutability: 'nonpayable', }, { type: 'function', - name: 'buyEnds', + name: 'removeFromBasket', inputs: [ - { - name: '', - type: 'address', - internalType: 'address', - }, - ], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'token', type: 'address', internalType: 'contract IERC20' }, ], - stateMutability: 'view', + outputs: [], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'closeAuction', + name: 'renounceRole', inputs: [ - { - name: 'auctionId', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'role', type: 'bytes32', internalType: 'bytes32' }, + { name: 'callerConfirmation', type: 'address', internalType: 'address' }, ], outputs: [], stateMutability: 'nonpayable', }, { type: 'function', - name: 'daoFeeRegistry', - inputs: [], - outputs: [ - { - name: '', - type: 'address', - internalType: 'contract IFolioDAOFeeRegistry', - }, + name: 'revokeRole', + inputs: [ + { name: 'role', type: 'bytes32', internalType: 'bytes32' }, + { name: 'account', type: 'address', internalType: 'address' }, ], - stateMutability: 'view', + outputs: [], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'daoPendingFeeShares', - inputs: [], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], - stateMutability: 'view', + name: 'setAuctionLength', + inputs: [{ name: '_newLength', type: 'uint256', internalType: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'decimals', - inputs: [], - outputs: [ + name: 'setBidsEnabled', + inputs: [{ name: '_bidsEnabled', type: 'bool', internalType: 'bool' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setFeeRecipients', + inputs: [ { - name: '', - type: 'uint8', - internalType: 'uint8', + name: '_newRecipients', + type: 'tuple[]', + internalType: 'struct IFolio.FeeRecipient[]', + components: [ + { name: 'recipient', type: 'address', internalType: 'address' }, + { name: 'portion', type: 'uint96', internalType: 'uint96' }, + ], }, ], - stateMutability: 'view', + outputs: [], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'distributeFees', - inputs: [], + name: 'setMandate', + inputs: [{ name: '_newMandate', type: 'string', internalType: 'string' }], outputs: [], stateMutability: 'nonpayable', }, { type: 'function', - name: 'feeRecipients', + name: 'setMintFee', + inputs: [{ name: '_newFee', type: 'uint256', internalType: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setName', + inputs: [{ name: '_newName', type: 'string', internalType: 'string' }], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'setRebalanceControl', inputs: [ { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], - outputs: [ - { - name: 'recipient', - type: 'address', - internalType: 'address', - }, - { - name: 'portion', - type: 'uint96', - internalType: 'uint96', + name: '_rebalanceControl', + type: 'tuple', + internalType: 'struct IFolio.RebalanceControl', + components: [ + { name: 'weightControl', type: 'bool', internalType: 'bool' }, + { + name: 'priceControl', + type: 'uint8', + internalType: 'enum IFolio.PriceControl', + }, + ], }, ], - stateMutability: 'view', + outputs: [], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'feeRecipientsPendingFeeShares', - inputs: [], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], - stateMutability: 'view', + name: 'setTVLFee', + inputs: [{ name: '_newFee', type: 'uint256', internalType: 'uint256' }], + outputs: [], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'folio', - inputs: [], - outputs: [ - { - name: '_assets', - type: 'address[]', - internalType: 'address[]', - }, - { - name: '_amounts', - type: 'uint256[]', - internalType: 'uint256[]', - }, + name: 'setTrustedFillerRegistry', + inputs: [ + { name: '_newFillerRegistry', type: 'address', internalType: 'address' }, + { name: '_enabled', type: 'bool', internalType: 'bool' }, ], - stateMutability: 'view', + outputs: [], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'getBid', + name: 'startRebalance', inputs: [ { - name: 'auctionId', - type: 'uint256', - internalType: 'uint256', + name: 'tokens', + type: 'tuple[]', + internalType: 'struct IFolio.TokenRebalanceParams[]', + components: [ + { name: 'token', type: 'address', internalType: 'address' }, + { + name: 'weight', + type: 'tuple', + internalType: 'struct IFolio.WeightRange', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], + }, + { + name: 'price', + type: 'tuple', + internalType: 'struct IFolio.PriceRange', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], + }, + { name: 'maxAuctionSize', type: 'uint256', internalType: 'uint256' }, + { name: 'inRebalance', type: 'bool', internalType: 'bool' }, + ], }, { - name: 'timestamp', - type: 'uint256', - internalType: 'uint256', + name: 'limits', + type: 'tuple', + internalType: 'struct IFolio.RebalanceLimits', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], }, { - name: 'sellAmount', + name: 'auctionLauncherWindow', type: 'uint256', internalType: 'uint256', }, + { name: 'ttl', type: 'uint256', internalType: 'uint256' }, ], + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'stateChangeActive', + inputs: [], outputs: [ - { - name: 'bidAmount', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'syncStateChangeActive', type: 'bool', internalType: 'bool' }, + { name: 'asyncStateChangeActive', type: 'bool', internalType: 'bool' }, ], stateMutability: 'view', }, { type: 'function', - name: 'getPendingFeeShares', + name: 'supportsInterface', + inputs: [{ name: 'interfaceId', type: 'bytes4', internalType: 'bytes4' }], + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'symbol', inputs: [], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], + outputs: [{ name: '', type: 'string', internalType: 'string' }], stateMutability: 'view', }, { type: 'function', - name: 'getPrice', + name: 'toAssets', inputs: [ - { - name: 'auctionId', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'timestamp', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'shares', type: 'uint256', internalType: 'uint256' }, + { name: 'rounding', type: 'uint8', internalType: 'enum Math.Rounding' }, ], outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, + { name: '_assets', type: 'address[]', internalType: 'address[]' }, + { name: '_amounts', type: 'uint256[]', internalType: 'uint256[]' }, ], stateMutability: 'view', }, { type: 'function', - name: 'getRoleAdmin', - inputs: [ - { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', - }, - ], + name: 'totalAssets', + inputs: [], outputs: [ - { - name: '', - type: 'bytes32', - internalType: 'bytes32', - }, + { name: '_assets', type: 'address[]', internalType: 'address[]' }, + { name: '_amounts', type: 'uint256[]', internalType: 'uint256[]' }, ], stateMutability: 'view', }, { type: 'function', - name: 'getRoleMember', - inputs: [ - { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', - }, - { - name: 'index', - type: 'uint256', - internalType: 'uint256', - }, - ], - outputs: [ - { - name: '', - type: 'address', - internalType: 'address', - }, - ], + name: 'totalSupply', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], stateMutability: 'view', }, { type: 'function', - name: 'getRoleMemberCount', + name: 'transfer', inputs: [ - { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', - }, - ], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, + { name: 'to', type: 'address', internalType: 'address' }, + { name: 'value', type: 'uint256', internalType: 'uint256' }, ], - stateMutability: 'view', + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], + stateMutability: 'nonpayable', }, { type: 'function', - name: 'getRoleMembers', + name: 'transferFrom', inputs: [ - { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', - }, + { name: 'from', type: 'address', internalType: 'address' }, + { name: 'to', type: 'address', internalType: 'address' }, + { name: 'value', type: 'uint256', internalType: 'uint256' }, ], + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], + stateMutability: 'nonpayable', + }, + { + type: 'function', + name: 'trustedFillerEnabled', + inputs: [], + outputs: [{ name: '', type: 'bool', internalType: 'bool' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'trustedFillerRegistry', + inputs: [], outputs: [ { name: '', - type: 'address[]', - internalType: 'address[]', + type: 'address', + internalType: 'contract ITrustedFillerRegistry', }, ], stateMutability: 'view', }, { type: 'function', - name: 'grantRole', + name: 'tvlFee', + inputs: [], + outputs: [{ name: '', type: 'uint256', internalType: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + name: 'version', + inputs: [], + outputs: [{ name: '', type: 'string', internalType: 'string' }], + stateMutability: 'pure', + }, + { + type: 'event', + name: 'Approval', inputs: [ { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', + name: 'owner', + type: 'address', + indexed: true, + internalType: 'address', }, { - name: 'account', + name: 'spender', type: 'address', + indexed: true, internalType: 'address', }, + { + name: 'value', + type: 'uint256', + indexed: false, + internalType: 'uint256', + }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'hasRole', + type: 'event', + name: 'AuctionBid', inputs: [ { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', + name: 'auctionId', + type: 'uint256', + indexed: true, + internalType: 'uint256', }, { - name: 'account', + name: 'sellToken', type: 'address', + indexed: true, internalType: 'address', }, - ], - outputs: [ { - name: '', - type: 'bool', - internalType: 'bool', + name: 'buyToken', + type: 'address', + indexed: true, + internalType: 'address', }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'initialize', - inputs: [ { - name: '_basicDetails', - type: 'tuple', - internalType: 'struct IFolio.FolioBasicDetails', - components: [ - { - name: 'name', - type: 'string', - internalType: 'string', - }, - { - name: 'symbol', - type: 'string', - internalType: 'string', - }, - { - name: 'assets', - type: 'address[]', - internalType: 'address[]', - }, - { - name: 'amounts', - type: 'uint256[]', - internalType: 'uint256[]', - }, - { - name: 'initialShares', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - name: '_additionalDetails', - type: 'tuple', - internalType: 'struct IFolio.FolioAdditionalDetails', - components: [ - { - name: 'auctionDelay', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'auctionLength', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'feeRecipients', - type: 'tuple[]', - internalType: 'struct IFolio.FeeRecipient[]', - components: [ - { - name: 'recipient', - type: 'address', - internalType: 'address', - }, - { - name: 'portion', - type: 'uint96', - internalType: 'uint96', - }, - ], - }, - { - name: 'tvlFee', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'mintFee', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'mandate', - type: 'string', - internalType: 'string', - }, - ], - }, - { - name: '_creator', - type: 'address', - internalType: 'address', + name: 'sellAmount', + type: 'uint256', + indexed: false, + internalType: 'uint256', }, { - name: '_daoFeeRegistry', - type: 'address', - internalType: 'address', + name: 'buyAmount', + type: 'uint256', + indexed: false, + internalType: 'uint256', }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'isKilled', - inputs: [], - outputs: [ + type: 'event', + name: 'AuctionClosed', + inputs: [ { - name: '', - type: 'bool', - internalType: 'bool', + name: 'auctionId', + type: 'uint256', + indexed: true, + internalType: 'uint256', }, ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'killFolio', - inputs: [], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'lastPoke', - inputs: [], - outputs: [ + type: 'event', + name: 'AuctionLengthSet', + inputs: [ { - name: '', + name: 'newAuctionLength', type: 'uint256', + indexed: false, internalType: 'uint256', }, ], - stateMutability: 'view', + anonymous: false, }, { - type: 'function', - name: 'lot', + type: 'event', + name: 'AuctionOpened', inputs: [ { - name: 'auctionId', + name: 'rebalanceNonce', type: 'uint256', + indexed: true, internalType: 'uint256', }, { - name: 'timestamp', + name: 'auctionId', type: 'uint256', + indexed: true, internalType: 'uint256', }, - ], - outputs: [ { - name: 'sellAmount', + name: 'tokens', + type: 'address[]', + indexed: false, + internalType: 'address[]', + }, + { + name: 'weights', + type: 'tuple[]', + indexed: false, + internalType: 'struct IFolio.WeightRange[]', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], + }, + { + name: 'prices', + type: 'tuple[]', + indexed: false, + internalType: 'struct IFolio.PriceRange[]', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], + }, + { + name: 'limits', + type: 'tuple', + indexed: false, + internalType: 'struct IFolio.RebalanceLimits', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], + }, + { + name: 'startTime', type: 'uint256', + indexed: false, internalType: 'uint256', }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'mandate', - inputs: [], - outputs: [ { - name: '', - type: 'string', - internalType: 'string', + name: 'endTime', + type: 'uint256', + indexed: false, + internalType: 'uint256', }, ], - stateMutability: 'view', + anonymous: false, }, { - type: 'function', - name: 'mint', + type: 'event', + name: 'AuctionTrustedFillCreated', inputs: [ { - name: 'shares', + name: 'auctionId', type: 'uint256', + indexed: true, internalType: 'uint256', }, { - name: 'receiver', + name: 'filler', type: 'address', + indexed: false, internalType: 'address', }, ], - outputs: [ - { - name: '_assets', - type: 'address[]', - internalType: 'address[]', - }, + anonymous: false, + }, + { + type: 'event', + name: 'BasketTokenAdded', + inputs: [ { - name: '_amounts', - type: 'uint256[]', - internalType: 'uint256[]', + name: 'token', + type: 'address', + indexed: true, + internalType: 'address', }, ], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'mintFee', - inputs: [], - outputs: [ + type: 'event', + name: 'BasketTokenRemoved', + inputs: [ { - name: '', - type: 'uint256', - internalType: 'uint256', + name: 'token', + type: 'address', + indexed: true, + internalType: 'address', }, ], - stateMutability: 'view', + anonymous: false, }, { - type: 'function', - name: 'name', - inputs: [], - outputs: [ + type: 'event', + name: 'BidsEnabledSet', + inputs: [ { - name: '', - type: 'string', - internalType: 'string', + name: 'bidsEnabled', + type: 'bool', + indexed: false, + internalType: 'bool', }, ], - stateMutability: 'view', + anonymous: false, }, { - type: 'function', - name: 'nextAuctionId', - inputs: [], - outputs: [ + type: 'event', + name: 'FeeRecipientsSet', + inputs: [ { - name: '', - type: 'uint256', - internalType: 'uint256', + name: 'recipients', + type: 'tuple[]', + indexed: false, + internalType: 'struct IFolio.FeeRecipient[]', + components: [ + { name: 'recipient', type: 'address', internalType: 'address' }, + { name: 'portion', type: 'uint96', internalType: 'uint96' }, + ], }, ], - stateMutability: 'view', + anonymous: false, }, + { type: 'event', name: 'FolioDeprecated', inputs: [], anonymous: false }, { - type: 'function', - name: 'openAuction', + type: 'event', + name: 'FolioFeePaid', inputs: [ { - name: 'auctionId', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'sellLimit', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'buyLimit', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'startPrice', - type: 'uint256', - internalType: 'uint256', + name: 'recipient', + type: 'address', + indexed: true, + internalType: 'address', }, { - name: 'endPrice', + name: 'amount', type: 'uint256', + indexed: false, internalType: 'uint256', }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, - { - type: 'function', - name: 'openAuctionPermissionlessly', + type: 'event', + name: 'Initialized', inputs: [ { - name: 'auctionId', - type: 'uint256', - internalType: 'uint256', - }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'poke', - inputs: [], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'redeem', - inputs: [ - { - name: 'shares', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'receiver', - type: 'address', - internalType: 'address', - }, - { - name: 'assets', - type: 'address[]', - internalType: 'address[]', - }, - { - name: 'minAmountsOut', - type: 'uint256[]', - internalType: 'uint256[]', - }, - ], - outputs: [ - { - name: '_amounts', - type: 'uint256[]', - internalType: 'uint256[]', + name: 'version', + type: 'uint64', + indexed: false, + internalType: 'uint64', }, ], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'removeFromBasket', + type: 'event', + name: 'MandateSet', inputs: [ { - name: 'token', - type: 'address', - internalType: 'contract IERC20', + name: 'newMandate', + type: 'string', + indexed: false, + internalType: 'string', }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'renounceRole', + type: 'event', + name: 'MintFeeSet', inputs: [ { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', - }, - { - name: 'callerConfirmation', - type: 'address', - internalType: 'address', + name: 'newFee', + type: 'uint256', + indexed: false, + internalType: 'uint256', }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'revokeRole', + type: 'event', + name: 'NameSet', inputs: [ { - name: 'role', - type: 'bytes32', - internalType: 'bytes32', - }, - { - name: 'account', - type: 'address', - internalType: 'address', + name: 'newName', + type: 'string', + indexed: false, + internalType: 'string', }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'sellEnds', + type: 'event', + name: 'ProtocolFeePaid', inputs: [ { - name: '', + name: 'recipient', type: 'address', + indexed: true, internalType: 'address', }, - ], - outputs: [ { - name: '', + name: 'amount', type: 'uint256', + indexed: false, internalType: 'uint256', }, ], - stateMutability: 'view', + anonymous: false, }, { - type: 'function', - name: 'setAuctionDelay', + type: 'event', + name: 'RebalanceControlSet', inputs: [ { - name: '_newDelay', - type: 'uint256', - internalType: 'uint256', + name: 'newControl', + type: 'tuple', + indexed: false, + internalType: 'struct IFolio.RebalanceControl', + components: [ + { name: 'weightControl', type: 'bool', internalType: 'bool' }, + { + name: 'priceControl', + type: 'uint8', + internalType: 'enum IFolio.PriceControl', + }, + ], }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'setAuctionLength', + type: 'event', + name: 'RebalanceEnded', inputs: [ { - name: '_newLength', + name: 'nonce', type: 'uint256', + indexed: false, internalType: 'uint256', }, ], - outputs: [], - stateMutability: 'nonpayable', + anonymous: false, }, { - type: 'function', - name: 'setFeeRecipients', + type: 'event', + name: 'RebalanceStarted', inputs: [ { - name: '_newRecipients', + name: 'nonce', + type: 'uint256', + indexed: true, + internalType: 'uint256', + }, + { + name: 'priceControl', + type: 'uint8', + indexed: false, + internalType: 'enum IFolio.PriceControl', + }, + { + name: 'tokens', type: 'tuple[]', - internalType: 'struct IFolio.FeeRecipient[]', + indexed: false, + internalType: 'struct IFolio.TokenRebalanceParams[]', components: [ + { name: 'token', type: 'address', internalType: 'address' }, { - name: 'recipient', - type: 'address', - internalType: 'address', + name: 'weight', + type: 'tuple', + internalType: 'struct IFolio.WeightRange', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], }, { - name: 'portion', - type: 'uint96', - internalType: 'uint96', + name: 'price', + type: 'tuple', + internalType: 'struct IFolio.PriceRange', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], }, + { name: 'maxAuctionSize', type: 'uint256', internalType: 'uint256' }, + { name: 'inRebalance', type: 'bool', internalType: 'bool' }, ], }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setMandate', - inputs: [ { - name: '_newMandate', - type: 'string', - internalType: 'string', + name: 'limits', + type: 'tuple', + indexed: false, + internalType: 'struct IFolio.RebalanceLimits', + components: [ + { name: 'low', type: 'uint256', internalType: 'uint256' }, + { name: 'spot', type: 'uint256', internalType: 'uint256' }, + { name: 'high', type: 'uint256', internalType: 'uint256' }, + ], }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setMintFee', - inputs: [ { - name: '_newFee', + name: 'startedAt', type: 'uint256', + indexed: false, internalType: 'uint256', }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'setTVLFee', - inputs: [ { - name: '_newFee', + name: 'restrictedUntil', type: 'uint256', + indexed: false, internalType: 'uint256', }, - ], - outputs: [], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'supportsInterface', - inputs: [ { - name: 'interfaceId', - type: 'bytes4', - internalType: 'bytes4', + name: 'availableUntil', + type: 'uint256', + indexed: false, + internalType: 'uint256', }, - ], - outputs: [ { - name: '', + name: 'bidsEnabled', type: 'bool', + indexed: false, internalType: 'bool', }, ], - stateMutability: 'view', + anonymous: false, }, { - type: 'function', - name: 'symbol', - inputs: [], - outputs: [ + type: 'event', + name: 'RoleAdminChanged', + inputs: [ + { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, { - name: '', - type: 'string', - internalType: 'string', + name: 'previousAdminRole', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', + }, + { + name: 'newAdminRole', + type: 'bytes32', + indexed: true, + internalType: 'bytes32', }, ], - stateMutability: 'view', + anonymous: false, }, { - type: 'function', - name: 'toAssets', + type: 'event', + name: 'RoleGranted', inputs: [ + { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, { - name: 'shares', - type: 'uint256', - internalType: 'uint256', + name: 'account', + type: 'address', + indexed: true, + internalType: 'address', }, { - name: 'rounding', - type: 'uint8', - internalType: 'enum Math.Rounding', + name: 'sender', + type: 'address', + indexed: true, + internalType: 'address', }, ], - outputs: [ + anonymous: false, + }, + { + type: 'event', + name: 'RoleRevoked', + inputs: [ + { name: 'role', type: 'bytes32', indexed: true, internalType: 'bytes32' }, { - name: '_assets', - type: 'address[]', - internalType: 'address[]', + name: 'account', + type: 'address', + indexed: true, + internalType: 'address', }, { - name: '_amounts', - type: 'uint256[]', - internalType: 'uint256[]', + name: 'sender', + type: 'address', + indexed: true, + internalType: 'address', }, ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'totalAssets', - inputs: [], - outputs: [ - { - name: '_assets', - type: 'address[]', - internalType: 'address[]', - }, - { - name: '_amounts', - type: 'uint256[]', - internalType: 'uint256[]', - }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'totalSupply', - inputs: [], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'transfer', - inputs: [ - { - name: 'to', - type: 'address', - internalType: 'address', - }, - { - name: 'value', - type: 'uint256', - internalType: 'uint256', - }, - ], - outputs: [ - { - name: '', - type: 'bool', - internalType: 'bool', - }, - ], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'transferFrom', - inputs: [ - { - name: 'from', - type: 'address', - internalType: 'address', - }, - { - name: 'to', - type: 'address', - internalType: 'address', - }, - { - name: 'value', - type: 'uint256', - internalType: 'uint256', - }, - ], - outputs: [ - { - name: '', - type: 'bool', - internalType: 'bool', - }, - ], - stateMutability: 'nonpayable', - }, - { - type: 'function', - name: 'tvlFee', - inputs: [], - outputs: [ - { - name: '', - type: 'uint256', - internalType: 'uint256', - }, - ], - stateMutability: 'view', - }, - { - type: 'function', - name: 'version', - inputs: [], - outputs: [ - { - name: '', - type: 'string', - internalType: 'string', - }, - ], - stateMutability: 'pure', - }, - { - type: 'event', - name: 'Approval', - inputs: [ - { - name: 'owner', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'spender', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'value', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'AuctionApproved', - inputs: [ - { - name: 'auctionId', - type: 'uint256', - indexed: true, - internalType: 'uint256', - }, - { - name: 'from', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'to', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'auction', - type: 'tuple', - indexed: false, - internalType: 'struct IFolio.Auction', - components: [ - { - name: 'id', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'sell', - type: 'address', - internalType: 'contract IERC20', - }, - { - name: 'buy', - type: 'address', - internalType: 'contract IERC20', - }, - { - name: 'sellLimit', - type: 'tuple', - internalType: 'struct IFolio.Range', - components: [ - { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'high', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - name: 'buyLimit', - type: 'tuple', - internalType: 'struct IFolio.Range', - components: [ - { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'high', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - name: 'prices', - type: 'tuple', - internalType: 'struct IFolio.Prices', - components: [ - { - name: 'start', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'end', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - name: 'availableAt', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'launchTimeout', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'start', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'end', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'k', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'AuctionBid', - inputs: [ - { - name: 'auctionId', - type: 'uint256', - indexed: true, - internalType: 'uint256', - }, - { - name: 'sellAmount', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - { - name: 'buyAmount', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'AuctionClosed', - inputs: [ - { - name: 'auctionId', - type: 'uint256', - indexed: true, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'AuctionDelaySet', - inputs: [ - { - name: 'newAuctionDelay', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'AuctionLengthSet', - inputs: [ - { - name: 'newAuctionLength', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'AuctionOpened', - inputs: [ - { - name: 'auctionId', - type: 'uint256', - indexed: true, - internalType: 'uint256', - }, - { - name: 'auction', - type: 'tuple', - indexed: false, - internalType: 'struct IFolio.Auction', - components: [ - { - name: 'id', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'sell', - type: 'address', - internalType: 'contract IERC20', - }, - { - name: 'buy', - type: 'address', - internalType: 'contract IERC20', - }, - { - name: 'sellLimit', - type: 'tuple', - internalType: 'struct IFolio.Range', - components: [ - { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'high', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - name: 'buyLimit', - type: 'tuple', - internalType: 'struct IFolio.Range', - components: [ - { - name: 'spot', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'low', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'high', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - name: 'prices', - type: 'tuple', - internalType: 'struct IFolio.Prices', - components: [ - { - name: 'start', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'end', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - name: 'availableAt', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'launchTimeout', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'start', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'end', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'k', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'BasketTokenAdded', - inputs: [ - { - name: 'token', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'BasketTokenRemoved', - inputs: [ - { - name: 'token', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'FeeRecipientSet', - inputs: [ - { - name: 'recipient', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'portion', - type: 'uint96', - indexed: false, - internalType: 'uint96', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'FolioFeePaid', - inputs: [ - { - name: 'recipient', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'amount', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'FolioKilled', - inputs: [], - anonymous: false, - }, - { - type: 'event', - name: 'Initialized', - inputs: [ - { - name: 'version', - type: 'uint64', - indexed: false, - internalType: 'uint64', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'MandateSet', - inputs: [ - { - name: 'newMandate', - type: 'string', - indexed: false, - internalType: 'string', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'MintFeeSet', - inputs: [ - { - name: 'newFee', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'ProtocolFeePaid', - inputs: [ - { - name: 'recipient', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'amount', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleAdminChanged', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'previousAdminRole', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'newAdminRole', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleGranted', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'account', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'sender', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'RoleRevoked', - inputs: [ - { - name: 'role', - type: 'bytes32', - indexed: true, - internalType: 'bytes32', - }, - { - name: 'account', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'sender', - type: 'address', - indexed: true, - internalType: 'address', - }, - ], - anonymous: false, + anonymous: false, }, { type: 'event', name: 'TVLFeeSet', inputs: [ { - name: 'newFee', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - { - name: 'feeAnnually', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'event', - name: 'Transfer', - inputs: [ - { - name: 'from', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'to', - type: 'address', - indexed: true, - internalType: 'address', - }, - { - name: 'value', - type: 'uint256', - indexed: false, - internalType: 'uint256', - }, - ], - anonymous: false, - }, - { - type: 'error', - name: 'AccessControlBadConfirmation', - inputs: [], - }, - { - type: 'error', - name: 'AccessControlUnauthorizedAccount', - inputs: [ - { - name: 'account', - type: 'address', - internalType: 'address', - }, - { - name: 'neededRole', - type: 'bytes32', - internalType: 'bytes32', - }, - ], - }, - { - type: 'error', - name: 'ERC20InsufficientAllowance', - inputs: [ - { - name: 'spender', - type: 'address', - internalType: 'address', - }, - { - name: 'allowance', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'needed', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - type: 'error', - name: 'ERC20InsufficientBalance', - inputs: [ - { - name: 'sender', - type: 'address', - internalType: 'address', - }, - { - name: 'balance', + name: 'newFee', type: 'uint256', + indexed: false, internalType: 'uint256', }, { - name: 'needed', + name: 'feeAnnually', type: 'uint256', + indexed: false, internalType: 'uint256', }, ], + anonymous: false, }, { - type: 'error', - name: 'ERC20InvalidApprover', + type: 'event', + name: 'Transfer', inputs: [ + { name: 'from', type: 'address', indexed: true, internalType: 'address' }, + { name: 'to', type: 'address', indexed: true, internalType: 'address' }, { - name: 'approver', - type: 'address', - internalType: 'address', + name: 'value', + type: 'uint256', + indexed: false, + internalType: 'uint256', }, ], + anonymous: false, }, { - type: 'error', - name: 'ERC20InvalidReceiver', + type: 'event', + name: 'TrustedFillerRegistrySet', inputs: [ { - name: 'receiver', + name: 'trustedFillerRegistry', type: 'address', + indexed: false, internalType: 'address', }, + { name: 'isEnabled', type: 'bool', indexed: false, internalType: 'bool' }, ], + anonymous: false, }, + { type: 'error', name: 'AccessControlBadConfirmation', inputs: [] }, { type: 'error', - name: 'ERC20InvalidSender', + name: 'AccessControlUnauthorizedAccount', inputs: [ - { - name: 'sender', - type: 'address', - internalType: 'address', - }, + { name: 'account', type: 'address', internalType: 'address' }, + { name: 'neededRole', type: 'bytes32', internalType: 'bytes32' }, ], }, { type: 'error', - name: 'ERC20InvalidSpender', + name: 'ERC20InsufficientAllowance', inputs: [ - { - name: 'spender', - type: 'address', - internalType: 'address', - }, + { name: 'spender', type: 'address', internalType: 'address' }, + { name: 'allowance', type: 'uint256', internalType: 'uint256' }, + { name: 'needed', type: 'uint256', internalType: 'uint256' }, ], }, { type: 'error', - name: 'Folio__AuctionCannotBeOpened', - inputs: [], - }, - { - type: 'error', - name: 'Folio__AuctionCannotBeOpenedPermissionlesslyYet', - inputs: [], - }, - { - type: 'error', - name: 'Folio__AuctionCollision', - inputs: [], - }, - { - type: 'error', - name: 'Folio__AuctionNotOngoing', - inputs: [], - }, - { - type: 'error', - name: 'Folio__AuctionTimeout', - inputs: [], - }, - { - type: 'error', - name: 'Folio__BadFeeTotal', - inputs: [], - }, - { - type: 'error', - name: 'Folio__BasketModificationFailed', - inputs: [], - }, - { - type: 'error', - name: 'Folio__EmptyAssets', - inputs: [], - }, - { - type: 'error', - name: 'Folio__ExcessiveBid', - inputs: [], - }, - { - type: 'error', - name: 'Folio__FeeRecipientInvalidAddress', - inputs: [], - }, - { - type: 'error', - name: 'Folio__FeeRecipientInvalidFeeShare', - inputs: [], - }, - { - type: 'error', - name: 'Folio__FolioKilled', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InsufficientBalance', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InsufficientBid', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidArrayLengths', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidAsset', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidAssetAmount', + name: 'ERC20InsufficientBalance', inputs: [ - { - name: 'asset', - type: 'address', - internalType: 'address', - }, + { name: 'sender', type: 'address', internalType: 'address' }, + { name: 'balance', type: 'uint256', internalType: 'uint256' }, + { name: 'needed', type: 'uint256', internalType: 'uint256' }, ], }, { type: 'error', - name: 'Folio__InvalidAuctionDelay', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidAuctionLength', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidAuctionTTL', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidAuctionTokens', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidBuyLimit', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidPrices', - inputs: [], - }, - { - type: 'error', - name: 'Folio__InvalidSellLimit', - inputs: [], - }, - { - type: 'error', - name: 'Folio__MintFeeTooHigh', - inputs: [], - }, - { - type: 'error', - name: 'Folio__SlippageExceeded', - inputs: [], - }, - { - type: 'error', - name: 'Folio__TVLFeeTooHigh', - inputs: [], - }, - { - type: 'error', - name: 'Folio__TVLFeeTooLow', - inputs: [], - }, - { - type: 'error', - name: 'Folio__TooManyFeeRecipients', - inputs: [], + name: 'ERC20InvalidApprover', + inputs: [{ name: 'approver', type: 'address', internalType: 'address' }], }, { type: 'error', - name: 'Folio__Unauthorized', - inputs: [], + name: 'ERC20InvalidReceiver', + inputs: [{ name: 'receiver', type: 'address', internalType: 'address' }], }, { type: 'error', - name: 'Folio__ZeroInitialShares', - inputs: [], + name: 'ERC20InvalidSender', + inputs: [{ name: 'sender', type: 'address', internalType: 'address' }], }, { type: 'error', - name: 'InvalidInitialization', - inputs: [], + name: 'ERC20InvalidSpender', + inputs: [{ name: 'spender', type: 'address', internalType: 'address' }], }, { type: 'error', - name: 'NotInitializing', + name: 'Folio__AuctionCannotBeOpenedWithoutRestriction', inputs: [], }, + { type: 'error', name: 'Folio__AuctionNotOngoing', inputs: [] }, + { type: 'error', name: 'Folio__BadFeeTotal', inputs: [] }, + { type: 'error', name: 'Folio__BalanceNotRemovable', inputs: [] }, + { type: 'error', name: 'Folio__BasketModificationFailed', inputs: [] }, + { type: 'error', name: 'Folio__DuplicateAsset', inputs: [] }, + { type: 'error', name: 'Folio__EmptyAssets', inputs: [] }, + { type: 'error', name: 'Folio__EmptyRebalance', inputs: [] }, + { type: 'error', name: 'Folio__FeeRecipientInvalidAddress', inputs: [] }, + { type: 'error', name: 'Folio__FeeRecipientInvalidFeeShare', inputs: [] }, + { type: 'error', name: 'Folio__FolioDeprecated', inputs: [] }, + { type: 'error', name: 'Folio__InsufficientBid', inputs: [] }, + { type: 'error', name: 'Folio__InsufficientBuyAvailable', inputs: [] }, + { type: 'error', name: 'Folio__InsufficientSellAvailable', inputs: [] }, + { type: 'error', name: 'Folio__InsufficientSharesOut', inputs: [] }, + { type: 'error', name: 'Folio__InvalidArrayLengths', inputs: [] }, + { type: 'error', name: 'Folio__InvalidAsset', inputs: [] }, { type: 'error', - name: 'PRBMath_MulDiv18_Overflow', - inputs: [ - { - name: 'x', - type: 'uint256', - internalType: 'uint256', - }, - { - name: 'y', - type: 'uint256', - internalType: 'uint256', - }, - ], - }, - { - type: 'error', - name: 'PRBMath_SD59x18_Exp2_InputTooBig', - inputs: [ - { - name: 'x', - type: 'int256', - internalType: 'SD59x18', - }, - ], - }, - { - type: 'error', - name: 'PRBMath_SD59x18_Exp_InputTooBig', - inputs: [ - { - name: 'x', - type: 'int256', - internalType: 'SD59x18', - }, - ], - }, - { - type: 'error', - name: 'PRBMath_SD59x18_IntoUint256_Underflow', - inputs: [ - { - name: 'x', - type: 'int256', - internalType: 'SD59x18', - }, - ], - }, - { - type: 'error', - name: 'PRBMath_UD60x18_Exp2_InputTooBig', - inputs: [ - { - name: 'x', - type: 'uint256', - internalType: 'UD60x18', - }, - ], - }, - { - type: 'error', - name: 'PRBMath_UD60x18_Log_InputTooSmall', - inputs: [ - { - name: 'x', - type: 'uint256', - internalType: 'UD60x18', - }, - ], - }, - { - type: 'error', - name: 'ReentrancyGuardReentrantCall', - inputs: [], - }, + name: 'Folio__InvalidAssetAmount', + inputs: [{ name: 'asset', type: 'address', internalType: 'address' }], + }, + { type: 'error', name: 'Folio__InvalidAuctionLength', inputs: [] }, + { type: 'error', name: 'Folio__InvalidLimits', inputs: [] }, + { type: 'error', name: 'Folio__InvalidPrices', inputs: [] }, + { type: 'error', name: 'Folio__InvalidRegistry', inputs: [] }, + { type: 'error', name: 'Folio__InvalidTTL', inputs: [] }, + { type: 'error', name: 'Folio__InvalidTransferToSelf', inputs: [] }, + { type: 'error', name: 'Folio__InvalidWeights', inputs: [] }, + { type: 'error', name: 'Folio__MintFeeTooHigh', inputs: [] }, + { type: 'error', name: 'Folio__MixedAtomicSwaps', inputs: [] }, + { type: 'error', name: 'Folio__NotRebalancing', inputs: [] }, + { type: 'error', name: 'Folio__PermissionlessBidsDisabled', inputs: [] }, + { type: 'error', name: 'Folio__SlippageExceeded', inputs: [] }, + { type: 'error', name: 'Folio__TVLFeeTooHigh', inputs: [] }, + { type: 'error', name: 'Folio__TVLFeeTooLow', inputs: [] }, + { type: 'error', name: 'Folio__TooManyFeeRecipients', inputs: [] }, + { type: 'error', name: 'Folio__TrustedFillerRegistryAlreadySet', inputs: [] }, + { type: 'error', name: 'Folio__TrustedFillerRegistryNotEnabled', inputs: [] }, + { type: 'error', name: 'Folio__Unauthorized', inputs: [] }, + { type: 'error', name: 'Folio__ZeroInitialShares', inputs: [] }, + { type: 'error', name: 'Folo__NotInRebalance', inputs: [] }, + { type: 'error', name: 'InvalidInitialization', inputs: [] }, + { type: 'error', name: 'NotInitializing', inputs: [] }, + { type: 'error', name: 'ReentrancyGuardReentrantCall', inputs: [] }, { type: 'error', name: 'SafeERC20FailedOperation', - inputs: [ - { - name: 'token', - type: 'address', - internalType: 'address', - }, - ], - }, - { - inputs: [ - { internalType: 'address[]', name: 'tokens', type: 'address[]' }, - { - components: [ - { internalType: 'uint256', name: 'low', type: 'uint256' }, - { internalType: 'uint256', name: 'spot', type: 'uint256' }, - { internalType: 'uint256', name: 'high', type: 'uint256' }, - ], - internalType: 'struct IFolio.WeightRange[]', - name: 'weights', - type: 'tuple[]', - }, - { - components: [ - { internalType: 'uint256', name: 'low', type: 'uint256' }, - { internalType: 'uint256', name: 'high', type: 'uint256' }, - ], - internalType: 'struct IFolio.PriceRange[]', - name: 'prices', - type: 'tuple[]', - }, - { - components: [ - { internalType: 'uint256', name: 'low', type: 'uint256' }, - { internalType: 'uint256', name: 'spot', type: 'uint256' }, - { internalType: 'uint256', name: 'high', type: 'uint256' }, - ], - internalType: 'struct IFolio.RebalanceLimits', - name: 'limits', - type: 'tuple', - }, - { - internalType: 'uint256', - name: 'auctionLauncherWindow', - type: 'uint256', - }, - { internalType: 'uint256', name: 'ttl', type: 'uint256' }, - ], - name: 'startRebalance', - outputs: [], - stateMutability: 'nonpayable', - type: 'function', + inputs: [{ name: 'token', type: 'address', internalType: 'address' }], }, ] as const diff --git a/src/abis/dtf-index-deployer-abi.ts b/src/abis/dtf-index-deployer-abi.ts index f6785b108..017bc22c8 100644 --- a/src/abis/dtf-index-deployer-abi.ts +++ b/src/abis/dtf-index-deployer-abi.ts @@ -1,337 +1,339 @@ export default [ { + type: 'constructor', inputs: [ - { internalType: 'address', name: '_daoFeeRegistry', type: 'address' }, - { internalType: 'address', name: '_versionRegistry', type: 'address' }, + { name: '_daoFeeRegistry', type: 'address', internalType: 'address' }, + { name: '_versionRegistry', type: 'address', internalType: 'address' }, { - internalType: 'address', name: '_trustedFillerRegistry', type: 'address', + internalType: 'address', }, { - internalType: 'contract IGovernanceDeployer', name: '_governanceDeployer', type: 'address', + internalType: 'contract IGovernanceDeployer', }, ], stateMutability: 'nonpayable', - type: 'constructor', - }, - { inputs: [], name: 'FolioDeployer__LengthMismatch', type: 'error' }, - { - inputs: [{ internalType: 'address', name: 'token', type: 'address' }], - name: 'SafeERC20FailedOperation', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'folioOwner', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'folio', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'folioAdmin', - type: 'address', - }, - ], - name: 'FolioDeployed', - type: 'event', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'stToken', - type: 'address', - }, - { - indexed: true, - internalType: 'address', - name: 'folio', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'ownerGovernor', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'ownerTimelock', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'tradingGovernor', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'tradingTimelock', - type: 'address', - }, - ], - name: 'GovernedFolioDeployed', - type: 'event', }, { - inputs: [], + type: 'function', name: 'daoFeeRegistry', - outputs: [{ internalType: 'address', name: '', type: 'address' }], + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view', - type: 'function', }, { + type: 'function', + name: 'deployFolio', inputs: [ { - components: [ - { internalType: 'string', name: 'name', type: 'string' }, - { internalType: 'string', name: 'symbol', type: 'string' }, - { internalType: 'address[]', name: 'assets', type: 'address[]' }, - { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }, - { internalType: 'uint256', name: 'initialShares', type: 'uint256' }, - ], - internalType: 'struct IFolio.FolioBasicDetails', name: 'basicDetails', type: 'tuple', + internalType: 'struct IFolio.FolioBasicDetails', + components: [ + { name: 'name', type: 'string', internalType: 'string' }, + { name: 'symbol', type: 'string', internalType: 'string' }, + { name: 'assets', type: 'address[]', internalType: 'address[]' }, + { name: 'amounts', type: 'uint256[]', internalType: 'uint256[]' }, + { name: 'initialShares', type: 'uint256', internalType: 'uint256' }, + ], }, { + name: 'additionalDetails', + type: 'tuple', + internalType: 'struct IFolio.FolioAdditionalDetails', components: [ - { internalType: 'uint256', name: 'auctionLength', type: 'uint256' }, + { name: 'auctionLength', type: 'uint256', internalType: 'uint256' }, { - components: [ - { internalType: 'address', name: 'recipient', type: 'address' }, - { internalType: 'uint96', name: 'portion', type: 'uint96' }, - ], - internalType: 'struct IFolio.FeeRecipient[]', name: 'feeRecipients', type: 'tuple[]', + internalType: 'struct IFolio.FeeRecipient[]', + components: [ + { name: 'recipient', type: 'address', internalType: 'address' }, + { name: 'portion', type: 'uint96', internalType: 'uint96' }, + ], }, - { internalType: 'uint256', name: 'tvlFee', type: 'uint256' }, - { internalType: 'uint256', name: 'mintFee', type: 'uint256' }, - { internalType: 'string', name: 'mandate', type: 'string' }, + { name: 'tvlFee', type: 'uint256', internalType: 'uint256' }, + { name: 'mintFee', type: 'uint256', internalType: 'uint256' }, + { name: 'mandate', type: 'string', internalType: 'string' }, ], - internalType: 'struct IFolio.FolioAdditionalDetails', - name: 'additionalDetails', - type: 'tuple', }, { + name: 'folioFlags', + type: 'tuple', + internalType: 'struct IFolio.FolioFlags', components: [ - { internalType: 'bool', name: 'trustedFillerEnabled', type: 'bool' }, + { name: 'trustedFillerEnabled', type: 'bool', internalType: 'bool' }, { + name: 'rebalanceControl', + type: 'tuple', + internalType: 'struct IFolio.RebalanceControl', components: [ - { internalType: 'bool', name: 'weightControl', type: 'bool' }, + { name: 'weightControl', type: 'bool', internalType: 'bool' }, { - internalType: 'enum IFolio.PriceControl', name: 'priceControl', type: 'uint8', + internalType: 'enum IFolio.PriceControl', }, ], - internalType: 'struct IFolio.RebalanceControl', - name: 'rebalanceControl', - type: 'tuple', }, + { name: 'bidsEnabled', type: 'bool', internalType: 'bool' }, ], - internalType: 'struct IFolio.FolioFlags', - name: 'folioFlags', - type: 'tuple', }, - { internalType: 'address', name: 'owner', type: 'address' }, - { internalType: 'address[]', name: 'basketManagers', type: 'address[]' }, + { name: 'owner', type: 'address', internalType: 'address' }, + { name: 'basketManagers', type: 'address[]', internalType: 'address[]' }, { - internalType: 'address[]', name: 'auctionLaunchers', type: 'address[]', + internalType: 'address[]', }, - { internalType: 'address[]', name: 'brandManagers', type: 'address[]' }, - { internalType: 'bytes32', name: 'deploymentNonce', type: 'bytes32' }, + { name: 'brandManagers', type: 'address[]', internalType: 'address[]' }, + { name: 'deploymentNonce', type: 'bytes32', internalType: 'bytes32' }, ], - name: 'deployFolio', outputs: [ - { internalType: 'contract Folio', name: 'folio', type: 'address' }, - { internalType: 'address', name: 'proxyAdmin', type: 'address' }, + { name: 'folio', type: 'address', internalType: 'contract Folio' }, + { name: 'proxyAdmin', type: 'address', internalType: 'address' }, ], stateMutability: 'nonpayable', - type: 'function', }, { + type: 'function', + name: 'deployGovernedFolio', inputs: [ - { internalType: 'contract IVotes', name: 'stToken', type: 'address' }, + { name: 'stToken', type: 'address', internalType: 'contract IVotes' }, { - components: [ - { internalType: 'string', name: 'name', type: 'string' }, - { internalType: 'string', name: 'symbol', type: 'string' }, - { internalType: 'address[]', name: 'assets', type: 'address[]' }, - { internalType: 'uint256[]', name: 'amounts', type: 'uint256[]' }, - { internalType: 'uint256', name: 'initialShares', type: 'uint256' }, - ], - internalType: 'struct IFolio.FolioBasicDetails', name: 'basicDetails', type: 'tuple', + internalType: 'struct IFolio.FolioBasicDetails', + components: [ + { name: 'name', type: 'string', internalType: 'string' }, + { name: 'symbol', type: 'string', internalType: 'string' }, + { name: 'assets', type: 'address[]', internalType: 'address[]' }, + { name: 'amounts', type: 'uint256[]', internalType: 'uint256[]' }, + { name: 'initialShares', type: 'uint256', internalType: 'uint256' }, + ], }, { + name: 'additionalDetails', + type: 'tuple', + internalType: 'struct IFolio.FolioAdditionalDetails', components: [ - { internalType: 'uint256', name: 'auctionLength', type: 'uint256' }, + { name: 'auctionLength', type: 'uint256', internalType: 'uint256' }, { - components: [ - { internalType: 'address', name: 'recipient', type: 'address' }, - { internalType: 'uint96', name: 'portion', type: 'uint96' }, - ], - internalType: 'struct IFolio.FeeRecipient[]', name: 'feeRecipients', type: 'tuple[]', + internalType: 'struct IFolio.FeeRecipient[]', + components: [ + { name: 'recipient', type: 'address', internalType: 'address' }, + { name: 'portion', type: 'uint96', internalType: 'uint96' }, + ], }, - { internalType: 'uint256', name: 'tvlFee', type: 'uint256' }, - { internalType: 'uint256', name: 'mintFee', type: 'uint256' }, - { internalType: 'string', name: 'mandate', type: 'string' }, + { name: 'tvlFee', type: 'uint256', internalType: 'uint256' }, + { name: 'mintFee', type: 'uint256', internalType: 'uint256' }, + { name: 'mandate', type: 'string', internalType: 'string' }, ], - internalType: 'struct IFolio.FolioAdditionalDetails', - name: 'additionalDetails', - type: 'tuple', }, { + name: 'folioFlags', + type: 'tuple', + internalType: 'struct IFolio.FolioFlags', components: [ - { internalType: 'bool', name: 'trustedFillerEnabled', type: 'bool' }, + { name: 'trustedFillerEnabled', type: 'bool', internalType: 'bool' }, { + name: 'rebalanceControl', + type: 'tuple', + internalType: 'struct IFolio.RebalanceControl', components: [ - { internalType: 'bool', name: 'weightControl', type: 'bool' }, + { name: 'weightControl', type: 'bool', internalType: 'bool' }, { - internalType: 'enum IFolio.PriceControl', name: 'priceControl', type: 'uint8', + internalType: 'enum IFolio.PriceControl', }, ], - internalType: 'struct IFolio.RebalanceControl', - name: 'rebalanceControl', - type: 'tuple', }, + { name: 'bidsEnabled', type: 'bool', internalType: 'bool' }, ], - internalType: 'struct IFolio.FolioFlags', - name: 'folioFlags', - type: 'tuple', }, { + name: 'ownerGovParams', + type: 'tuple', + internalType: 'struct IGovernanceDeployer.GovParams', components: [ - { internalType: 'uint48', name: 'votingDelay', type: 'uint48' }, - { internalType: 'uint32', name: 'votingPeriod', type: 'uint32' }, + { name: 'votingDelay', type: 'uint48', internalType: 'uint48' }, + { name: 'votingPeriod', type: 'uint32', internalType: 'uint32' }, { - internalType: 'uint256', name: 'proposalThreshold', type: 'uint256', + internalType: 'uint256', }, - { internalType: 'uint256', name: 'quorumThreshold', type: 'uint256' }, - { internalType: 'uint256', name: 'timelockDelay', type: 'uint256' }, - { internalType: 'address[]', name: 'guardians', type: 'address[]' }, + { name: 'quorumThreshold', type: 'uint256', internalType: 'uint256' }, + { name: 'timelockDelay', type: 'uint256', internalType: 'uint256' }, + { name: 'guardians', type: 'address[]', internalType: 'address[]' }, ], - internalType: 'struct IGovernanceDeployer.GovParams', - name: 'ownerGovParams', - type: 'tuple', }, { + name: 'tradingGovParams', + type: 'tuple', + internalType: 'struct IGovernanceDeployer.GovParams', components: [ - { internalType: 'uint48', name: 'votingDelay', type: 'uint48' }, - { internalType: 'uint32', name: 'votingPeriod', type: 'uint32' }, + { name: 'votingDelay', type: 'uint48', internalType: 'uint48' }, + { name: 'votingPeriod', type: 'uint32', internalType: 'uint32' }, { - internalType: 'uint256', name: 'proposalThreshold', type: 'uint256', + internalType: 'uint256', }, - { internalType: 'uint256', name: 'quorumThreshold', type: 'uint256' }, - { internalType: 'uint256', name: 'timelockDelay', type: 'uint256' }, - { internalType: 'address[]', name: 'guardians', type: 'address[]' }, + { name: 'quorumThreshold', type: 'uint256', internalType: 'uint256' }, + { name: 'timelockDelay', type: 'uint256', internalType: 'uint256' }, + { name: 'guardians', type: 'address[]', internalType: 'address[]' }, ], - internalType: 'struct IGovernanceDeployer.GovParams', - name: 'tradingGovParams', - type: 'tuple', }, { + name: 'govRoles', + type: 'tuple', + internalType: 'struct IFolioDeployer.GovRoles', components: [ { - internalType: 'address[]', name: 'existingBasketManagers', type: 'address[]', + internalType: 'address[]', }, { - internalType: 'address[]', name: 'auctionLaunchers', type: 'address[]', + internalType: 'address[]', }, { - internalType: 'address[]', name: 'brandManagers', type: 'address[]', + internalType: 'address[]', }, ], - internalType: 'struct IGovernanceDeployer.GovRoles', - name: 'govRoles', - type: 'tuple', }, - { internalType: 'bytes32', name: 'deploymentNonce', type: 'bytes32' }, + { name: 'deploymentNonce', type: 'bytes32', internalType: 'bytes32' }, ], - name: 'deployGovernedFolio', outputs: [ - { internalType: 'contract Folio', name: 'folio', type: 'address' }, - { internalType: 'address', name: 'proxyAdmin', type: 'address' }, + { name: 'folio', type: 'address', internalType: 'contract Folio' }, + { name: 'proxyAdmin', type: 'address', internalType: 'address' }, ], stateMutability: 'nonpayable', - type: 'function', }, { - inputs: [], + type: 'function', name: 'folioImplementation', - outputs: [{ internalType: 'address', name: '', type: 'address' }], + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view', - type: 'function', }, { - inputs: [], + type: 'function', name: 'governanceDeployer', + inputs: [], outputs: [ { - internalType: 'contract IGovernanceDeployer', name: '', type: 'address', + internalType: 'contract IGovernanceDeployer', }, ], stateMutability: 'view', - type: 'function', }, { - inputs: [], + type: 'function', name: 'trustedFillerRegistry', - outputs: [{ internalType: 'address', name: '', type: 'address' }], + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view', - type: 'function', }, { - inputs: [], + type: 'function', name: 'version', - outputs: [{ internalType: 'string', name: '', type: 'string' }], + inputs: [], + outputs: [{ name: '', type: 'string', internalType: 'string' }], stateMutability: 'pure', - type: 'function', }, { - inputs: [], + type: 'function', name: 'versionRegistry', - outputs: [{ internalType: 'address', name: '', type: 'address' }], + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view', - type: 'function', + }, + { + type: 'event', + name: 'FolioDeployed', + inputs: [ + { + name: 'folioOwner', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'folio', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'folioAdmin', + type: 'address', + indexed: false, + internalType: 'address', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'GovernedFolioDeployed', + inputs: [ + { + name: 'stToken', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'folio', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'ownerGovernor', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'ownerTimelock', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'tradingGovernor', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'tradingTimelock', + type: 'address', + indexed: false, + internalType: 'address', + }, + ], + anonymous: false, + }, + { type: 'error', name: 'FolioDeployer__LengthMismatch', inputs: [] }, + { + type: 'error', + name: 'SafeERC20FailedOperation', + inputs: [{ name: 'token', type: 'address', internalType: 'address' }], }, ] as const diff --git a/src/abis/dtf-index-governance-deployer-abi.ts b/src/abis/dtf-index-governance-deployer-abi.ts index 9b86ff178..f4433a9f4 100644 --- a/src/abis/dtf-index-governance-deployer-abi.ts +++ b/src/abis/dtf-index-governance-deployer-abi.ts @@ -1,171 +1,171 @@ export default [ { - inputs: [ - { - internalType: 'address', - name: '_governorImplementation', - type: 'address', - }, - { - internalType: 'address', - name: '_timelockImplementation', - type: 'address', - }, - ], - stateMutability: 'nonpayable', type: 'constructor', - }, - { inputs: [], name: 'FailedDeployment', type: 'error' }, - { - inputs: [ - { internalType: 'uint256', name: 'balance', type: 'uint256' }, - { internalType: 'uint256', name: 'needed', type: 'uint256' }, - ], - name: 'InsufficientBalance', - type: 'error', - }, - { - anonymous: false, - inputs: [ - { - indexed: true, - internalType: 'address', - name: 'stToken', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'governor', - type: 'address', - }, - { - indexed: false, - internalType: 'address', - name: 'timelock', - type: 'address', - }, - ], - name: 'DeployedGovernance', - type: 'event', - }, - { - anonymous: false, inputs: [ { - indexed: true, - internalType: 'address', - name: 'underlying', + name: '_governorImplementation', type: 'address', - }, - { - indexed: true, internalType: 'address', - name: 'stToken', - type: 'address', }, { - indexed: false, - internalType: 'address', - name: 'governor', + name: '_timelockImplementation', type: 'address', - }, - { - indexed: false, internalType: 'address', - name: 'timelock', - type: 'address', }, ], - name: 'DeployedGovernedStakingToken', - type: 'event', + stateMutability: 'nonpayable', }, { + type: 'function', + name: 'deployGovernanceWithTimelock', inputs: [ { + name: 'govParams', + type: 'tuple', + internalType: 'struct IGovernanceDeployer.GovParams', components: [ - { internalType: 'uint48', name: 'votingDelay', type: 'uint48' }, - { internalType: 'uint32', name: 'votingPeriod', type: 'uint32' }, + { name: 'votingDelay', type: 'uint48', internalType: 'uint48' }, + { name: 'votingPeriod', type: 'uint32', internalType: 'uint32' }, { - internalType: 'uint256', name: 'proposalThreshold', type: 'uint256', + internalType: 'uint256', }, - { internalType: 'uint256', name: 'quorumThreshold', type: 'uint256' }, - { internalType: 'uint256', name: 'timelockDelay', type: 'uint256' }, - { internalType: 'address[]', name: 'guardians', type: 'address[]' }, + { name: 'quorumThreshold', type: 'uint256', internalType: 'uint256' }, + { name: 'timelockDelay', type: 'uint256', internalType: 'uint256' }, + { name: 'guardians', type: 'address[]', internalType: 'address[]' }, ], - internalType: 'struct IGovernanceDeployer.GovParams', - name: 'govParams', - type: 'tuple', }, - { internalType: 'contract IVotes', name: 'stToken', type: 'address' }, - { internalType: 'bytes32', name: 'deploymentNonce', type: 'bytes32' }, + { name: 'stToken', type: 'address', internalType: 'contract IVotes' }, + { name: 'deploymentNonce', type: 'bytes32', internalType: 'bytes32' }, ], - name: 'deployGovernanceWithTimelock', outputs: [ - { internalType: 'address', name: 'governor', type: 'address' }, - { internalType: 'address', name: 'timelock', type: 'address' }, + { name: 'governor', type: 'address', internalType: 'address' }, + { name: 'timelock', type: 'address', internalType: 'address' }, ], stateMutability: 'nonpayable', - type: 'function', }, { + type: 'function', + name: 'deployGovernedStakingToken', inputs: [ - { internalType: 'string', name: 'name', type: 'string' }, - { internalType: 'string', name: 'symbol', type: 'string' }, - { internalType: 'contract IERC20', name: 'underlying', type: 'address' }, + { name: 'name', type: 'string', internalType: 'string' }, + { name: 'symbol', type: 'string', internalType: 'string' }, + { name: 'underlying', type: 'address', internalType: 'contract IERC20' }, { + name: 'govParams', + type: 'tuple', + internalType: 'struct IGovernanceDeployer.GovParams', components: [ - { internalType: 'uint48', name: 'votingDelay', type: 'uint48' }, - { internalType: 'uint32', name: 'votingPeriod', type: 'uint32' }, + { name: 'votingDelay', type: 'uint48', internalType: 'uint48' }, + { name: 'votingPeriod', type: 'uint32', internalType: 'uint32' }, { - internalType: 'uint256', name: 'proposalThreshold', type: 'uint256', + internalType: 'uint256', }, - { internalType: 'uint256', name: 'quorumThreshold', type: 'uint256' }, - { internalType: 'uint256', name: 'timelockDelay', type: 'uint256' }, - { internalType: 'address[]', name: 'guardians', type: 'address[]' }, + { name: 'quorumThreshold', type: 'uint256', internalType: 'uint256' }, + { name: 'timelockDelay', type: 'uint256', internalType: 'uint256' }, + { name: 'guardians', type: 'address[]', internalType: 'address[]' }, ], - internalType: 'struct IGovernanceDeployer.GovParams', - name: 'govParams', - type: 'tuple', }, - { internalType: 'bytes32', name: 'deploymentNonce', type: 'bytes32' }, + { name: 'deploymentNonce', type: 'bytes32', internalType: 'bytes32' }, ], - name: 'deployGovernedStakingToken', outputs: [ { - internalType: 'contract StakingVault', name: 'stToken', type: 'address', + internalType: 'contract StakingVault', }, - { internalType: 'address', name: 'governor', type: 'address' }, - { internalType: 'address', name: 'timelock', type: 'address' }, + { name: 'governor', type: 'address', internalType: 'address' }, + { name: 'timelock', type: 'address', internalType: 'address' }, ], stateMutability: 'nonpayable', - type: 'function', }, { - inputs: [], + type: 'function', name: 'governorImplementation', - outputs: [{ internalType: 'address', name: '', type: 'address' }], + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view', - type: 'function', }, { - inputs: [], + type: 'function', name: 'timelockImplementation', - outputs: [{ internalType: 'address', name: '', type: 'address' }], + inputs: [], + outputs: [{ name: '', type: 'address', internalType: 'address' }], stateMutability: 'view', - type: 'function', }, { - inputs: [], + type: 'function', name: 'version', - outputs: [{ internalType: 'string', name: '', type: 'string' }], + inputs: [], + outputs: [{ name: '', type: 'string', internalType: 'string' }], stateMutability: 'pure', - type: 'function', + }, + { + type: 'event', + name: 'DeployedGovernance', + inputs: [ + { + name: 'stToken', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'governor', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'timelock', + type: 'address', + indexed: false, + internalType: 'address', + }, + ], + anonymous: false, + }, + { + type: 'event', + name: 'DeployedGovernedStakingToken', + inputs: [ + { + name: 'underlying', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'stToken', + type: 'address', + indexed: true, + internalType: 'address', + }, + { + name: 'governor', + type: 'address', + indexed: false, + internalType: 'address', + }, + { + name: 'timelock', + type: 'address', + indexed: false, + internalType: 'address', + }, + ], + anonymous: false, + }, + { type: 'error', name: 'FailedDeployment', inputs: [] }, + { + type: 'error', + name: 'InsufficientBalance', + inputs: [ + { name: 'balance', type: 'uint256', internalType: 'uint256' }, + { name: 'needed', type: 'uint256', internalType: 'uint256' }, + ], }, ] as const diff --git a/src/hooks/use-rebalance-basket-preview.ts b/src/hooks/use-rebalance-basket-preview.ts index 94e2f8d0e..fafe7a22b 100644 --- a/src/hooks/use-rebalance-basket-preview.ts +++ b/src/hooks/use-rebalance-basket-preview.ts @@ -1,10 +1,12 @@ import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' import { chainIdAtom } from '@/state/atoms' import { indexDTFAtom, indexDTFBasketAtom, indexDTFBasketSharesAtom, indexDTFRebalanceControlAtom, + indexDTFVersionAtom, } from '@/state/dtf/atoms' import { DecodedCalldata, Token } from '@/types' import { calculatePriceFromRange } from '@/utils' @@ -52,43 +54,83 @@ type Range = { high: bigint } +// V5 TokenRebalanceParams structure from the ABI +type TokenRebalanceParams = { + token: Address + weight: Range + price: { low: bigint; high: bigint } + maxAuctionSize: bigint + inRebalance: boolean +} + export const useDecodedRebalanceCalldata = ( calldata: Hex[] | undefined ): { data: RebalanceCall; calldata: DecodedCalldata } | undefined => { + const version = useAtomValue(indexDTFVersionAtom) + return useMemo(() => { // Rebalance calls is always only one if (calldata?.length !== 1) return undefined + const isV5 = version.startsWith('5') + try { - const decodedCalldata = getDecodedCalldata(dtfIndexAbiV4, calldata[0]) + // Try to decode with the appropriate ABI based on version + const abi = isV5 ? dtfIndexAbiV5 : dtfIndexAbiV4 + const decodedCalldata = getDecodedCalldata(abi, calldata[0]) if (decodedCalldata.signature !== 'startRebalance') return undefined - const data = decodedCalldata.data as unknown as [ - Address[], - Range[], - { low: bigint; high: bigint }[], - Range, - bigint, - bigint, - ] - - return { - data: { - tokens: data[0], - weights: data[1], - prices: data[2], - limits: data[3], - auctionLauncherWindow: data[4], - ttl: data[5], - } as RebalanceCall, - calldata: decodedCalldata, + if (isV5) { + // V5 format: startRebalance(TokenRebalanceParams[], limits, auctionLauncherWindow, ttl) + const data = decodedCalldata.data as unknown as [ + TokenRebalanceParams[], + Range, + bigint, + bigint, + ] + + const tokenParams = data[0] + + return { + data: { + tokens: tokenParams.map((t) => t.token), + weights: tokenParams.map((t) => t.weight), + prices: tokenParams.map((t) => t.price), + limits: data[1], + auctionLauncherWindow: data[2], + ttl: data[3], + } as RebalanceCall, + calldata: decodedCalldata, + } + } else { + // V4 format: startRebalance(tokens[], weights[], prices[], limits, auctionLauncherWindow, ttl) + const data = decodedCalldata.data as unknown as [ + Address[], + Range[], + { low: bigint; high: bigint }[], + Range, + bigint, + bigint, + ] + + return { + data: { + tokens: data[0], + weights: data[1], + prices: data[2], + limits: data[3], + auctionLauncherWindow: data[4], + ttl: data[5], + } as RebalanceCall, + calldata: decodedCalldata, + } } } catch (e) { console.error('Error decoding rebalance calldata', e) return undefined } - }, [JSON.stringify(calldata)]) + }, [JSON.stringify(calldata), version]) } const currentBasketMapAtom = atom | undefined>((get) => { diff --git a/src/hooks/usePrices.ts b/src/hooks/usePrices.ts index e041cf099..990dd5877 100644 --- a/src/hooks/usePrices.ts +++ b/src/hooks/usePrices.ts @@ -4,7 +4,7 @@ import { AssetPrice, DTFPrice } from '@/types/prices' import { RESERVE_API } from '@/utils/constants' import { useReadContracts } from 'wagmi' import { useMemo } from 'react' -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' type FolioResult = { status: 'success' | 'failure' diff --git a/src/state/chain/atoms/chainAtoms.ts b/src/state/chain/atoms/chainAtoms.ts index 913739663..1e9270a96 100644 --- a/src/state/chain/atoms/chainAtoms.ts +++ b/src/state/chain/atoms/chainAtoms.ts @@ -92,7 +92,7 @@ export const INDEX_DTF_SUBGRAPH_URL = { [ChainId.Mainnet]: 'https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-index-mainnet/prod/gn', [ChainId.Base]: - 'https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-index-base/prod/gn', + 'https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-index-base/1.8.6-test1/gn', [ChainId.Arbitrum]: 'https://api.goldsky.com/api/public/project_cmgzim3e100095np2gjnbh6ry/subgraphs/dtf-index-bsc/prod/gn', // TODO? maybe never [ChainId.BSC]: diff --git a/src/utils/addresses.ts b/src/utils/addresses.ts index 1d3b5b4f0..ed90b7ac6 100644 --- a/src/utils/addresses.ts +++ b/src/utils/addresses.ts @@ -32,18 +32,20 @@ export const FACADE_WRITE_ADDRESS: AddressMap = { [ChainId.Arbitrum]: '0xe2B652E538543d02f985A5E422645A704633956d', } +// v5.0 export const INDEX_DEPLOYER_ADDRESS: AddressMap = { - [ChainId.Mainnet]: '0xBE3B47587cEeff7D48008A0114f51cD571beC63A', - [ChainId.Base]: '0xA203AA351723cf943f91684e9F5eFcA7175Ae7EA', + [ChainId.Mainnet]: '0x4D201a6e5BF975E2CEE9e5cbDfc803C0Ff122073', + [ChainId.Base]: '0x3451fD177E9a8bB4Eb8271E627A804BD22A816F9', [ChainId.Arbitrum]: '0x', - [ChainId.BSC]: '0x5Bed18AcA50E6057E6658Fe8498004092EedCDcF', + [ChainId.BSC]: '0x72f87239981159ed23673012EE3806Ca6114AB2A', } +// v5.0 export const INDEX_GOVERNANCE_DEPLOYER_ADDRESS: AddressMap = { - [ChainId.Mainnet]: '0x5Bed18AcA50E6057E6658Fe8498004092EedCDcF', - [ChainId.Base]: '0x1A7D043c84fe781b6df046fEfCf673F71110208D', + [ChainId.Mainnet]: '0x72f87239981159ed23673012EE3806Ca6114AB2A', + [ChainId.Base]: '0xECA52a5BDBAd98a5B4B6B944C4C9cc636D4D7461', [ChainId.Arbitrum]: '0x', - [ChainId.BSC]: '0x270d928b9Ee38BAD93601D197256390b3c3C13Ec', + [ChainId.BSC]: '0xA7BC1265C37A8D285cd2B10c842Efb8415A7bF9f', } /** diff --git a/src/views/index-dtf/auctions/atoms.ts b/src/views/index-dtf/auctions/atoms.ts index dacb9503c..ceacb5e62 100644 --- a/src/views/index-dtf/auctions/atoms.ts +++ b/src/views/index-dtf/auctions/atoms.ts @@ -3,6 +3,7 @@ import { Token } from '@/types' import { atom } from 'jotai' import { governanceProposalsAtom } from '../governance/atoms' import { RebalanceMetrics } from './views/rebalance-list/hooks/use-rebalance-metrics' +import { indexDTFVersionAtom } from '@/state/dtf/atoms' export type Rebalance = { id: string @@ -29,6 +30,16 @@ export type RebalanceByProposal = { proposal: PartialProposal } +export const isV4Atom = atom((get) => { + const dtf = get(indexDTFVersionAtom) + return dtf === '4.0.0' +}) + +export const isV5Atom = atom((get) => { + const dtf = get(indexDTFVersionAtom) + return dtf === '5.0.0' +}) + export const rebalancesAtom = atom(undefined) export const currentProposalIdAtom = atom('') export const currentRebalanceAtom = atom( diff --git a/src/views/index-dtf/auctions/legacy/components/launch-trade-button.tsx b/src/views/index-dtf/auctions/legacy/components/launch-trade-button.tsx index d6bfc3689..19ce24966 100644 --- a/src/views/index-dtf/auctions/legacy/components/launch-trade-button.tsx +++ b/src/views/index-dtf/auctions/legacy/components/launch-trade-button.tsx @@ -1,45 +1,6 @@ -import dtfIndexAbi from '@/abis/dtf-index-abi' -import dtfIndexAbiV2 from '@/abis/dtf-index-abi-v2' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' -import { chainIdAtom } from '@/state/atoms' -import { indexDTFAtom, indexDTFVersionAtom } from '@/state/dtf/atoms' -import { getCurrentTime } from '@/utils' -import { atom, useAtomValue, useSetAtom } from 'jotai' -import { LoaderCircle } from 'lucide-react' -import { useEffect, useState } from 'react' -import { Address } from 'viem' -import { useWaitForTransactionReceipt, useWriteContract } from 'wagmi' -import { - AssetTrade, - dtfTradeMapAtom, - isAuctionLauncherAtom, - TRADE_STATE, -} from '../atoms' -import useAuctionLimits from '../hooks/useAuctionLimits' -import AuctionEjectSwitch from './auction-eject-switch' -import TradeButtonStates from './trade-button-states' - -// Updates the trade state! -export const updateTradeStateAtom = atom(null, (get, set, tradeId: string) => { - const dtf = get(indexDTFAtom) - const tradeMap = get(dtfTradeMapAtom) - - // Edge case if we are here, these exists - if (!tradeMap || !dtf) return - - const currentTime = getCurrentTime() - - set(dtfTradeMapAtom, { - ...tradeMap, - [tradeId]: { - ...tradeMap[tradeId], - state: TRADE_STATE.RUNNING, - start: currentTime, - end: currentTime + dtf.auctionLength, - }, - }) -}) +import { AssetTrade } from '../atoms' const LaunchTradeButton = ({ trade, @@ -48,100 +9,10 @@ const LaunchTradeButton = ({ trade: AssetTrade className?: string }) => { - const chainId = useAtomValue(chainIdAtom) - const version = useAtomValue(indexDTFVersionAtom) - const isAuctionLauncher = useAtomValue(isAuctionLauncherAtom) - const updateTradeState = useSetAtom(updateTradeStateAtom) - const { writeContract, isError, isPending, data } = useWriteContract() - const { isSuccess } = useWaitForTransactionReceipt({ - hash: data, - chainId, - }) - const isLoading = isPending || (!!data && !isSuccess && !isError) - const [ejectFully, setEjectFully] = useState(false) - const auctionLimits = useAuctionLimits(trade, ejectFully) - - const canLaunch = - trade.state === TRADE_STATE.AVAILABLE || - (isAuctionLauncher && - trade.state === TRADE_STATE.PENDING && - trade.availableRuns > 0 && - trade.boughtAmount < trade.buyLimitSpot && - auctionLimits) - const isDisabled = isLoading || !canLaunch - - // TODO: Show sonnet - useEffect(() => { - if (isSuccess) { - updateTradeState(trade.id) - } - }, [isSuccess]) - - const handleLaunch = () => { - if (getCurrentTime() >= trade.launchTimeout + 5) return - - // Trade id has the dtfId as prefix - const [dtfAddress, tradeId] = trade.id.split('-') - - // Open trade - if (isAuctionLauncher) { - if (!auctionLimits) return - - const { sellLimit, buyLimit, startPrice, endPrice } = auctionLimits - - writeContract({ - address: dtfAddress as Address, - abi: dtfIndexAbi, - functionName: 'openAuction', - args: [BigInt(tradeId), sellLimit, buyLimit, startPrice, endPrice], - }) - } else { - if (version === '1.0.0') { - writeContract({ - address: dtfAddress as Address, - abi: dtfIndexAbi, - functionName: 'openAuctionPermissionlessly', - args: [BigInt(tradeId)], - }) - } else { - writeContract({ - address: dtfAddress as Address, - abi: dtfIndexAbiV2, - functionName: 'openAuctionUnrestricted', - args: [BigInt(tradeId)], - }) - } - } - } - - if ( - trade.state !== TRADE_STATE.PENDING && - trade.state !== TRADE_STATE.AVAILABLE - ) { - return - } - return (
- -
) diff --git a/src/views/index-dtf/auctions/legacy/hooks/useAuctionLimits.ts b/src/views/index-dtf/auctions/legacy/hooks/useAuctionLimits.ts index 4342cb23f..cf2064543 100644 --- a/src/views/index-dtf/auctions/legacy/hooks/useAuctionLimits.ts +++ b/src/views/index-dtf/auctions/legacy/hooks/useAuctionLimits.ts @@ -1,4 +1,4 @@ -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import { getBasketTrackingDTF } from '@/lib/index-rebalance/get-basket-by-trades' import { openAuction } from '@/lib/index-rebalance/open-auction' import { Auction } from '@/lib/index-rebalance/types' diff --git a/src/views/index-dtf/auctions/views/rebalance/components/community-launch-auctions-button.tsx b/src/views/index-dtf/auctions/views/rebalance/components/community-launch-auctions-button.tsx index 819021b87..3dcb8b3ed 100644 --- a/src/views/index-dtf/auctions/views/rebalance/components/community-launch-auctions-button.tsx +++ b/src/views/index-dtf/auctions/views/rebalance/components/community-launch-auctions-button.tsx @@ -1,4 +1,4 @@ -import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' +import dtfIndexAbi from '@/abis/dtf-index-abi' import { Button } from '@/components/ui/button' import { indexDTFAtom } from '@/state/dtf/atoms' import { parseDuration, parseDurationShort } from '@/utils' @@ -90,9 +90,10 @@ const CommunityLaunchAuctionsButton = () => { try { setIsLaunching(true) setError(null) + writeContract({ address: dtf?.id, - abi: dtfIndexAbiV4, + abi: dtfIndexAbi, functionName: 'openAuctionUnrestricted', args: [BigInt(rebalance.rebalance.nonce)], }) diff --git a/src/views/index-dtf/auctions/views/rebalance/components/launch-auctions-button.tsx b/src/views/index-dtf/auctions/views/rebalance/components/launch-auctions-button.tsx index c10a402e7..a7d2e1f38 100644 --- a/src/views/index-dtf/auctions/views/rebalance/components/launch-auctions-button.tsx +++ b/src/views/index-dtf/auctions/views/rebalance/components/launch-auctions-button.tsx @@ -1,4 +1,4 @@ -import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' +import dtfIndexAbi from '@/abis/dtf-index-abi' import { Button } from '@/components/ui/button' import { indexDTFAtom, isHybridDTFAtom } from '@/state/dtf/atoms' import { parseDuration } from '@/utils' @@ -86,6 +86,7 @@ const LaunchAuctionsButton = () => { : rebalanceParams.initialWeights const [openAuctionArgs] = getRebalanceOpenAuction( + rebalanceParams.folioVersion, rebalance.rebalance.tokens, rebalanceParams.rebalance, rebalanceParams.supply, @@ -103,7 +104,7 @@ const LaunchAuctionsButton = () => { writeContract({ address: dtf?.id, - abi: dtfIndexAbiV4, + abi: dtfIndexAbi, functionName: 'openAuction', args: [ openAuctionArgs.rebalanceNonce, diff --git a/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-content.tsx b/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-content.tsx index 4fb8f5f9c..7afc3e57d 100644 --- a/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-content.tsx +++ b/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-content.tsx @@ -4,9 +4,11 @@ import { isHybridDTFAtom, } from '@/state/dtf/atoms' import { + FolioVersion, getStartRebalance, WeightRange, } from '@reserve-protocol/dtf-rebalance-lib' +import { StartRebalanceArgsPartial as StartRebalanceArgsPartialV4 } from '@reserve-protocol/dtf-rebalance-lib/dist/4.0.0/types' import { useAtomValue, useSetAtom } from 'jotai' import { areWeightsSavedAtom, @@ -24,6 +26,7 @@ import { import { calculateTargetShares, prepareRebalanceData } from './utils/weight-calculation-utils' import ManageWeightsHeader from './manage-weights-header' import ManageWeightsHero from './manage-weights-hero' +import { getRebalanceTokens, getRebalanceWeights } from '../../utils/transforms' const ManageWeightsContent = () => { const rebalanceParams = useRebalanceParams() @@ -49,8 +52,14 @@ const ManageWeightsContent = () => { if (!rebalanceParams || !rebalanceControl) return try { + // Get tokens using version-aware helper + const rebalanceTokens = getRebalanceTokens( + rebalanceParams.rebalance, + rebalanceParams.folioVersion + ) + const targetShares = calculateTargetShares( - rebalanceParams.rebalance.tokens, + rebalanceTokens, tokenMap, proposedUnits, basketItems, @@ -60,14 +69,18 @@ const ManageWeightsContent = () => { const rebalanceData = prepareRebalanceData( targetShares, - rebalanceParams.rebalance.tokens, + rebalanceTokens, tokenMap, basketItems, rebalanceParams.currentAssets, rebalanceParams.prices ) - const { weights } = getStartRebalance( + // Max auction size per token in USD (hardcoded to $1M) + const maxAuctionSizes = rebalanceData.tokens.map(() => 1_000_000) + + const startRebalanceArgs = getStartRebalance( + rebalanceParams.folioVersion, rebalanceParams.supply, rebalanceData.tokens, rebalanceData.assets, @@ -75,13 +88,26 @@ const ManageWeightsContent = () => { rebalanceData.targetBasket, rebalanceData.prices, rebalanceData.error, + maxAuctionSizes, rebalanceControl.weightControl, isHybridDTF ) + // Get weights - for v5 we need to extract from tokens, for v4 use weights directly + let weights: WeightRange[] + if (rebalanceParams.folioVersion === FolioVersion.V5) { + // For v5, getRebalanceWeights helper extracts from rebalance, but here we need from startRebalanceArgs + // The v5 returns tokens: TokenRebalanceParams[], so we extract weights from there + const argsV5 = startRebalanceArgs as { tokens: Array<{ weight: WeightRange }> } + weights = argsV5.tokens.map((t) => t.weight) + } else { + const argsV4 = startRebalanceArgs as StartRebalanceArgsPartialV4 + weights = argsV4.weights + } + // Create weights map ensuring correct token order const weightsMap: Record = {} - rebalanceParams.rebalance.tokens.forEach((tokenAddress, index) => { + rebalanceTokens.forEach((tokenAddress, index) => { const address = tokenAddress.toLowerCase() weightsMap[address] = weights[index] @@ -94,7 +120,10 @@ const ManageWeightsContent = () => { }) // Validate weights distribution - const totalWeight = weights.reduce((sum, w) => sum + Number(w.spot), 0) + const totalWeight = weights.reduce( + (sum: number, w: WeightRange) => sum + Number(w.spot), + 0 + ) console.log('Total weight (should be close to 1):', totalWeight) setSavedWeights(weightsMap) diff --git a/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-view.tsx b/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-view.tsx index 2e856c421..13b73e19a 100644 --- a/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-view.tsx +++ b/src/views/index-dtf/auctions/views/rebalance/components/manage-weights/manage-weights-view.tsx @@ -14,6 +14,7 @@ import { BasketItem, } from '@/components/index-basket-setup' import ManageWeightsContent from './manage-weights-content' +import { getRebalanceTokens, getRebalanceWeights } from '../../utils/transforms' const ManageWeightsView = () => { const [showView] = useAtom(showManageWeightsViewAtom) @@ -30,17 +31,27 @@ const ManageWeightsView = () => { return { initialBasket: basket, priceMap: prices } } + // Get tokens and weights using version-aware helpers + const rebalanceTokens = getRebalanceTokens( + rebalanceParams.rebalance, + rebalanceParams.folioVersion + ) + const rebalanceWeights = getRebalanceWeights( + rebalanceParams.rebalance, + rebalanceParams.folioVersion + ) + // Always calculate proposed units from rebalance weights for comparison let proposedUnitsFromWeights: Record = {} - if (rebalanceParams.rebalance.weights) { + if (rebalanceWeights.length > 0) { // Weights are in D27 format (tok/BU - tokens per basket unit) // For a DTF with 1 basket unit = 1 DTF token (typical case), // the weight directly represents tokens per DTF token in D27 format - rebalanceParams.rebalance.tokens.forEach((tokenAddress, index) => { + rebalanceTokens.forEach((tokenAddress, index) => { const address = tokenAddress.toLowerCase() const token = tokenMap[address] - const weight = rebalanceParams.rebalance.weights[index] + const weight = rebalanceWeights[index] if (token && weight) { // The spot weight in D27 represents tokens per basket unit @@ -53,12 +64,15 @@ const ManageWeightsView = () => { // whole token units = weight / 10^decimals / 10^9 // Which is equivalent to: formatUnits(weight, decimals + 9) - proposedUnitsFromWeights[address] = formatUnits(spotWeight, token.decimals + 9) + proposedUnitsFromWeights[address] = formatUnits( + spotWeight, + token.decimals + 9 + ) } }) } - const tokenData = rebalanceParams.rebalance.tokens + const tokenData = rebalanceTokens .map((tokenAddress) => { const address = tokenAddress.toLowerCase() const token = tokenMap[address] @@ -80,14 +94,14 @@ const ManageWeightsView = () => { tokenData.map((d) => d.price) ) - rebalanceParams.rebalance.tokens.forEach((tokenAddress, index) => { + rebalanceTokens.forEach((tokenAddress, index) => { const address = tokenAddress.toLowerCase() const token = tokenMap[address] const assets = rebalanceParams.currentAssets[address] || 0n const totalSupply = rebalanceParams.supply || 1n // TODO @audit - const folio = assets * 10n ** 18n / totalSupply + const folio = (assets * 10n ** 18n) / totalSupply if (token) { const folioValue = formatUnits(folio, token.decimals) diff --git a/src/views/index-dtf/auctions/views/rebalance/components/rebalance-action.tsx b/src/views/index-dtf/auctions/views/rebalance/components/rebalance-action.tsx index 97642e6ff..b703556d6 100644 --- a/src/views/index-dtf/auctions/views/rebalance/components/rebalance-action.tsx +++ b/src/views/index-dtf/auctions/views/rebalance/components/rebalance-action.tsx @@ -1,5 +1,5 @@ import { isAuctionLauncherAtom, isHybridDTFAtom } from '@/state/dtf/atoms' -import { AuctionRound } from '@reserve-protocol/dtf-rebalance-lib' +import { AuctionRound, WeightRange } from '@reserve-protocol/dtf-rebalance-lib' import { useAtomValue, useSetAtom } from 'jotai' import { useMemo } from 'react' import { @@ -16,6 +16,7 @@ import LaunchAuctionsButton from './launch-auctions-button' import RebalanceActionOverview from './rebalance-action-overview' import { Button } from '@/components/ui/button' import { PencilRuler } from 'lucide-react' +import { getRebalanceWeights } from '../utils/transforms' const ROUND_TITLE = { [AuctionRound.EJECT]: 'Remove Tokens', @@ -29,10 +30,17 @@ const RoundDescription = () => { const tokenMap = useAtomValue(rebalanceTokenMapAtom) const description = useMemo(() => { + if (!params) return 'Buy/sell tokens to move closer to proposed weights.' + + // Get weights using version-aware helper + const rebalanceWeights = getRebalanceWeights( + params.rebalance, + params.folioVersion + ) + const removal = metrics?.round === AuctionRound.EJECT && - !!params && - params?.rebalance.weights.find((w) => w.spot === 0n) + rebalanceWeights.find((w: WeightRange) => w.spot === 0n) if (!removal) return 'Buy/sell tokens to move closer to proposed weights.' diff --git a/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-current-data.ts b/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-current-data.ts index e51ddd046..e10e2e7be 100644 --- a/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-current-data.ts +++ b/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-current-data.ts @@ -1,31 +1,57 @@ import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' -import { indexDTFAtom } from '@/state/dtf/atoms' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' +import { indexDTFAtom, indexDTFVersionAtom } from '@/state/dtf/atoms' +import { FolioVersion } from '@reserve-protocol/dtf-rebalance-lib' +import { Rebalance as RebalanceV4 } from '@reserve-protocol/dtf-rebalance-lib/dist/4.0.0/types' +import { Rebalance as RebalanceV5 } from '@reserve-protocol/dtf-rebalance-lib/dist/types' import { useAtomValue } from 'jotai' +import { useEffect, useMemo } from 'react' import { useReadContracts } from 'wagmi' import { mapToAssets } from '../utils' import { isAuctionOngoingAtom } from '../atoms' -import { useEffect } from 'react' +import { + extractBidsEnabledFromV5, + getFolioVersion, + transformV4Rebalance, + transformV5Rebalance, +} from '../utils/transforms' + +export type RebalanceCurrentData = { + supply: bigint + rebalance: RebalanceV4 | RebalanceV5 + currentAssets: Record + folioVersion: FolioVersion + bidsEnabled: boolean +} const useRebalanceCurrentData = () => { const dtf = useAtomValue(indexDTFAtom) + const versionString = useAtomValue(indexDTFVersionAtom) const isAuctionOngoing = useAtomValue(isAuctionOngoingAtom) + const folioVersion = useMemo( + () => getFolioVersion(versionString), + [versionString] + ) + const isV5 = folioVersion === FolioVersion.V5 + const abi = isV5 ? dtfIndexAbiV5 : dtfIndexAbiV4 + const result = useReadContracts({ contracts: [ { - abi: dtfIndexAbiV4, + abi, address: dtf?.id, functionName: 'totalSupply', chainId: dtf?.chainId, }, { - abi: dtfIndexAbiV4, + abi, address: dtf?.id, functionName: 'getRebalance', chainId: dtf?.chainId, }, { - abi: dtfIndexAbiV4, + abi, address: dtf?.id, functionName: 'totalAssets', args: [], @@ -35,24 +61,42 @@ const useRebalanceCurrentData = () => { allowFailure: false, query: { enabled: !!dtf?.id, - select: (data) => { - const [supply, rebalance, [assets, balances]] = data + select: (data): RebalanceCurrentData => { + const [supply, rebalanceRaw, assetsData] = data as unknown as [ + bigint, + readonly unknown[], + readonly [readonly `0x${string}`[], readonly bigint[]], + ] + + const [assets, balances] = assetsData + + // Transform based on version + const rebalance = isV5 + ? transformV5Rebalance(rebalanceRaw) + : transformV4Rebalance(rebalanceRaw) + + // v5: extract from getRebalance response (index 5), v4: always enabled + const bidsEnabled = isV5 + ? extractBidsEnabledFromV5(rebalanceRaw) + : true return { supply, rebalance, currentAssets: mapToAssets(assets, balances), + folioVersion, + bidsEnabled, } }, }, }) + // Refetch every 10s during active auction useEffect(() => { if (isAuctionOngoing) { const interval = setInterval(() => { result.refetch() - }, 10000) // 10 seconds - + }, 10000) return () => clearInterval(interval) } }, [isAuctionOngoing, result.refetch]) diff --git a/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-initial-data.ts b/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-initial-data.ts index ef5bdcd27..e8c58cfa6 100644 --- a/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-initial-data.ts +++ b/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-initial-data.ts @@ -1,18 +1,29 @@ import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' -import { indexDTFAtom } from '@/state/dtf/atoms' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' +import { indexDTFAtom, indexDTFVersionAtom } from '@/state/dtf/atoms' +import { FolioVersion } from '@reserve-protocol/dtf-rebalance-lib' import { useAtomValue } from 'jotai' +import { useMemo } from 'react' import { useReadContracts } from 'wagmi' import { currentRebalanceAtom } from '../../../atoms' import { mapToAssets } from '../utils' +import { getFolioVersion } from '../utils/transforms' const useRebalanceInitialData = () => { const dtf = useAtomValue(indexDTFAtom) const rebalance = useAtomValue(currentRebalanceAtom) + const versionString = useAtomValue(indexDTFVersionAtom) + + const folioVersion = useMemo( + () => getFolioVersion(versionString), + [versionString] + ) + const abi = folioVersion === FolioVersion.V5 ? dtfIndexAbiV5 : dtfIndexAbiV4 return useReadContracts({ contracts: [ { - abi: dtfIndexAbiV4, + abi, address: dtf?.id, functionName: 'totalSupply', chainId: dtf?.chainId, @@ -20,7 +31,7 @@ const useRebalanceInitialData = () => { }, { - abi: dtfIndexAbiV4, + abi, address: dtf?.id, functionName: 'totalAssets', chainId: dtf?.chainId, @@ -32,7 +43,12 @@ const useRebalanceInitialData = () => { query: { enabled: !!rebalance?.proposal.creationBlock && !!dtf?.id, select: (data) => { - const [supply, [assets, balances]] = data + const [supply, assetsData] = data as [ + bigint, + readonly [readonly `0x${string}`[], readonly bigint[]], + ] + + const [assets, balances] = assetsData return { supply, diff --git a/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-params.ts b/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-params.ts index 992cbbd69..a3069ccc7 100644 --- a/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-params.ts +++ b/src/views/index-dtf/auctions/views/rebalance/hooks/use-rebalance-params.ts @@ -1,4 +1,5 @@ import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' import useAssetPricesWithSnapshot, { TokenPriceWithSnapshot, } from '@/hooks/use-asset-prices-with-snapshot' @@ -6,19 +7,27 @@ import { indexDTFAtom, indexDTFBasketAtom, indexDTFRebalanceControlAtom, + indexDTFVersionAtom, isHybridDTFAtom, } from '@/state/dtf/atoms' import { Token, Volatility } from '@/types' import { calculatePriceFromRange } from '@/utils' -import { - Rebalance, - WeightRange, -} from '@reserve-protocol/dtf-rebalance-lib/dist/types' +import { FolioVersion, WeightRange } from '@reserve-protocol/dtf-rebalance-lib' +import { Rebalance as RebalanceV4 } from '@reserve-protocol/dtf-rebalance-lib/dist/4.0.0/types' +import { Rebalance as RebalanceV5 } from '@reserve-protocol/dtf-rebalance-lib/dist/types' import { useAtomValue } from 'jotai' import { useMemo } from 'react' import { useReadContract } from 'wagmi' import { currentRebalanceAtom } from '../../../atoms' import { originalRebalanceWeightsAtom, rebalanceAuctionsAtom } from '../atoms' +import { + getFolioVersion, + getRebalancePrices, + getRebalanceTokens, + getRebalanceWeights, + transformV4Rebalance, + transformV5Rebalance, +} from '../utils/transforms' import useRebalanceCurrentData from './use-rebalance-current-data' import useRebalanceInitialData from './use-rebalance-initial-data' import useRebalancePriceVolatility from './use-rebalance-price-volatility' @@ -26,7 +35,7 @@ import useRebalancePriceVolatility from './use-rebalance-price-volatility' export type RebalanceParams = { supply: bigint initialSupply: bigint - rebalance: Rebalance + rebalance: RebalanceV4 | RebalanceV5 currentAssets: Record initialAssets: Record initialPrices: Record @@ -34,6 +43,8 @@ export type RebalanceParams = { prices: TokenPriceWithSnapshot tokenPriceVolatility: Record isTrackingDTF: boolean + folioVersion: FolioVersion + bidsEnabled: boolean } const useRebalanceParams = () => { @@ -45,6 +56,13 @@ const useRebalanceParams = () => { const auctions = useAtomValue(rebalanceAuctionsAtom) const originalWeights = useAtomValue(originalRebalanceWeightsAtom) const tokenPriceVolatility = useRebalancePriceVolatility() + const versionString = useAtomValue(indexDTFVersionAtom) + + const folioVersion = useMemo( + () => getFolioVersion(versionString), + [versionString] + ) + const abi = folioVersion === FolioVersion.V5 ? dtfIndexAbiV5 : dtfIndexAbiV4 const rebalanceTokens = useMemo(() => { if (!rebalance || !basket) return [] @@ -65,8 +83,8 @@ const useRebalanceParams = () => { const { data: prices } = useAssetPricesWithSnapshot(rebalanceTokens) const { data: currentRebalanceData } = useRebalanceCurrentData() const { data: initialRebalanceData } = useRebalanceInitialData() - const { data: initialRebalance } = useReadContract({ - abi: dtfIndexAbiV4, + const { data: initialRebalanceRaw } = useReadContract({ + abi, address: dtf?.id, functionName: 'getRebalance', chainId: dtf?.chainId, @@ -84,7 +102,7 @@ const useRebalanceParams = () => { !prices || !rebalanceControl || !rebalance || - !initialRebalance || + !initialRebalanceRaw || !tokenPriceVolatility ) return undefined @@ -97,26 +115,26 @@ const useRebalanceParams = () => { }, {} as Record ) + + // Transform historical rebalance using version-aware helpers + const historicalRebalance = + folioVersion === FolioVersion.V5 + ? transformV5Rebalance(initialRebalanceRaw as readonly unknown[]) + : transformV4Rebalance(initialRebalanceRaw as readonly unknown[]) + + const historicalTokens = getRebalanceTokens(historicalRebalance, folioVersion) + const historicalWeights = getRebalanceWeights(historicalRebalance, folioVersion) + const historicalPrices = getRebalancePrices(historicalRebalance, folioVersion) + const initialPrices: Record = {} let initialWeights: Record = {} - for (let i = 0; i < initialRebalance[1].length; i++) { - const token = initialRebalance[1][i].toLowerCase() + for (let i = 0; i < historicalTokens.length; i++) { + const token = historicalTokens[i].toLowerCase() const decimals = tokenMap[token].decimals - initialPrices[token] = calculatePriceFromRange( - { - low: initialRebalance[3][i].low, - high: initialRebalance[3][i].high, - }, - decimals - ) - - initialWeights[token] = { - low: initialRebalance[2][i].low, - spot: initialRebalance[2][i].spot, - high: initialRebalance[2][i].high, - } + initialPrices[token] = calculatePriceFromRange(historicalPrices[i], decimals) + initialWeights[token] = historicalWeights[i] } // Use historical weights for subsequent auctions in hybrid DTFs @@ -125,7 +143,7 @@ const useRebalanceParams = () => { } return { - initialSupply: initialSupply, + initialSupply, initialPrices, initialWeights, initialAssets, @@ -134,23 +152,14 @@ const useRebalanceParams = () => { prices, tokenPriceVolatility, isTrackingDTF: !rebalanceControl.weightControl, - rebalance: { - nonce: currentRebalanceData.rebalance[0], - tokens: currentRebalanceData.rebalance[1], - weights: currentRebalanceData.rebalance[2], - initialPrices: currentRebalanceData.rebalance[3], - inRebalance: currentRebalanceData.rebalance[4], - limits: currentRebalanceData.rebalance[5], - startedAt: currentRebalanceData.rebalance[6], - restrictedUntil: currentRebalanceData.rebalance[7], - availableUntil: currentRebalanceData.rebalance[8], - priceControl: currentRebalanceData.rebalance[9], - } as Rebalance, + rebalance: currentRebalanceData.rebalance, + folioVersion: currentRebalanceData.folioVersion, + bidsEnabled: currentRebalanceData.bidsEnabled, } as RebalanceParams }, [ currentRebalanceData, initialRebalanceData, - initialRebalance, + initialRebalanceRaw, prices, rebalance, rebalanceControl, @@ -158,6 +167,7 @@ const useRebalanceParams = () => { auctions.length, originalWeights, tokenPriceVolatility, + folioVersion, ]) } diff --git a/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-historical-weights.tsx b/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-historical-weights.tsx index 036f63552..300b4742b 100644 --- a/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-historical-weights.tsx +++ b/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-historical-weights.tsx @@ -1,20 +1,35 @@ import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' -import { indexDTFAtom, isHybridDTFAtom } from '@/state/dtf/atoms' -import { WeightRange } from '@reserve-protocol/dtf-rebalance-lib/dist/types' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' +import { indexDTFAtom, indexDTFVersionAtom, isHybridDTFAtom } from '@/state/dtf/atoms' +import { FolioVersion, WeightRange } from '@reserve-protocol/dtf-rebalance-lib' import { useAtomValue, useSetAtom } from 'jotai' -import { useEffect } from 'react' +import { useEffect, useMemo } from 'react' import { useReadContract } from 'wagmi' import { originalRebalanceWeightsAtom, rebalanceAuctionsAtom } from '../atoms' +import { + getFolioVersion, + getRebalanceTokens, + getRebalanceWeights, + transformV4Rebalance, + transformV5Rebalance, +} from '../utils/transforms' const RebalanceHistoricalWeightsUpdater = () => { const dtf = useAtomValue(indexDTFAtom) const auctions = useAtomValue(rebalanceAuctionsAtom) const isHybridDTF = useAtomValue(isHybridDTFAtom) const setOriginalWeights = useSetAtom(originalRebalanceWeightsAtom) + const versionString = useAtomValue(indexDTFVersionAtom) + + const folioVersion = useMemo( + () => getFolioVersion(versionString), + [versionString] + ) + const abi = folioVersion === FolioVersion.V5 ? dtfIndexAbiV5 : dtfIndexAbiV4 // Query for historical weights at first auction block for hybrid DTFs const result = useReadContract({ - abi: dtfIndexAbiV4, + abi, address: dtf?.id, functionName: 'getRebalance', chainId: dtf?.chainId, @@ -34,20 +49,21 @@ const RebalanceHistoricalWeightsUpdater = () => { // Store historical weights for hybrid DTFs useEffect(() => { if (result.data && isHybridDTF) { - const { data: historicalRebalance } = result + const historicalRebalance = + folioVersion === FolioVersion.V5 + ? transformV5Rebalance(result.data as readonly unknown[]) + : transformV4Rebalance(result.data as readonly unknown[]) + + const tokens = getRebalanceTokens(historicalRebalance, folioVersion) + const weightList = getRebalanceWeights(historicalRebalance, folioVersion) const weights: Record = {} - for (let i = 0; i < historicalRebalance[1].length; i++) { - const token = historicalRebalance[1][i].toLowerCase() - weights[token] = { - low: historicalRebalance[2][i].low, - spot: historicalRebalance[2][i].spot, - high: historicalRebalance[2][i].high, - } + for (let i = 0; i < tokens.length; i++) { + weights[tokens[i].toLowerCase()] = weightList[i] } setOriginalWeights(weights) } - }, [result.data, isHybridDTF, setOriginalWeights]) + }, [result.data, isHybridDTF, folioVersion, setOriginalWeights]) return null } diff --git a/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-metrics-updater.tsx b/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-metrics-updater.tsx index 6472e7412..ecbea15ad 100644 --- a/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-metrics-updater.tsx +++ b/src/views/index-dtf/auctions/views/rebalance/updaters/rebalance-metrics-updater.tsx @@ -46,6 +46,7 @@ const RebalanceMetricsUpdater = () => { prices, isTrackingDTF, tokenPriceVolatility, + folioVersion, } = params // Use saved weights for hybrid DTFs on first auction if available @@ -59,6 +60,7 @@ const RebalanceMetricsUpdater = () => { // First, calculate metrics with the current percent to get auctionSize const [, initialMetrics] = getRebalanceOpenAuction( + folioVersion, currentRebalance.rebalance.tokens, rebalance, supply, @@ -92,6 +94,7 @@ const RebalanceMetricsUpdater = () => { const [, rebalanceMetrics] = effectivePercent !== rebalancePercent ? getRebalanceOpenAuction( + folioVersion, currentRebalance.rebalance.tokens, rebalance, supply, diff --git a/src/views/index-dtf/auctions/views/rebalance/utils/get-rebalance-open-auction.ts b/src/views/index-dtf/auctions/views/rebalance/utils/get-rebalance-open-auction.ts index ff4d7264a..28f44a73b 100644 --- a/src/views/index-dtf/auctions/views/rebalance/utils/get-rebalance-open-auction.ts +++ b/src/views/index-dtf/auctions/views/rebalance/utils/get-rebalance-open-auction.ts @@ -1,16 +1,20 @@ import { TokenPriceWithSnapshot } from '@/hooks/use-asset-prices-with-snapshot' import { Token, Volatility } from '@/types' import { + FolioVersion, getOpenAuction, getTargetBasket, - Rebalance, WeightRange, } from '@reserve-protocol/dtf-rebalance-lib' +import { Rebalance as RebalanceV4 } from '@reserve-protocol/dtf-rebalance-lib/dist/4.0.0/types' +import { Rebalance as RebalanceV5 } from '@reserve-protocol/dtf-rebalance-lib/dist/types' import { AUCTION_PRICE_VOLATILITY } from '../atoms' +import { getRebalanceTokens } from './transforms' function getRebalanceOpenAuction( + version: FolioVersion, tokens: Token[], - rebalance: Rebalance, + rebalance: RebalanceV4 | RebalanceV5, supply: bigint, initialSupply: bigint, currentAssets: Record, @@ -31,7 +35,10 @@ function getRebalanceOpenAuction( {} as Record ) - // Lets start by creating a map of the basket tokens + // Use version-aware helper to get token addresses + const rebalanceTokens = getRebalanceTokens(rebalance, version) + + // Build arrays for the library call const decimals: bigint[] = [] const currentPrices: number[] = [] const snapshotPrices: number[] = [] @@ -40,7 +47,7 @@ function getRebalanceOpenAuction( const currentFolioAssets: bigint[] = [] const weights: WeightRange[] = [] - rebalance.tokens.forEach((token) => { + rebalanceTokens.forEach((token) => { const lowercasedAddress = token.toLowerCase() const tokenDecimals = tokenMap[lowercasedAddress].decimals @@ -65,7 +72,9 @@ function getRebalanceOpenAuction( decimals ) + // Pass version to the library function return getOpenAuction( + version, rebalance, supply, initialSupply, diff --git a/src/views/index-dtf/auctions/views/rebalance/utils/transforms.ts b/src/views/index-dtf/auctions/views/rebalance/utils/transforms.ts new file mode 100644 index 000000000..cb9a34baa --- /dev/null +++ b/src/views/index-dtf/auctions/views/rebalance/utils/transforms.ts @@ -0,0 +1,152 @@ +import { FolioVersion, PriceControl } from '@reserve-protocol/dtf-rebalance-lib' +import { Rebalance as RebalanceV4 } from '@reserve-protocol/dtf-rebalance-lib/dist/4.0.0/types' +import { Rebalance as RebalanceV5 } from '@reserve-protocol/dtf-rebalance-lib/dist/types' + +// ------------------------------------------------------------------- +// Version Detection +// ------------------------------------------------------------------- + +/** + * Convert version string to FolioVersion enum + * v5.x.x → V5, everything else → V4 + */ +export function getFolioVersion(versionString: string): FolioVersion { + return versionString.startsWith('5') ? FolioVersion.V5 : FolioVersion.V4 +} + +// ------------------------------------------------------------------- +// Contract Response → Library Type Transformations +// ------------------------------------------------------------------- + +/** + * Transform v4 getRebalance() contract response to library RebalanceV4 type + * + * v4 returns tuple: [nonce, tokens[], weights[], initialPrices[], inRebalance[], + * limits, startedAt, restrictedUntil, availableUntil, priceControl] + */ +export function transformV4Rebalance(raw: readonly unknown[]): RebalanceV4 { + return { + nonce: raw[0] as bigint, + tokens: raw[1] as string[], + weights: raw[2] as Array<{ low: bigint; spot: bigint; high: bigint }>, + initialPrices: raw[3] as Array<{ low: bigint; high: bigint }>, + inRebalance: raw[4] as boolean[], + limits: raw[5] as { low: bigint; spot: bigint; high: bigint }, + startedAt: raw[6] as bigint, + restrictedUntil: raw[7] as bigint, + availableUntil: raw[8] as bigint, + priceControl: raw[9] as PriceControl, + } +} + +/** + * Transform v5 getRebalance() contract response to library RebalanceV5 type + * + * v5 returns tuple: [nonce, priceControl, tokens[], limits, timestamps, bidsEnabled] + * where tokens[] is TokenRebalanceParams[] with nested weight/price/maxAuctionSize/inRebalance + */ +export function transformV5Rebalance(raw: readonly unknown[]): RebalanceV5 { + const tokenParams = raw[2] as Array<{ + token: string + weight: { low: bigint; spot: bigint; high: bigint } + price: { low: bigint; high: bigint } + maxAuctionSize: bigint + inRebalance: boolean + }> + + const timestamps = raw[4] as { + startedAt: bigint + restrictedUntil: bigint + availableUntil: bigint + } + + return { + nonce: raw[0] as bigint, + priceControl: raw[1] as PriceControl, + tokens: tokenParams.map((t) => ({ + token: t.token, + weight: { low: t.weight.low, spot: t.weight.spot, high: t.weight.high }, + price: { low: t.price.low, high: t.price.high }, + maxAuctionSize: t.maxAuctionSize, + inRebalance: t.inRebalance, + })), + limits: raw[3] as { low: bigint; spot: bigint; high: bigint }, + timestamps: { + startedAt: timestamps.startedAt, + restrictedUntil: timestamps.restrictedUntil, + availableUntil: timestamps.availableUntil, + }, + } +} + +// ------------------------------------------------------------------- +// Unified Accessors (version-agnostic helpers) +// ------------------------------------------------------------------- + +/** Get token addresses from either v4 or v5 rebalance */ +export function getRebalanceTokens( + rebalance: RebalanceV4 | RebalanceV5, + version: FolioVersion +): string[] { + if (version === FolioVersion.V5) { + return (rebalance as RebalanceV5).tokens.map((t) => t.token) + } + return (rebalance as RebalanceV4).tokens +} + +/** Get weights from either v4 or v5 rebalance */ +export function getRebalanceWeights( + rebalance: RebalanceV4 | RebalanceV5, + version: FolioVersion +): Array<{ low: bigint; spot: bigint; high: bigint }> { + if (version === FolioVersion.V5) { + return (rebalance as RebalanceV5).tokens.map((t) => t.weight) + } + return (rebalance as RebalanceV4).weights +} + +/** Get prices from either v4 or v5 rebalance */ +export function getRebalancePrices( + rebalance: RebalanceV4 | RebalanceV5, + version: FolioVersion +): Array<{ low: bigint; high: bigint }> { + if (version === FolioVersion.V5) { + return (rebalance as RebalanceV5).tokens.map((t) => t.price) + } + return (rebalance as RebalanceV4).initialPrices +} + +/** Get inRebalance flags from either v4 or v5 rebalance */ +export function getRebalanceInRebalance( + rebalance: RebalanceV4 | RebalanceV5, + version: FolioVersion +): boolean[] { + if (version === FolioVersion.V5) { + return (rebalance as RebalanceV5).tokens.map((t) => t.inRebalance) + } + return (rebalance as RebalanceV4).inRebalance +} + +/** Get timestamps from either v4 or v5 rebalance */ +export function getRebalanceTimestamps( + rebalance: RebalanceV4 | RebalanceV5, + version: FolioVersion +): { startedAt: bigint; restrictedUntil: bigint; availableUntil: bigint } { + if (version === FolioVersion.V5) { + return (rebalance as RebalanceV5).timestamps + } + const r = rebalance as RebalanceV4 + return { + startedAt: r.startedAt, + restrictedUntil: r.restrictedUntil, + availableUntil: r.availableUntil, + } +} + +/** + * Extract bidsEnabled from v5 getRebalance() raw response + * v5 getRebalance returns: [nonce, priceControl, tokens[], limits, timestamps, bidsEnabled_] + */ +export function extractBidsEnabledFromV5(raw: readonly unknown[]): boolean { + return raw[5] as boolean +} diff --git a/src/views/index-dtf/deploy/form-fields.ts b/src/views/index-dtf/deploy/form-fields.ts index f2c060ed9..f9fd523af 100644 --- a/src/views/index-dtf/deploy/form-fields.ts +++ b/src/views/index-dtf/deploy/form-fields.ts @@ -46,7 +46,7 @@ export const dtfDeploySteps: Record = { ], }, auctions: { - fields: ['auctionLength', 'weightControl'], + fields: ['auctionLength', 'weightControl', 'bidsEnabled'], }, roles: { fields: ['guardians', 'brandManagers', 'auctionLaunchers'], @@ -169,6 +169,7 @@ export const DeployFormSchema = z .min(0) .max(45, 'Auction length must not exceed 45 minutes'), weightControl: z.boolean(), + bidsEnabled: z.boolean(), guardians: z.array( z .string() @@ -426,6 +427,7 @@ export const dtfDeployDefaultValues = { additionalRevenueRecipients: [], auctionLength: 30, weightControl: false, + bidsEnabled: true, guardians: [], brandManagers: [], auctionLaunchers: [], diff --git a/src/views/index-dtf/deploy/steps/auctions/auctions-form.tsx b/src/views/index-dtf/deploy/steps/auctions/auctions-form.tsx index 0728f5dba..e094d6251 100644 --- a/src/views/index-dtf/deploy/steps/auctions/auctions-form.tsx +++ b/src/views/index-dtf/deploy/steps/auctions/auctions-form.tsx @@ -1,6 +1,6 @@ import { Switch } from '@/components/ui/switch' import { humanizeMinutes } from '@/utils' -import { Hourglass, LandPlot } from 'lucide-react' +import { HandCoins, Hourglass, LandPlot } from 'lucide-react' import { Controller, useFormContext } from 'react-hook-form' import ToggleGroupWithCustom from '../../components/toggle-group-with-custom' @@ -51,11 +51,41 @@ const WeightControl = () => { ) } +const BidsEnabled = () => { + const { control } = useFormContext() + + return ( +
+
+
+ +
+ +
+
Permissionless Bids
+
+ Enable bids directly via the folio. +
+
+ + ( + + )} + /> +
+
+ ) +} + const AuctionsForm = () => (
{TOGGLE_FORMS.map((form) => ( ))} +
) diff --git a/src/views/index-dtf/deploy/steps/confirm-deploy/manual/components/confirm-manual-deploy-button.tsx b/src/views/index-dtf/deploy/steps/confirm-deploy/manual/components/confirm-manual-deploy-button.tsx index de7bdf2f1..8118e53bf 100644 --- a/src/views/index-dtf/deploy/steps/confirm-deploy/manual/components/confirm-manual-deploy-button.tsx +++ b/src/views/index-dtf/deploy/steps/confirm-deploy/manual/components/confirm-manual-deploy-button.tsx @@ -63,6 +63,7 @@ type FolioFlags = { weightControl: boolean priceControl: number } + bidsEnabled: boolean } type GovernanceConfig = { @@ -156,6 +157,7 @@ const txAtom = atom< weightControl: formData.weightControl, // false -> tracking / true -> native priceControl: PriceControl.PARTIAL, }, + bidsEnabled: formData.bidsEnabled, // true for cowswap } const guardians = formData.guardians.filter(Boolean) as Address[] diff --git a/src/views/index-dtf/deploy/steps/confirm-deploy/simple/atoms.ts b/src/views/index-dtf/deploy/steps/confirm-deploy/simple/atoms.ts index 91eb22a47..c4ec20d8c 100644 --- a/src/views/index-dtf/deploy/steps/confirm-deploy/simple/atoms.ts +++ b/src/views/index-dtf/deploy/steps/confirm-deploy/simple/atoms.ts @@ -103,6 +103,7 @@ export const zapDeployPayloadAtom = atom< weightControl: formData.weightControl, // false -> tracking / true -> native priceControl: PriceControl.PARTIAL, }, + bidsEnabled: formData.bidsEnabled, // true for cowswap } // Ungoverned DTF diff --git a/src/views/index-dtf/governance/components/proposal-preview/atoms.ts b/src/views/index-dtf/governance/components/proposal-preview/atoms.ts index 45b0ad7ec..ff0b1f782 100644 --- a/src/views/index-dtf/governance/components/proposal-preview/atoms.ts +++ b/src/views/index-dtf/governance/components/proposal-preview/atoms.ts @@ -15,6 +15,10 @@ import { spellAbi as v4SpellAbi, spellAddress as v4SpellAddress, } from '../../views/propose/upgrade-banners/propose-v4-upgrade' +import { + spellAbi as v5SpellAbi, + spellAddress as v5SpellAddress, +} from '../../views/propose/upgrade-banners/propose-v5-upgrade' export const dtfAbiMapppingAtom = atom((get) => { const dtf = get(indexDTFAtom) @@ -56,6 +60,10 @@ export const dtfAbiMapppingAtom = atom((get) => { abiMapping[v4SpellAddress[dtf.chainId].toLowerCase()] = v4SpellAbi } + if (v5SpellAddress[dtf.chainId]) { + abiMapping[v5SpellAddress[dtf.chainId].toLowerCase()] = v5SpellAbi + } + return abiMapping }) @@ -103,5 +111,9 @@ export const dtfContractAliasAtom = atom((get) => { aliasMapping[v4SpellAddress[dtf.chainId].toLowerCase()] = 'V4 Upgrade Spell' } + if (v5SpellAddress[dtf.chainId]) { + aliasMapping[v5SpellAddress[dtf.chainId].toLowerCase()] = 'V5 Upgrade Spell' + } + return aliasMapping }) diff --git a/src/views/index-dtf/governance/components/proposal-preview/rebalance-preview/legacy-basket-proposal-preview.tsx b/src/views/index-dtf/governance/components/proposal-preview/rebalance-preview/legacy-basket-proposal-preview.tsx index ee80d9588..6cc48b082 100644 --- a/src/views/index-dtf/governance/components/proposal-preview/rebalance-preview/legacy-basket-proposal-preview.tsx +++ b/src/views/index-dtf/governance/components/proposal-preview/rebalance-preview/legacy-basket-proposal-preview.tsx @@ -1,4 +1,4 @@ -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import TokenLogo from '@/components/token-logo' import { Button } from '@/components/ui/button' import { diff --git a/src/views/index-dtf/governance/views/propose/basket/atoms.ts b/src/views/index-dtf/governance/views/propose/basket/atoms.ts index bd0edd431..f3786acc6 100644 --- a/src/views/index-dtf/governance/views/propose/basket/atoms.ts +++ b/src/views/index-dtf/governance/views/propose/basket/atoms.ts @@ -1,7 +1,10 @@ -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import dtfIndexAbiV2 from '@/abis/dtf-index-abi-v2' import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' import { getStartRebalance } from '@reserve-protocol/dtf-rebalance-lib' +import { StartRebalanceArgsPartial as StartRebalanceArgsPartialV4 } from '@reserve-protocol/dtf-rebalance-lib/dist/4.0.0/types' +import { StartRebalanceArgsPartial as StartRebalanceArgsPartialV5 } from '@reserve-protocol/dtf-rebalance-lib/dist/types' import { getAuctions } from '@/lib/index-rebalance/get-auctions' import { getCurrentBasket } from '@/lib/index-rebalance/utils' import { @@ -473,6 +476,9 @@ const REBALANCE_PRICE_VOLATILITY: Record = { export const tokenPriceVolatilityAtom = atom>({}) +// Max auction size per token in USD (hardcoded to $1M) +const MAX_AUCTION_SIZE_USD = 1_000_000 + export const basketProposalCalldatasAtom = atom((get) => { const isSingleton = get(isSingletonRebalanceAtom) @@ -495,6 +501,10 @@ export const basketProposalCalldatasAtom = atom((get) => { const proposedShares = get(proposedSharesAtom) const priceMap = get(priceMapAtom) const tokenPriceVolatility = get(tokenPriceVolatilityAtom) + const version = get(indexDTFVersionAtom) + + // Determine folio version (4 and 5 are the enum values) + const folioVersion = version.startsWith('5') ? 5 : 4 if ( !isConfirmed || @@ -513,61 +523,75 @@ export const basketProposalCalldatasAtom = atom((get) => { const _prices: number[] = [] const error: number[] = [] const balances: bigint[] = [] - - let index = 0 + const maxAuctionSizes: number[] = [] for (const asset of Object.keys(proposedBasket)) { + const assetLower = asset.toLowerCase() tokens.push(asset as Address) decimals.push(BigInt(proposedBasket[asset].token.decimals)) currentBasket.push(parseUnits(proposedBasket[asset].currentShares, 16)) - balances.push(dtfBalances[asset] || 0n) + balances.push(dtfBalances[asset] || dtfBalances[assetLower] || 0n) if ( (isUnitBasket || basketInputType === 'unit' || isHybridDTF) && derivedProposedShares ) { - targetBasket.push(derivedProposedShares[asset]) + targetBasket.push(derivedProposedShares[asset] || derivedProposedShares[assetLower]) } else { - targetBasket.push(parseUnits(proposedShares[asset], 16)) + targetBasket.push(parseUnits(proposedShares[asset] || proposedShares[assetLower], 16)) } - _prices.push(priceMap[asset] ?? 0) + _prices.push(priceMap[asset] ?? priceMap[assetLower] ?? 0) error.push( - REBALANCE_PRICE_VOLATILITY[tokenPriceVolatility[asset] || 'high'] + REBALANCE_PRICE_VOLATILITY[tokenPriceVolatility[asset] || tokenPriceVolatility[assetLower] || 'high'] ) - - index++ + maxAuctionSizes.push(MAX_AUCTION_SIZE_USD) } - try { - const { weights, prices, limits } = getStartRebalance( - supply, - tokens, - balances, - decimals, - targetBasket, - _prices, - error, - rebalanceControl?.weightControl, - isHybridDTF - ) + const startRebalanceArgs = getStartRebalance( + folioVersion, + supply, + tokens, + balances, + decimals, + targetBasket, + _prices, + error, + maxAuctionSizes, + rebalanceControl?.weightControl, + isHybridDTF + ) + // Encode with version-appropriate ABI and arguments + if (folioVersion === 5) { + const argsV5 = startRebalanceArgs as StartRebalanceArgsPartialV5 + return [ + encodeFunctionData({ + abi: dtfIndexAbiV5, + functionName: 'startRebalance', + args: [ + argsV5.tokens as any, + argsV5.limits as any, + BigInt(ttl.auctionLauncherWindow), + BigInt(ttl.ttl), + ], + }), + ] + } else { + const argsV4 = startRebalanceArgs as StartRebalanceArgsPartialV4 return [ encodeFunctionData({ abi: dtfIndexAbiV4, functionName: 'startRebalance', args: [ tokens, - weights, - prices, - limits, + argsV4.weights as any, + argsV4.prices as any, + argsV4.limits as any, BigInt(ttl.auctionLauncherWindow), BigInt(ttl.ttl), ], }), ] - } catch (e) { - console.error('Error getting rebalance', e) - return undefined } }) diff --git a/src/views/index-dtf/governance/views/propose/basket/components/proposal-basket-preview.tsx b/src/views/index-dtf/governance/views/propose/basket/components/proposal-basket-preview.tsx index 33834e5e0..91342b2f9 100644 --- a/src/views/index-dtf/governance/views/propose/basket/components/proposal-basket-preview.tsx +++ b/src/views/index-dtf/governance/views/propose/basket/components/proposal-basket-preview.tsx @@ -1,4 +1,4 @@ -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import TokenLogo from '@/components/token-logo' import { Button } from '@/components/ui/button' import { diff --git a/src/views/index-dtf/governance/views/propose/basket/updater.tsx b/src/views/index-dtf/governance/views/propose/basket/updater.tsx index db322241c..6ae81c87f 100644 --- a/src/views/index-dtf/governance/views/propose/basket/updater.tsx +++ b/src/views/index-dtf/governance/views/propose/basket/updater.tsx @@ -1,4 +1,4 @@ -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import { chainIdAtom } from '@/state/atoms' import { indexDTFBasketAtom, diff --git a/src/views/index-dtf/governance/views/propose/index.tsx b/src/views/index-dtf/governance/views/propose/index.tsx index 5908f62a0..36cc2390d 100644 --- a/src/views/index-dtf/governance/views/propose/index.tsx +++ b/src/views/index-dtf/governance/views/propose/index.tsx @@ -11,6 +11,7 @@ import { } from 'lucide-react' import { Link } from 'react-router-dom' import ProposeV4Upgrade from './upgrade-banners/propose-v4-upgrade' +import ProposeV5Upgrade from './upgrade-banners/propose-v5-upgrade' const proposalTypes = [ { @@ -93,6 +94,7 @@ const ProposalTypeSelection = () => {
+
diff --git a/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-governance-spell-31-03-2025.tsx b/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-governance-spell-31-03-2025.tsx index 73bd8aae4..65366deb9 100644 --- a/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-governance-spell-31-03-2025.tsx +++ b/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-governance-spell-31-03-2025.tsx @@ -1,5 +1,5 @@ import dtfAdminAbi from '@/abis/dtf-admin-abi' -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import DTFIndexGovernance from '@/abis/dtf-index-governance' import stakingVaultAbi from '@/abis/dtf-index-staking-vault' import { Button } from '@/components/ui/button' diff --git a/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-v4-upgrade.tsx b/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-v4-upgrade.tsx index 90bc2e892..7ec4b5575 100644 --- a/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-v4-upgrade.tsx +++ b/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-v4-upgrade.tsx @@ -1,5 +1,5 @@ import dtfAdminAbi from '@/abis/dtf-admin-abi' -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import DTFIndexGovernance from '@/abis/dtf-index-governance' import { Button } from '@/components/ui/button' import { getProposalState, PartialProposal } from '@/lib/governance' diff --git a/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-v5-upgrade.tsx b/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-v5-upgrade.tsx new file mode 100644 index 000000000..5cc81d9da --- /dev/null +++ b/src/views/index-dtf/governance/views/propose/upgrade-banners/propose-v5-upgrade.tsx @@ -0,0 +1,204 @@ +import dtfAdminAbi from '@/abis/dtf-admin-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' +import DTFIndexGovernance from '@/abis/dtf-index-governance' +import { Button } from '@/components/ui/button' +import { getProposalState, PartialProposal } from '@/lib/governance' +import { chainIdAtom } from '@/state/atoms' +import { indexDTFAtom, indexDTFVersionAtom } from '@/state/dtf/atoms' +import { getCurrentTime } from '@/utils' +import { ChainId } from '@/utils/chains' +import { PROPOSAL_STATES } from '@/utils/constants' +import { useAtomValue, useSetAtom } from 'jotai' +import { AlertCircle, Loader2 } from 'lucide-react' +import { useCallback, useEffect } from 'react' +import { encodeFunctionData, getAddress, pad } from 'viem' +import { useWaitForTransactionReceipt, useWriteContract } from 'wagmi' +import { governanceProposalsAtom, refetchTokenAtom } from '../../../atoms' +import { useIsProposeAllowed } from '../../../hooks/use-is-propose-allowed' +import { toast } from 'sonner' + +export const spellAbi = [ + { inputs: [], stateMutability: 'nonpayable', type: 'constructor' }, + { + inputs: [ + { internalType: 'contract Folio', name: 'folio', type: 'address' }, + { + internalType: 'contract FolioProxyAdmin', + name: 'proxyAdmin', + type: 'address', + }, + ], + name: 'cast', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'version', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'pure', + type: 'function', + }, +] as const + +export const spellAddress: Record = { + [ChainId.Mainnet]: getAddress('0x044B6F685FB8D0c3fd56D92FCBE5F0Ad947d2D53'), + [ChainId.Base]: getAddress('0x04B3eD311C68dfB9649D9faf695115F23DcbB540'), + [ChainId.BSC]: getAddress('0xe8e67a366e5166c442B6D376ADc772b93CdE7825'), +} + +const UPGRADE_FOLIO_MESSAGE = 'Release 5.0.0 upgrade' + +type SpellUpgradeProps = { + refetch: () => void +} + +const ProposeBanner = ({ refetch }: SpellUpgradeProps) => { + const dtf = useAtomValue(indexDTFAtom) + const chainId = useAtomValue(chainIdAtom) + const spell = spellAddress[chainId] + + const { writeContract, data, isPending } = useWriteContract() + + const { isSuccess } = useWaitForTransactionReceipt({ + hash: data, + chainId, + }) + + const isReady = + dtf?.id && + dtf?.proxyAdmin && + dtf?.ownerGovernance?.id && + dtf?.tradingGovernance?.id + const proposalAvailable = !!spell + + const handlePropose = () => { + if (!dtf || !spell || !dtf.ownerGovernance) return + + writeContract({ + address: dtf.ownerGovernance.id, + abi: DTFIndexGovernance, + functionName: 'propose', + args: [ + [dtf.id, dtf.proxyAdmin, spell], + [0n, 0n, 0n], + [ + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'grantRole', + args: [pad('0x0', { size: 32 }), spell], + }), + encodeFunctionData({ + abi: dtfAdminAbi, + functionName: 'transferOwnership', + args: [spell], + }), + encodeFunctionData({ + abi: spellAbi, + functionName: 'cast', + args: [dtf.id, dtf.proxyAdmin], + }), + ], + UPGRADE_FOLIO_MESSAGE, + ], + }) + } + + useEffect(() => { + if (isSuccess) { + setTimeout(() => { + toast('Proposal created!', { + description: 'DTF V5.0.0 upgrade proposal created', + icon: '🎉', + }) + refetch() + }, 10000) + } + }, [isSuccess]) + + if (!proposalAvailable) { + return null + } + + return ( +
+
+ +
+

New version available

+

+ Release 5.0.0 introduces improved rebalancing with per-token auction + size limits and the ability to disable bids for individual tokens. + See docs.reserve.org for more details. +

+
+
+ +
+ ) +} + +const validProposalExists = ( + proposals: PartialProposal[], + description: string +): boolean => { + const states = [ + PROPOSAL_STATES.PENDING, + PROPOSAL_STATES.ACTIVE, + PROPOSAL_STATES.SUCCEEDED, + PROPOSAL_STATES.QUEUED, + PROPOSAL_STATES.EXECUTED, + ] + return proposals.some((p) => { + if (p.description !== description) { + return false + } + + const pState = getProposalState(p) + + if (pState.state === PROPOSAL_STATES.EXPIRED) { + return false + } + + return states.includes(pState.state) + }) +} + +export default function ProposeV5Upgrade() { + const { isProposeAllowed } = useIsProposeAllowed() + const proposals = useAtomValue(governanceProposalsAtom) + const version = useAtomValue(indexDTFVersionAtom) + const setRefetchToken = useSetAtom(refetchTokenAtom) + + const refetch = useCallback(() => { + setRefetchToken(getCurrentTime()) + }, [setRefetchToken]) + + // Show banner for v4.x DTFs (4.0.0 and 4.0.1) + const isUpgradeable = version.startsWith('4.') + + if (!isProposeAllowed || !proposals || !isUpgradeable) return null + + const existsFolioUpgrade = validProposalExists( + proposals, + UPGRADE_FOLIO_MESSAGE + ) + + if (existsFolioUpgrade) { + return null + } + + return +} diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts index d1ee27675..b99bfefe8 100644 --- a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts @@ -1,6 +1,7 @@ -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import dtfIndexAbiV2 from '@/abis/dtf-index-abi-v2' import dtfIndexAbiV4 from '@/abis/dtf-index-abi-v4' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' import timelockAbi from '@/abis/Timelock' import { indexDTFAtom, @@ -31,6 +32,7 @@ import { export const selectedSectionAtom = atom(undefined) // Change detection atoms +export const tokenNameChangeAtom = atom(undefined) export const mandateChangeAtom = atom(undefined) export const rolesChangesAtom = atom<{ @@ -54,9 +56,16 @@ export const auctionLengthChangeAtom = atom(undefined) export const weightControlChangeAtom = atom(undefined) +export const bidsEnabledChangeAtom = atom(undefined) + export const governanceChangesAtom = atom({}) // Has changes atoms for easy checking +export const hasTokenNameChangeAtom = atom((get) => { + const change = get(tokenNameChangeAtom) + return change !== undefined +}) + export const hasMandateChangeAtom = atom((get) => { const change = get(mandateChangeAtom) return change !== undefined @@ -95,6 +104,11 @@ export const hasWeightControlChangeAtom = atom((get) => { return change !== undefined }) +export const hasBidsEnabledChangeAtom = atom((get) => { + const change = get(bidsEnabledChangeAtom) + return change !== undefined +}) + export const hasGovernanceChangesAtom = atom((get) => { const changes = get(governanceChangesAtom) return !!( @@ -137,27 +151,29 @@ export const isFormValidAtom = atom(true) export const isProposalValidAtom = atom((get) => { const removedBasketTokens = get(removedBasketTokensAtom) + const hasTokenNameChange = get(hasTokenNameChangeAtom) const hasMandateChange = get(hasMandateChangeAtom) const hasRolesChanges = get(hasRolesChangesAtom) const hasRevenueDistributionChanges = get(hasRevenueDistributionChangesAtom) const hasDtfRevenueChanges = get(hasDtfRevenueChangesAtom) const hasAuctionLengthChange = get(hasAuctionLengthChangeAtom) const hasWeightControlChange = get(hasWeightControlChangeAtom) + const hasBidsEnabledChange = get(hasBidsEnabledChangeAtom) const hasGovernanceChanges = get(hasGovernanceChangesAtom) const isFormValid = get(isFormValidAtom) const hasChanges = removedBasketTokens.length > 0 || + hasTokenNameChange || hasMandateChange || hasRolesChanges || hasRevenueDistributionChanges || hasDtfRevenueChanges || hasAuctionLengthChange || hasWeightControlChange || + hasBidsEnabledChange || hasGovernanceChanges - console.log('has changes', hasChanges) - return hasChanges }) @@ -169,7 +185,7 @@ export const proposalDescriptionAtom = atom(undefined) export const currentQuorumPercentageAtom = atom((get) => { const dtf = get(indexDTFAtom) if (!dtf?.ownerGovernance) return 0 - + const { quorumNumerator, quorumDenominator } = dtf.ownerGovernance return (Number(quorumNumerator) / Number(quorumDenominator)) * 100 }) @@ -182,481 +198,519 @@ const BRAND_MANAGER_ROLE = const AUCTION_LAUNCHER_ROLE = '0x13ff1b2625181b311f257c723b5e6d366eb318b212d9dd694c48fcf227659df5' as const -export const dtfSettingsProposalDataAtom = atom((get) => { - const isConfirmed = get(isProposalConfirmedAtom) - const indexDTF = get(indexDTFAtom) - const version = get(indexDTFVersionAtom) - const removedBasketTokens = get(removedBasketTokensAtom) - const mandateChange = get(mandateChangeAtom) - const rolesChanges = get(rolesChangesAtom) - const revenueDistributionChanges = get(revenueDistributionChangesAtom) - const dtfRevenueChanges = get(dtfRevenueChangesAtom) - const auctionLengthChange = get(auctionLengthChangeAtom) - const weightControlChange = get(weightControlChangeAtom) - const rebalanceControl = get(indexDTFRebalanceControlAtom) - const governanceChanges = get(governanceChangesAtom) - const feeRecipients = get(feeRecipientsAtom) - - if (!isConfirmed || !indexDTF) return undefined - - const calldatas: Hex[] = [] - const targets: Address[] = [] - - // 1. Remove dust tokens - if (removedBasketTokens.length > 0) { - const isV2 = version === '2.0.0' - for (const token of removedBasketTokens) { - if (isV2) { +export const dtfSettingsProposalDataAtom = atom( + (get) => { + const isConfirmed = get(isProposalConfirmedAtom) + const indexDTF = get(indexDTFAtom) + const version = get(indexDTFVersionAtom) + const removedBasketTokens = get(removedBasketTokensAtom) + const tokenNameChange = get(tokenNameChangeAtom) + const mandateChange = get(mandateChangeAtom) + const rolesChanges = get(rolesChangesAtom) + const revenueDistributionChanges = get(revenueDistributionChangesAtom) + const dtfRevenueChanges = get(dtfRevenueChangesAtom) + const auctionLengthChange = get(auctionLengthChangeAtom) + const weightControlChange = get(weightControlChangeAtom) + const bidsEnabledChange = get(bidsEnabledChangeAtom) + const rebalanceControl = get(indexDTFRebalanceControlAtom) + const governanceChanges = get(governanceChangesAtom) + const feeRecipients = get(feeRecipientsAtom) + + if (!isConfirmed || !indexDTF) return undefined + + const calldatas: Hex[] = [] + const targets: Address[] = [] + + // 1. Remove dust tokens + if (removedBasketTokens.length > 0) { + const isV2 = version === '2.0.0' + for (const token of removedBasketTokens) { + if (isV2) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbiV2, + functionName: 'setDustAmount', + args: [token.address, BIGINT_MAX], + }) + ) + targets.push(indexDTF.id as Address) + } calldatas.push( encodeFunctionData({ abi: dtfIndexAbiV2, - functionName: 'setDustAmount', - args: [token.address, BIGINT_MAX], + functionName: 'removeFromBasket', + args: [token.address], }) ) targets.push(indexDTF.id as Address) } + } + + // 2. Set token name (v5+) + if (tokenNameChange !== undefined) { calldatas.push( encodeFunctionData({ - abi: dtfIndexAbiV2, - functionName: 'removeFromBasket', - args: [token.address], + abi: dtfIndexAbiV5, + functionName: 'setName', + args: [tokenNameChange], }) ) targets.push(indexDTF.id as Address) } - } - // 2. Set mandate - if (mandateChange !== undefined) { - calldatas.push( - encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'setMandate', - args: [mandateChange], - }) - ) - targets.push(indexDTF.id as Address) - } - - // 3. Handle role changes - if (rolesChanges.guardians && indexDTF.ownerGovernance?.timelock?.id) { - const currentGuardians = indexDTF.ownerGovernance?.timelock?.guardians || [] - const newGuardians = rolesChanges.guardians - const timelockAddress = indexDTF.ownerGovernance.timelock.id as Address - - // Revoke removed guardians - const removedGuardians = currentGuardians.filter( - (addr) => - !newGuardians.some( - (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() - ) - ) - for (const guardian of removedGuardians) { + // 3. Set mandate + if (mandateChange !== undefined) { calldatas.push( encodeFunctionData({ - abi: timelockAbi, - functionName: 'revokeRole', - args: [GUARDIAN_ROLE, guardian], + abi: dtfIndexAbi, + functionName: 'setMandate', + args: [mandateChange], }) ) - targets.push(timelockAddress) + targets.push(indexDTF.id as Address) } - // Grant new guardians - const addedGuardians = newGuardians.filter( - (addr) => - !currentGuardians.some( - (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() + // 3. Handle role changes + if (rolesChanges.guardians && indexDTF.ownerGovernance?.timelock?.id) { + const currentGuardians = + indexDTF.ownerGovernance?.timelock?.guardians || [] + const newGuardians = rolesChanges.guardians + const timelockAddress = indexDTF.ownerGovernance.timelock.id as Address + + // Revoke removed guardians + const removedGuardians = currentGuardians.filter( + (addr) => + !newGuardians.some( + (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() + ) + ) + for (const guardian of removedGuardians) { + calldatas.push( + encodeFunctionData({ + abi: timelockAbi, + functionName: 'revokeRole', + args: [GUARDIAN_ROLE, guardian], + }) ) - ) - for (const guardian of addedGuardians) { - calldatas.push( - encodeFunctionData({ - abi: timelockAbi, - functionName: 'grantRole', - args: [GUARDIAN_ROLE, guardian], - }) + targets.push(timelockAddress) + } + + // Grant new guardians + const addedGuardians = newGuardians.filter( + (addr) => + !currentGuardians.some( + (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() + ) + ) + for (const guardian of addedGuardians) { + calldatas.push( + encodeFunctionData({ + abi: timelockAbi, + functionName: 'grantRole', + args: [GUARDIAN_ROLE, guardian], + }) + ) + targets.push(timelockAddress) + } + } else if (rolesChanges.guardians) { + // Fallback to DTF contract if timelock not available + const currentGuardians = + indexDTF.ownerGovernance?.timelock?.guardians || [] + const newGuardians = rolesChanges.guardians + + // Revoke removed guardians + const removedGuardians = currentGuardians.filter( + (addr) => + !newGuardians.some( + (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() + ) + ) + for (const guardian of removedGuardians) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'revokeRole', + args: [GUARDIAN_ROLE, guardian], + }) + ) + targets.push(indexDTF.id as Address) + } + + // Grant new guardians + const addedGuardians = newGuardians.filter( + (addr) => + !currentGuardians.some( + (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() + ) ) - targets.push(timelockAddress) + for (const guardian of addedGuardians) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'grantRole', + args: [GUARDIAN_ROLE, guardian], + }) + ) + targets.push(indexDTF.id as Address) + } } - } else if (rolesChanges.guardians) { - // Fallback to DTF contract if timelock not available - const currentGuardians = indexDTF.ownerGovernance?.timelock?.guardians || [] - const newGuardians = rolesChanges.guardians - - // Revoke removed guardians - const removedGuardians = currentGuardians.filter( - (addr) => - !newGuardians.some( - (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() + + if (rolesChanges.brandManagers) { + const currentBrandManagers = indexDTF.brandManagers || [] + const newBrandManagers = rolesChanges.brandManagers + + // Revoke removed brand managers + const removedBrandManagers = currentBrandManagers.filter( + (addr) => + !newBrandManagers.some( + (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() + ) + ) + for (const brandManager of removedBrandManagers) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'revokeRole', + args: [BRAND_MANAGER_ROLE, brandManager], + }) ) - ) - for (const guardian of removedGuardians) { - calldatas.push( - encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'revokeRole', - args: [GUARDIAN_ROLE, guardian], - }) + targets.push(indexDTF.id as Address) + } + + // Grant new brand managers + const addedBrandManagers = newBrandManagers.filter( + (addr) => + !currentBrandManagers.some( + (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() + ) ) - targets.push(indexDTF.id as Address) + for (const brandManager of addedBrandManagers) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'grantRole', + args: [BRAND_MANAGER_ROLE, brandManager], + }) + ) + targets.push(indexDTF.id as Address) + } } - // Grant new guardians - const addedGuardians = newGuardians.filter( - (addr) => - !currentGuardians.some( - (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() + if (rolesChanges.auctionLaunchers) { + const currentAuctionLaunchers = indexDTF.auctionLaunchers || [] + const newAuctionLaunchers = rolesChanges.auctionLaunchers + + // Revoke removed auction launchers + const removedAuctionLaunchers = currentAuctionLaunchers.filter( + (addr) => + !newAuctionLaunchers.some( + (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() + ) + ) + for (const auctionLauncher of removedAuctionLaunchers) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'revokeRole', + args: [AUCTION_LAUNCHER_ROLE, auctionLauncher], + }) + ) + targets.push(indexDTF.id as Address) + } + + // Grant new auction launchers + const addedAuctionLaunchers = newAuctionLaunchers.filter( + (addr) => + !currentAuctionLaunchers.some( + (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() + ) + ) + for (const auctionLauncher of addedAuctionLaunchers) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'grantRole', + args: [AUCTION_LAUNCHER_ROLE, auctionLauncher], + }) ) - ) - for (const guardian of addedGuardians) { + targets.push(indexDTF.id as Address) + } + } + + // 4. Set mint fee + if (dtfRevenueChanges.mintFee !== undefined) { calldatas.push( encodeFunctionData({ abi: dtfIndexAbi, - functionName: 'grantRole', - args: [GUARDIAN_ROLE, guardian], + functionName: 'setMintFee', + args: [parseEther((dtfRevenueChanges.mintFee / 100).toString())], // Convert percentage to 18 decimal precision }) ) targets.push(indexDTF.id as Address) } - } - - if (rolesChanges.brandManagers) { - const currentBrandManagers = indexDTF.brandManagers || [] - const newBrandManagers = rolesChanges.brandManagers - // Revoke removed brand managers - const removedBrandManagers = currentBrandManagers.filter( - (addr) => - !newBrandManagers.some( - (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() - ) - ) - for (const brandManager of removedBrandManagers) { + // 4b. Set TVL fee + if (dtfRevenueChanges.tvlFee !== undefined) { calldatas.push( encodeFunctionData({ abi: dtfIndexAbi, - functionName: 'revokeRole', - args: [BRAND_MANAGER_ROLE, brandManager], + functionName: 'setTVLFee', + args: [parseEther((dtfRevenueChanges.tvlFee / 100).toString())], // Convert percentage to decimal }) ) targets.push(indexDTF.id as Address) } - // Grant new brand managers - const addedBrandManagers = newBrandManagers.filter( - (addr) => - !currentBrandManagers.some( - (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() - ) - ) - for (const brandManager of addedBrandManagers) { + // 5. Set auction length + if (auctionLengthChange !== undefined) { calldatas.push( encodeFunctionData({ abi: dtfIndexAbi, - functionName: 'grantRole', - args: [BRAND_MANAGER_ROLE, brandManager], + functionName: 'setAuctionLength', + args: [BigInt(auctionLengthChange * 60)], // Convert minutes to seconds }) ) targets.push(indexDTF.id as Address) } - } - - if (rolesChanges.auctionLaunchers) { - const currentAuctionLaunchers = indexDTF.auctionLaunchers || [] - const newAuctionLaunchers = rolesChanges.auctionLaunchers - // Revoke removed auction launchers - const removedAuctionLaunchers = currentAuctionLaunchers.filter( - (addr) => - !newAuctionLaunchers.some( - (newAddr) => newAddr.toLowerCase() === addr.toLowerCase() - ) - ) - for (const auctionLauncher of removedAuctionLaunchers) { + // 5b. Set rebalance control (weight control) + if (weightControlChange !== undefined && rebalanceControl) { + // Keep the current priceControl value, only change weightControl calldatas.push( encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'revokeRole', - args: [AUCTION_LAUNCHER_ROLE, auctionLauncher], + abi: dtfIndexAbiV4, + functionName: 'setRebalanceControl', + args: [ + { + weightControl: weightControlChange, + priceControl: rebalanceControl.priceControl, + }, + ], }) ) targets.push(indexDTF.id as Address) } - // Grant new auction launchers - const addedAuctionLaunchers = newAuctionLaunchers.filter( - (addr) => - !currentAuctionLaunchers.some( - (currAddr) => currAddr.toLowerCase() === addr.toLowerCase() - ) - ) - for (const auctionLauncher of addedAuctionLaunchers) { + // 5c. Set bids enabled (v5+) + if (bidsEnabledChange !== undefined) { calldatas.push( encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'grantRole', - args: [AUCTION_LAUNCHER_ROLE, auctionLauncher], + abi: dtfIndexAbiV5, + functionName: 'setBidsEnabled', + args: [bidsEnabledChange], }) ) targets.push(indexDTF.id as Address) } - } - // 4. Set mint fee - if (dtfRevenueChanges.mintFee !== undefined) { - calldatas.push( - encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'setMintFee', - args: [parseEther((dtfRevenueChanges.mintFee / 100).toString())], // Convert percentage to 18 decimal precision - }) - ) - targets.push(indexDTF.id as Address) - } + // 6. Set fee recipients + if ( + revenueDistributionChanges.governanceShare !== undefined || + revenueDistributionChanges.deployerShare !== undefined || + revenueDistributionChanges.additionalRecipients !== undefined + ) { + if (!feeRecipients) return undefined + + // Calculate new fee recipients array similar to deploy logic + const newFeeRecipients: { recipient: Address; portion: bigint }[] = [] + + // Calculate using the same logic as calculateRevenueDistribution + const chainId = get(chainIdAtom) + const platformFee = getPlatformFee(chainId) + + // Convert from actual percentage (including platform fee) to contract percentage (excluding platform fee) + // User input: actual % of total revenue -> Contract needs: % of non-platform portion + // Example BSC: User inputs 67% -> Contract needs 100% (67% is 100% of the 67% non-platform portion) + const calculateShare = (sharePercentage: number) => { + // Convert actual percentage to fraction of non-platform portion + const actualFraction = sharePercentage / 100 + const nonPlatformFraction = (100 - platformFee) / 100 + const contractFraction = actualFraction / nonPlatformFraction + return parseEther(contractFraction.toString()) + } - // 4b. Set TVL fee - if (dtfRevenueChanges.tvlFee !== undefined) { - calldatas.push( - encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'setTVLFee', - args: [parseEther((dtfRevenueChanges.tvlFee / 100).toString())], // Convert percentage to decimal - }) - ) - targets.push(indexDTF.id as Address) - } + // Get current values + const governanceShare = + revenueDistributionChanges.governanceShare ?? + feeRecipients.governanceShare + const deployerShare = + revenueDistributionChanges.deployerShare ?? feeRecipients.deployerShare + const additionalRecipients = + revenueDistributionChanges.additionalRecipients ?? + feeRecipients.externalRecipients + + // Build recipients array (excluding last one for now) + const tempRecipients: { recipient: Address; portion: bigint }[] = [] + + // Additional recipients + if (additionalRecipients && additionalRecipients.length > 0) { + for (const recipient of additionalRecipients) { + if (recipient.share > 0 && recipient.address) { + tempRecipients.push({ + recipient: recipient.address as Address, + portion: calculateShare(recipient.share), + }) + } + } + } - // 5. Set auction length - if (auctionLengthChange !== undefined) { - calldatas.push( - encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'setAuctionLength', - args: [BigInt(auctionLengthChange * 60)], // Convert minutes to seconds - }) - ) - targets.push(indexDTF.id as Address) - } + // Deployer share + if (deployerShare > 0) { + tempRecipients.push({ + recipient: indexDTF.deployer as Address, + portion: calculateShare(deployerShare), + }) + } - // 5b. Set rebalance control (weight control) - if (weightControlChange !== undefined && rebalanceControl) { - // Keep the current priceControl value, only change weightControl - calldatas.push( - encodeFunctionData({ - abi: dtfIndexAbiV4, - functionName: 'setRebalanceControl', - args: [{ - weightControl: weightControlChange, - priceControl: rebalanceControl.priceControl - }], - }) - ) - targets.push(indexDTF.id as Address) - } + // Governance share + if (governanceShare > 0 && indexDTF.stToken) { + tempRecipients.push({ + recipient: indexDTF.stToken.id as Address, + portion: calculateShare(governanceShare), + }) + } - // 6. Set fee recipients - if ( - revenueDistributionChanges.governanceShare !== undefined || - revenueDistributionChanges.deployerShare !== undefined || - revenueDistributionChanges.additionalRecipients !== undefined - ) { - if (!feeRecipients) return undefined - - // Calculate new fee recipients array similar to deploy logic - const newFeeRecipients: { recipient: Address; portion: bigint }[] = [] - - // Calculate using the same logic as calculateRevenueDistribution - const chainId = get(chainIdAtom) - const platformFee = getPlatformFee(chainId) - - // Convert from actual percentage (including platform fee) to contract percentage (excluding platform fee) - // User input: actual % of total revenue -> Contract needs: % of non-platform portion - // Example BSC: User inputs 67% -> Contract needs 100% (67% is 100% of the 67% non-platform portion) - const calculateShare = (sharePercentage: number) => { - // Convert actual percentage to fraction of non-platform portion - const actualFraction = sharePercentage / 100 - const nonPlatformFraction = (100 - platformFee) / 100 - const contractFraction = actualFraction / nonPlatformFraction - return parseEther(contractFraction.toString()) - } + // Calculate sum and adjust last recipient to avoid rounding errors + if (tempRecipients.length > 0) { + const currentSum = tempRecipients + .slice(0, -1) + .reduce((sum, item) => sum + item.portion, 0n) - // Get current values - const governanceShare = - revenueDistributionChanges.governanceShare ?? - feeRecipients.governanceShare - const deployerShare = - revenueDistributionChanges.deployerShare ?? feeRecipients.deployerShare - const additionalRecipients = - revenueDistributionChanges.additionalRecipients ?? - feeRecipients.externalRecipients - - // Build recipients array (excluding last one for now) - const tempRecipients: { recipient: Address; portion: bigint }[] = [] - - // Additional recipients - if (additionalRecipients && additionalRecipients.length > 0) { - for (const recipient of additionalRecipients) { - if (recipient.share > 0 && recipient.address) { - tempRecipients.push({ - recipient: recipient.address as Address, - portion: calculateShare(recipient.share), - }) + if (tempRecipients.length > 1) { + tempRecipients[tempRecipients.length - 1].portion = + parseEther('1') - currentSum } + + // Sort by address + tempRecipients.sort((a, b) => + a.recipient.toLowerCase().localeCompare(b.recipient.toLowerCase()) + ) + + newFeeRecipients.push(...tempRecipients) } - } - // Deployer share - if (deployerShare > 0) { - tempRecipients.push({ - recipient: indexDTF.deployer as Address, - portion: calculateShare(deployerShare), - }) + // Only add the calldata if we have recipients + if (newFeeRecipients.length > 0) { + calldatas.push( + encodeFunctionData({ + abi: dtfIndexAbi, + functionName: 'setFeeRecipients', + args: [newFeeRecipients], + }) + ) + targets.push(indexDTF.id as Address) + } } - // Governance share - if (governanceShare > 0 && indexDTF.stToken) { - tempRecipients.push({ - recipient: indexDTF.stToken.id as Address, - portion: calculateShare(governanceShare), - }) - } + // 7. Handle governance parameter changes + if (indexDTF.ownerGovernance && Object.keys(governanceChanges).length > 0) { + const governanceAddress = indexDTF.ownerGovernance.id as Address + const timelockAddress = indexDTF.ownerGovernance.timelock?.id as Address - // Calculate sum and adjust last recipient to avoid rounding errors - if (tempRecipients.length > 0) { - const currentSum = tempRecipients - .slice(0, -1) - .reduce((sum, item) => sum + item.portion, 0n) + // Set voting delay + if (governanceChanges.votingDelay !== undefined) { + calldatas.push(encodeVotingDelay(governanceChanges.votingDelay)) + targets.push(governanceAddress) + } - if (tempRecipients.length > 1) { - tempRecipients[tempRecipients.length - 1].portion = - parseEther('1') - currentSum + // Set voting period + if (governanceChanges.votingPeriod !== undefined) { + calldatas.push(encodeVotingPeriod(governanceChanges.votingPeriod)) + targets.push(governanceAddress) } - // Sort by address - tempRecipients.sort((a, b) => - a.recipient.toLowerCase().localeCompare(b.recipient.toLowerCase()) - ) + // Set proposal threshold + if (governanceChanges.proposalThreshold !== undefined) { + calldatas.push( + encodeProposalThreshold(governanceChanges.proposalThreshold) + ) + targets.push(governanceAddress) + } - newFeeRecipients.push(...tempRecipients) - } + // Set quorum votes + if (governanceChanges.quorumPercent !== undefined) { + calldatas.push( + encodeQuorum( + governanceChanges.quorumPercent, + indexDTF.ownerGovernance.quorumDenominator + ) + ) + targets.push(governanceAddress) + } - // Only add the calldata if we have recipients - if (newFeeRecipients.length > 0) { - calldatas.push( - encodeFunctionData({ - abi: dtfIndexAbi, - functionName: 'setFeeRecipients', - args: [newFeeRecipients], - }) - ) - targets.push(indexDTF.id as Address) + // Set execution delay (timelock) + if (governanceChanges.executionDelay !== undefined && timelockAddress) { + calldatas.push(encodeExecutionDelay(governanceChanges.executionDelay)) + targets.push(timelockAddress) + } } + + return calldatas.length > 0 ? { calldatas, targets } : undefined } +) - // 7. Handle governance parameter changes - if (indexDTF.ownerGovernance && Object.keys(governanceChanges).length > 0) { - const governanceAddress = indexDTF.ownerGovernance.id as Address - const timelockAddress = indexDTF.ownerGovernance.timelock?.id as Address +// Atom for formatted governance changes for display +export const dtfGovernanceChangesDisplayAtom = atom( + (get) => { + const governanceChanges = get(governanceChangesAtom) + const dtf = get(indexDTFAtom) + + if (!dtf?.ownerGovernance) return [] + + const governance = dtf.ownerGovernance + const changes = [] - // Set voting delay if (governanceChanges.votingDelay !== undefined) { - calldatas.push(encodeVotingDelay(governanceChanges.votingDelay)) - targets.push(governanceAddress) + changes.push({ + key: 'votingDelay' as keyof GovernanceChanges, + title: 'Voting Delay', + current: humanizeTimeFromSeconds(Number(governance.votingDelay)), + new: humanizeTimeFromSeconds(governanceChanges.votingDelay), + }) } - // Set voting period if (governanceChanges.votingPeriod !== undefined) { - calldatas.push(encodeVotingPeriod(governanceChanges.votingPeriod)) - targets.push(governanceAddress) + changes.push({ + key: 'votingPeriod' as keyof GovernanceChanges, + title: 'Voting Period', + current: humanizeTimeFromSeconds(Number(governance.votingPeriod)), + new: humanizeTimeFromSeconds(governanceChanges.votingPeriod), + }) } - // Set proposal threshold if (governanceChanges.proposalThreshold !== undefined) { - calldatas.push(encodeProposalThreshold(governanceChanges.proposalThreshold)) - targets.push(governanceAddress) + changes.push({ + key: 'proposalThreshold' as keyof GovernanceChanges, + title: 'Proposal Threshold', + current: `${proposalThresholdToPercentage(governance.proposalThreshold).toFixed(2)}%`, + new: `${Number(governanceChanges.proposalThreshold).toFixed(2)}%`, + }) } - // Set quorum votes if (governanceChanges.quorumPercent !== undefined) { - calldatas.push(encodeQuorum( - governanceChanges.quorumPercent, - indexDTF.ownerGovernance.quorumDenominator - )) - targets.push(governanceAddress) + const currentQuorum = get(currentQuorumPercentageAtom) + changes.push({ + key: 'quorumPercent' as keyof GovernanceChanges, + title: 'Voting Quorum', + current: `${currentQuorum.toFixed(2)}%`, + new: `${governanceChanges.quorumPercent}%`, + }) } - // Set execution delay (timelock) - if (governanceChanges.executionDelay !== undefined && timelockAddress) { - calldatas.push(encodeExecutionDelay(governanceChanges.executionDelay)) - targets.push(timelockAddress) + if (governanceChanges.executionDelay !== undefined) { + changes.push({ + key: 'executionDelay' as keyof GovernanceChanges, + title: 'Execution Delay', + current: humanizeTimeFromSeconds( + Number(governance.timelock?.executionDelay || 0) + ), + new: humanizeTimeFromSeconds(governanceChanges.executionDelay), + }) } - } - - return calldatas.length > 0 ? { calldatas, targets } : undefined -}) - -// Atom for formatted governance changes for display -export const dtfGovernanceChangesDisplayAtom = atom((get) => { - const governanceChanges = get(governanceChangesAtom) - const dtf = get(indexDTFAtom) - - if (!dtf?.ownerGovernance) return [] - - const governance = dtf.ownerGovernance - const changes = [] - if (governanceChanges.votingDelay !== undefined) { - changes.push({ - key: 'votingDelay' as keyof GovernanceChanges, - title: 'Voting Delay', - current: humanizeTimeFromSeconds(Number(governance.votingDelay)), - new: humanizeTimeFromSeconds(governanceChanges.votingDelay), - }) + return changes } - - if (governanceChanges.votingPeriod !== undefined) { - changes.push({ - key: 'votingPeriod' as keyof GovernanceChanges, - title: 'Voting Period', - current: humanizeTimeFromSeconds(Number(governance.votingPeriod)), - new: humanizeTimeFromSeconds(governanceChanges.votingPeriod), - }) - } - - if (governanceChanges.proposalThreshold !== undefined) { - changes.push({ - key: 'proposalThreshold' as keyof GovernanceChanges, - title: 'Proposal Threshold', - current: `${proposalThresholdToPercentage(governance.proposalThreshold).toFixed(2)}%`, - new: `${Number(governanceChanges.proposalThreshold).toFixed(2)}%`, - }) - } - - if (governanceChanges.quorumPercent !== undefined) { - const currentQuorum = get(currentQuorumPercentageAtom) - changes.push({ - key: 'quorumPercent' as keyof GovernanceChanges, - title: 'Voting Quorum', - current: `${currentQuorum.toFixed(2)}%`, - new: `${governanceChanges.quorumPercent}%`, - }) - } - - if (governanceChanges.executionDelay !== undefined) { - changes.push({ - key: 'executionDelay' as keyof GovernanceChanges, - title: 'Execution Delay', - current: humanizeTimeFromSeconds( - Number(governance.timelock?.executionDelay || 0) - ), - new: humanizeTimeFromSeconds(governanceChanges.executionDelay), - }) - } - - return changes -}) +) // Backwards compatibility atom export const dtfSettingsProposalCalldatasAtom = atom( diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/changes/auction-settings-changes.tsx b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/changes/auction-settings-changes.tsx index a18d7cb19..c895261f8 100644 --- a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/changes/auction-settings-changes.tsx +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/changes/auction-settings-changes.tsx @@ -1,29 +1,58 @@ -import { indexDTFAtom, indexDTFRebalanceControlAtom } from '@/state/dtf/atoms' +import dtfIndexAbiV5 from '@/abis/dtf-index-abi' +import { + indexDTFAtom, + indexDTFRebalanceControlAtom, + indexDTFVersionAtom, +} from '@/state/dtf/atoms' import { useAtom, useAtomValue } from 'jotai' import { useFormContext } from 'react-hook-form' -import { ArrowRight, Clock, ScrollText, LandPlot } from 'lucide-react' +import { ArrowRight, Clock, ScrollText, LandPlot, HandCoins } from 'lucide-react' import { auctionLengthChangeAtom, hasAuctionLengthChangeAtom, weightControlChangeAtom, hasWeightControlChangeAtom, + bidsEnabledChangeAtom, + hasBidsEnabledChangeAtom, } from '../../atoms' import { ChangeSection, RevertButton } from './shared' +import { useReadContract } from 'wagmi' const AuctionSettingsChanges = () => { const indexDTF = useAtomValue(indexDTFAtom) const rebalanceControl = useAtomValue(indexDTFRebalanceControlAtom) + const version = useAtomValue(indexDTFVersionAtom) + const isV5 = version.startsWith('5') + const [auctionLengthChange, setAuctionLengthChange] = useAtom( auctionLengthChangeAtom ) const [weightControlChange, setWeightControlChange] = useAtom( weightControlChangeAtom ) + const [bidsEnabledChange, setBidsEnabledChange] = useAtom( + bidsEnabledChangeAtom + ) const hasAuctionLengthChange = useAtomValue(hasAuctionLengthChangeAtom) const hasWeightControlChange = useAtomValue(hasWeightControlChangeAtom) + const hasBidsEnabledChange = useAtomValue(hasBidsEnabledChangeAtom) const { setValue } = useFormContext() - if (!indexDTF || (!hasAuctionLengthChange && !hasWeightControlChange)) return null + // Read current bidsEnabled from contract (v5+ only) + const { data: currentBidsEnabled } = useReadContract({ + abi: dtfIndexAbiV5, + address: indexDTF?.id, + functionName: 'bidsEnabled', + chainId: indexDTF?.chainId, + query: { + enabled: !!indexDTF?.id && isV5, + }, + }) + + const hasAnyChange = + hasAuctionLengthChange || hasWeightControlChange || hasBidsEnabledChange + + if (!indexDTF || !hasAnyChange) return null const currentAuctionLength = indexDTF.auctionLength const newAuctionLength = auctionLengthChange @@ -38,6 +67,11 @@ const AuctionSettingsChanges = () => { setValue('weightControl', rebalanceControl?.weightControl ?? true) } + const onRevertBidsEnabled = () => { + setBidsEnabledChange(undefined) + setValue('bidsEnabled', currentBidsEnabled ?? true) + } + return ( }> {hasAuctionLengthChange && auctionLengthChange && ( @@ -59,26 +93,51 @@ const AuctionSettingsChanges = () => {
)} - - {hasWeightControlChange && weightControlChange !== undefined && rebalanceControl && ( -
- -
-
Weight Control
-
- - {rebalanceControl.weightControl ? 'Enabled' : 'Disabled'} - - - - {weightControlChange ? 'Enabled' : 'Disabled'} - + + {hasBidsEnabledChange && + bidsEnabledChange !== undefined && + isV5 && + currentBidsEnabled !== undefined && ( +
+ +
+
Permissionless Bids
+
+ + {currentBidsEnabled ? 'Enabled' : 'Disabled'} + + + + {bidsEnabledChange ? 'Enabled' : 'Disabled'} + +
+ +
+ )} - -
- )} + {hasWeightControlChange && + weightControlChange !== undefined && + rebalanceControl && ( +
+ +
+
Weight Control
+
+ + {rebalanceControl.weightControl ? 'Enabled' : 'Disabled'} + + + + {weightControlChange ? 'Enabled' : 'Disabled'} + +
+
+ + +
+ )} ) } diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/changes/basics-changes.tsx b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/changes/basics-changes.tsx new file mode 100644 index 000000000..dc20c254e --- /dev/null +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/changes/basics-changes.tsx @@ -0,0 +1,76 @@ +import { indexDTFAtom, indexDTFVersionAtom } from '@/state/dtf/atoms' +import { useAtom, useAtomValue } from 'jotai' +import { useFormContext } from 'react-hook-form' +import { ArrowRight, FileText, Type } from 'lucide-react' +import { + mandateChangeAtom, + hasMandateChangeAtom, + tokenNameChangeAtom, + hasTokenNameChangeAtom, +} from '../../atoms' +import { ChangeSection, RevertButton } from './shared' + +const BasicsChanges = () => { + const indexDTF = useAtomValue(indexDTFAtom) + const version = useAtomValue(indexDTFVersionAtom) + const isV5 = version.startsWith('5') + const [tokenNameChange, setTokenNameChange] = useAtom(tokenNameChangeAtom) + const [mandateChange, setMandateChange] = useAtom(mandateChangeAtom) + const hasTokenNameChange = useAtomValue(hasTokenNameChangeAtom) + const hasMandateChange = useAtomValue(hasMandateChangeAtom) + const { setValue } = useFormContext() + + const hasAnyChange = hasTokenNameChange || hasMandateChange + + if (!hasAnyChange || !indexDTF) return null + + const handleRevertTokenName = () => { + setTokenNameChange(undefined) + setValue('tokenName', indexDTF.token.name || '') + } + + const handleRevertMandate = () => { + setMandateChange(undefined) + setValue('mandate', indexDTF.mandate || '') + } + + return ( + }> + {hasTokenNameChange && isV5 && tokenNameChange && ( +
+ +
+
Token Name
+
+ + {indexDTF.token.name} + + + {tokenNameChange} +
+
+ +
+ )} + + {hasMandateChange && mandateChange && ( +
+
+
Current mandate:
+

+ {indexDTF.mandate || 'No mandate set'} +

+
+ +
+
New mandate:
+

{mandateChange}

+
+ +
+ )} +
+ ) +} + +export default BasicsChanges diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-changes.tsx b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-changes.tsx index ac10df0d9..84436be8d 100644 --- a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-changes.tsx +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-changes.tsx @@ -1,15 +1,18 @@ import { useAtomValue } from 'jotai' import { removedBasketTokensAtom, + hasTokenNameChangeAtom, hasMandateChangeAtom, hasRolesChangesAtom, hasRevenueDistributionChangesAtom, hasDtfRevenueChangesAtom, hasAuctionLengthChangeAtom, + hasWeightControlChangeAtom, + hasBidsEnabledChangeAtom, hasGovernanceChangesAtom, } from '../atoms' import RemovedTokensChanges from './changes/removed-tokens-changes' -import MandateChanges from './changes/mandate-changes' +import BasicsChanges from './changes/basics-changes' import RoleChanges from './changes/role-changes' import RevenueChanges from './changes/revenue-changes' import AuctionSettingsChanges from './changes/auction-settings-changes' @@ -17,6 +20,7 @@ import GovernanceChanges from './changes/governance-changes' const DTFSettingsProposalChanges = () => { const removedBasketTokens = useAtomValue(removedBasketTokensAtom) + const hasTokenNameChange = useAtomValue(hasTokenNameChangeAtom) const hasMandateChange = useAtomValue(hasMandateChangeAtom) const hasRolesChanges = useAtomValue(hasRolesChangesAtom) const hasRevenueDistributionChanges = useAtomValue( @@ -24,15 +28,20 @@ const DTFSettingsProposalChanges = () => { ) const hasDtfRevenueChanges = useAtomValue(hasDtfRevenueChangesAtom) const hasAuctionLengthChange = useAtomValue(hasAuctionLengthChangeAtom) + const hasWeightControlChange = useAtomValue(hasWeightControlChangeAtom) + const hasBidsEnabledChange = useAtomValue(hasBidsEnabledChangeAtom) const hasGovernanceChanges = useAtomValue(hasGovernanceChangesAtom) const hasAnyChanges = removedBasketTokens.length > 0 || + hasTokenNameChange || hasMandateChange || hasRolesChanges || hasRevenueDistributionChanges || hasDtfRevenueChanges || hasAuctionLengthChange || + hasWeightControlChange || + hasBidsEnabledChange || hasGovernanceChanges if (!hasAnyChanges) { @@ -41,7 +50,7 @@ const DTFSettingsProposalChanges = () => { return (
- + diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-preview.tsx b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-preview.tsx index 51bb93ca1..2f7c0a97e 100644 --- a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-preview.tsx +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-preview.tsx @@ -1,5 +1,5 @@ import dtfIndexAbiV2 from '@/abis/dtf-index-abi-v2' -import dtfIndexAbi from '@/abis/dtf-index-abi' +import dtfIndexAbi from '@/abis/dtf-index-abi-v1' import { Button } from '@/components/ui/button' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { chainIdAtom } from '@/state/atoms' @@ -21,13 +21,7 @@ import { useAtomValue } from 'jotai' import { ArrowUpRightIcon } from 'lucide-react' import { useMemo } from 'react' import { Link } from 'react-router-dom' -import { - Abi, - Address, - decodeFunctionData, - getAbiItem, - Hex, -} from 'viem' +import { Abi, Address, decodeFunctionData, getAbiItem, Hex } from 'viem' const TABS = { SUMMARY: 'overview', @@ -140,23 +134,26 @@ const RawPreview = ({ data }: { data: DecodedCalldata[] }) => { ) } - const UnknownPreview = ({ signature }: { signature: string }) => { - return
Preview not available for: {signature}
+ return ( +
+ Preview not available for: {signature} +
+ ) } const dtfSettingsPreviewComponents: Record> = { // Token management removeFromBasket: RemoveFromBasketPreview, setDustAmount: SetDustAmountPreview, - + // Settings updates setMandate: SetMandatePreview, setMintFee: SetMintFeePreview, setTVLFee: SetTVLFeePreview, setAuctionLength: SetAuctionLengthPreview, setFeeRecipients: SetFeeRecipientsPreview, - + // Role management grantRole: GrantRolePreview, revokeRole: RevokeRolePreview, @@ -168,10 +165,7 @@ const OverviewPreview = ({ data }: { data: DecodedCalldata[] }) => { {data.map((call) => { const Component = dtfSettingsPreviewComponents[call.signature] return Component ? ( - + ) : ( ) diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-sections.tsx b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-sections.tsx index a44bee328..d321467a5 100644 --- a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-sections.tsx +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/dtf-settings-proposal-sections.tsx @@ -57,8 +57,8 @@ const DTF_SETTINGS: DTF_SETTING[] = [ { id: 'mandate', icon: , - title: 'Mandate', - titleSecondary: 'Mandate', + title: 'Basics', + titleSecondary: 'Basics', content: , }, { diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-auction-settings.tsx b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-auction-settings.tsx index 9ee89f1b4..805309b93 100644 --- a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-auction-settings.tsx +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-auction-settings.tsx @@ -1,6 +1,10 @@ +import { Switch } from '@/components/ui/switch' +import { indexDTFVersionAtom } from '@/state/dtf/atoms' import { humanizeMinutes } from '@/utils' import ToggleGroupWithCustom from '@/views/index-dtf/deploy/components/toggle-group-with-custom' -import { Hourglass } from 'lucide-react' +import { useAtomValue } from 'jotai' +import { HandCoins, Hourglass } from 'lucide-react' +import { Controller, useFormContext } from 'react-hook-form' import ProposeWeightControl from './propose-weight-control' const TOGGLE_FORMS = [ @@ -19,10 +23,44 @@ const TOGGLE_FORMS = [ }, ] +const ProposeBidsEnabled = () => { + const { control } = useFormContext() + const version = useAtomValue(indexDTFVersionAtom) + const isV5 = version.startsWith('5') + + if (!isV5) return null + + return ( +
+
+
+ +
+ +
+
Permissionless Bids
+
+ Enable bids directly via the folio. +
+
+ + ( + + )} + /> +
+
+ ) +} + const ProposeAuctionSettings = () => { return (
+
) diff --git a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-metadata.tsx b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-metadata.tsx index 550a79e6a..f4fb23075 100644 --- a/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-metadata.tsx +++ b/src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-metadata.tsx @@ -2,21 +2,48 @@ import { FormControl, FormField, FormItem, + FormLabel, FormMessage, } from '@/components/ui/form' +import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' +import { indexDTFVersionAtom } from '@/state/dtf/atoms' +import { useAtomValue } from 'jotai' import { useFormContext } from 'react-hook-form' const ProposeMetadata = () => { const form = useFormContext() + const version = useAtomValue(indexDTFVersionAtom) + const isV5 = version.startsWith('5') return ( -
+
+ {isV5 && ( + ( + + Token Name + + + + + + )} + /> + )} ( + Mandate