From 29db31798ff66a6fbb4d1602a835b5137cb2d2c8 Mon Sep 17 00:00:00 2001 From: tadej-solidant Date: Thu, 18 Jan 2024 14:58:58 +0100 Subject: [PATCH] feat(apps): Spool.fi v2 support --- src/apps/spool-v2/assets/logo.png | Bin 0 -> 83938 bytes .../spool-v2/contracts/abis/spool-lens.json | 132 ++ .../contracts/abis/spool-staking.json | 810 +++++++++++ .../spool-v2/contracts/abis/spool-vault.json | 815 +++++++++++ .../contracts/abis/spool-vospool.json | 1173 ++++++++++++++++ .../contracts/abis/strategy-registry.json | 801 +++++++++++ src/apps/spool-v2/contracts/index.ts | 4 + .../contracts/viem.contract-factory.ts | 35 + src/apps/spool-v2/contracts/viem/SpoolLens.ts | 146 ++ .../spool-v2/contracts/viem/SpoolStaking.ts | 824 ++++++++++++ .../spool-v2/contracts/viem/SpoolVault.ts | 829 ++++++++++++ .../spool-v2/contracts/viem/SpoolVospool.ts | 1187 +++++++++++++++++ .../contracts/viem/StrategyRegistry.ts | 815 +++++++++++ src/apps/spool-v2/contracts/viem/index.ts | 15 + .../spool-v2/ethereum/spool-v2.constants.ts | 208 +++ ...ol-v2.staking.contract-position-fetcher.ts | 154 +++ src/apps/spool-v2/ethereum/spool-v2.types.ts | 291 ++++ ...pool-v2.vault.contract-position-fetcher.ts | 739 ++++++++++ .../spool-v2.vo-spool.token-fetcher.ts | 44 + src/apps/spool-v2/spool-v2.module.ts | 18 + 20 files changed, 9040 insertions(+) create mode 100644 src/apps/spool-v2/assets/logo.png create mode 100644 src/apps/spool-v2/contracts/abis/spool-lens.json create mode 100644 src/apps/spool-v2/contracts/abis/spool-staking.json create mode 100644 src/apps/spool-v2/contracts/abis/spool-vault.json create mode 100644 src/apps/spool-v2/contracts/abis/spool-vospool.json create mode 100644 src/apps/spool-v2/contracts/abis/strategy-registry.json create mode 100644 src/apps/spool-v2/contracts/index.ts create mode 100644 src/apps/spool-v2/contracts/viem.contract-factory.ts create mode 100644 src/apps/spool-v2/contracts/viem/SpoolLens.ts create mode 100644 src/apps/spool-v2/contracts/viem/SpoolStaking.ts create mode 100644 src/apps/spool-v2/contracts/viem/SpoolVault.ts create mode 100644 src/apps/spool-v2/contracts/viem/SpoolVospool.ts create mode 100644 src/apps/spool-v2/contracts/viem/StrategyRegistry.ts create mode 100644 src/apps/spool-v2/contracts/viem/index.ts create mode 100644 src/apps/spool-v2/ethereum/spool-v2.constants.ts create mode 100644 src/apps/spool-v2/ethereum/spool-v2.staking.contract-position-fetcher.ts create mode 100644 src/apps/spool-v2/ethereum/spool-v2.types.ts create mode 100644 src/apps/spool-v2/ethereum/spool-v2.vault.contract-position-fetcher.ts create mode 100644 src/apps/spool-v2/ethereum/spool-v2.vo-spool.token-fetcher.ts create mode 100644 src/apps/spool-v2/spool-v2.module.ts diff --git a/src/apps/spool-v2/assets/logo.png b/src/apps/spool-v2/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..154bed7a3d78151cc9002b4d8e395305baac0f39 GIT binary patch literal 83938 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 literal 0 HcmV?d00001 diff --git a/src/apps/spool-v2/contracts/abis/spool-lens.json b/src/apps/spool-v2/contracts/abis/spool-lens.json new file mode 100644 index 000000000..c8bc0c8ab --- /dev/null +++ b/src/apps/spool-v2/contracts/abis/spool-lens.json @@ -0,0 +1,132 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "smartVault", + "type": "address" + } + ], + "name": "getSVTTotalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "address", + "name": "riskProvider", + "type": "address" + }, + { + "internalType": "address", + "name": "allocationProvider", + "type": "address" + } + ], + "name": "getSmartVaultAllocations", + "outputs": [ + { + "internalType": "uint256[][]", + "name": "allocations", + "type": "uint256[][]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "smartVault", + "type": "address" + }, + { + "internalType": "bool", + "name": "doFlush", + "type": "bool" + } + ], + "name": "getSmartVaultAssetBalances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "balances", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "smartVault", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "nftIds", + "type": "uint256[]" + } + ], + "name": "getUserSVTBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "currentBalance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "smartVault", + "type": "address" + }, + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "nftIds", + "type": "uint256[]" + } + ], + "name": "getUserSVTsfromNFTs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "nftSvts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/apps/spool-v2/contracts/abis/spool-staking.json b/src/apps/spool-v2/contracts/abis/spool-staking.json new file mode 100644 index 000000000..cec171051 --- /dev/null +++ b/src/apps/spool-v2/contracts/abis/spool-staking.json @@ -0,0 +1,810 @@ +[ + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "_stakingToken", + "type": "address" + }, + { + "internalType": "contract IVoSPOOL", + "name": "_voSpool", + "type": "address" + }, + { + "internalType": "contract IVoSpoolRewards", + "name": "_voSpoolRewards", + "type": "address" + }, + { + "internalType": "contract IRewardDistributor", + "name": "_rewardDistributor", + "type": "address" + }, + { + "internalType": "contract ISpoolOwner", + "name": "_spoolOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "canStakeFor", + "type": "bool" + } + ], + "name": "CanStakeForSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "periodFinish", + "type": "uint32" + } + ], + "name": "PeriodFinishUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "name": "RewardAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "RewardCompounded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "RewardPaid", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "RewardRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "leftover", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "periodFinish", + "type": "uint32" + } + ], + "name": "RewardUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "stakedFor", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "stakedBy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "StakedFor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Unstaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "VoRewardCompounded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "VoSpoolRewardPaid", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "rewardsDuration", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "addToken", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "allowFor", + "type": "address" + } + ], + "name": "allowUnstakeFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balances", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "canStakeFor", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "doCompoundVoSpoolRewards", + "type": "bool" + } + ], + "name": "compound", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "earned", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "doClaimVoSpoolRewards", + "type": "bool" + } + ], + "name": "getActiveRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "bool", + "name": "doClaimVoSpoolRewards", + "type": "bool" + } + ], + "name": "getRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getUpdatedVoSpoolRewardAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "rewards", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "lastTimeRewardApplicable", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "_rewardsDuration", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + } + ], + "name": "notifyRewardAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "tokenAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenAmount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recoverTo", + "type": "address" + } + ], + "name": "recoverERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "removeReward", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "name": "rewardConfiguration", + "outputs": [ + { + "internalType": "uint32", + "name": "rewardsDuration", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "periodFinish", + "type": "uint32" + }, + { + "internalType": "uint192", + "name": "rewardRate", + "type": "uint192" + }, + { + "internalType": "uint32", + "name": "lastUpdateTime", + "type": "uint32" + }, + { + "internalType": "uint224", + "name": "rewardPerTokenStored", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rewardDistributor", + "outputs": [ + { + "internalType": "contract IRewardDistributor", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + } + ], + "name": "rewardPerToken", + "outputs": [ + { + "internalType": "uint224", + "name": "", + "type": "uint224" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "rewardTokens", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "rewardTokensCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bool", + "name": "_canStakeFor", + "type": "bool" + } + ], + "name": "setCanStakeFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "stakeFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "stakedBy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stakingToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "name": "tokenBlacklist", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalStaked", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "unstake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint32", + "name": "timestamp", + "type": "uint32" + } + ], + "name": "updatePeriodFinish", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "voSpool", + "outputs": [ + { + "internalType": "contract IVoSPOOL", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "voSpoolRewards", + "outputs": [ + { + "internalType": "contract IVoSpoolRewards", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/apps/spool-v2/contracts/abis/spool-vault.json b/src/apps/spool-v2/contracts/abis/spool-vault.json new file mode 100644 index 000000000..5ac084cfe --- /dev/null +++ b/src/apps/spool-v2/contracts/abis/spool-vault.json @@ -0,0 +1,815 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "baseUri", + "type": "string" + } + ], + "name": "BaseURIChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "assetGroupId", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOfFractional", + "outputs": [ + { + "internalType": "uint256", + "name": "fractionalBalance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfFractionalBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "fractionalBalances", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "nftIds", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "nftAmounts", + "type": "uint256[]" + } + ], + "name": "burnNFTs", + "outputs": [ + { + "internalType": "bytes[]", + "name": "metadata", + "type": "bytes[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vaultShares", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "shares", + "type": "uint256[]" + } + ], + "name": "burnVaultShares", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "claimer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "claimShares", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "nftIds", + "type": "uint256[]" + } + ], + "name": "getMetadata", + "outputs": [ + { + "internalType": "bytes[]", + "name": "metadata", + "type": "bytes[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256[]", + "name": "assets", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "initiated", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "flushIndex", + "type": "uint256" + } + ], + "internalType": "struct DepositMetadata", + "name": "metadata", + "type": "tuple" + } + ], + "name": "mintDepositNFT", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vaultShares", + "type": "uint256" + } + ], + "name": "mintVaultShares", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "vaultShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "flushIndex", + "type": "uint256" + } + ], + "internalType": "struct WithdrawalMetadata", + "name": "metadata", + "type": "tuple" + } + ], + "name": "mintWithdrawalNFT", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "uri_", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFromSpender", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vaultName", + "outputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/apps/spool-v2/contracts/abis/spool-vospool.json b/src/apps/spool-v2/contracts/abis/spool-vospool.json new file mode 100644 index 000000000..d8014ca64 --- /dev/null +++ b/src/apps/spool-v2/contracts/abis/spool-vospool.json @@ -0,0 +1,1173 @@ +[ + { + "inputs": [ + { + "internalType": "contract ISpoolOwner", + "name": "_spoolOwner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_firstTrancheEndTime", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "source", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Burned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "lastUpdatedTrancheIndex", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "totalMaturedVotingPower", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "totalMaturingAmount", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint56", + "name": "totalRawUnmaturedVotingPower", + "type": "uint56" + } + ], + "name": "GlobalGradualUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "source", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "burnAll", + "type": "bool" + } + ], + "name": "GradualBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "GradualMinted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "set", + "type": "bool" + } + ], + "name": "GradualMinterSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Minted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "minter", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "set", + "type": "bool" + } + ], + "name": "MinterSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint16", + "name": "lastUpdatedTrancheIndex", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "maturedVotingPower", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "maturingAmount", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint56", + "name": "rawUnmaturedVotingPower", + "type": "uint56" + } + ], + "name": "UserGradualUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "FULL_POWER_TIME", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "FULL_POWER_TRANCHES_COUNT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TRANCHE_TIME", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "burnAll", + "type": "bool" + } + ], + "name": "burnGradual", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "firstTrancheStartTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentTrancheIndex", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getGlobalGradual", + "outputs": [ + { + "components": [ + { + "internalType": "uint48", + "name": "totalMaturedVotingPower", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "totalMaturingAmount", + "type": "uint48" + }, + { + "internalType": "uint56", + "name": "totalRawUnmaturedVotingPower", + "type": "uint56" + }, + { + "internalType": "uint16", + "name": "lastUpdatedTrancheIndex", + "type": "uint16" + } + ], + "internalType": "struct GlobalGradual", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getLastFinishedTrancheIndex", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNextTrancheEndTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNotUpdatedGlobalGradual", + "outputs": [ + { + "components": [ + { + "internalType": "uint48", + "name": "totalMaturedVotingPower", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "totalMaturingAmount", + "type": "uint48" + }, + { + "internalType": "uint56", + "name": "totalRawUnmaturedVotingPower", + "type": "uint56" + }, + { + "internalType": "uint16", + "name": "lastUpdatedTrancheIndex", + "type": "uint16" + } + ], + "internalType": "struct GlobalGradual", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getNotUpdatedUserGradual", + "outputs": [ + { + "components": [ + { + "internalType": "uint48", + "name": "maturedVotingPower", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "maturingAmount", + "type": "uint48" + }, + { + "internalType": "uint56", + "name": "rawUnmaturedVotingPower", + "type": "uint56" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "arrayIndex", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "position", + "type": "uint8" + } + ], + "internalType": "struct UserTranchePosition", + "name": "oldestTranchePosition", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "arrayIndex", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "position", + "type": "uint8" + } + ], + "internalType": "struct UserTranchePosition", + "name": "latestTranchePosition", + "type": "tuple" + }, + { + "internalType": "uint16", + "name": "lastUpdatedTrancheIndex", + "type": "uint16" + } + ], + "internalType": "struct UserGradual", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalGradualVotingPower", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "trancheIndex", + "type": "uint256" + } + ], + "name": "getTrancheEndTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "time", + "type": "uint256" + } + ], + "name": "getTrancheIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserGradual", + "outputs": [ + { + "components": [ + { + "internalType": "uint48", + "name": "maturedVotingPower", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "maturingAmount", + "type": "uint48" + }, + { + "internalType": "uint56", + "name": "rawUnmaturedVotingPower", + "type": "uint56" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "arrayIndex", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "position", + "type": "uint8" + } + ], + "internalType": "struct UserTranchePosition", + "name": "oldestTranchePosition", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "arrayIndex", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "position", + "type": "uint8" + } + ], + "internalType": "struct UserTranchePosition", + "name": "latestTranchePosition", + "type": "tuple" + }, + { + "internalType": "uint16", + "name": "lastUpdatedTrancheIndex", + "type": "uint16" + } + ], + "internalType": "struct UserGradual", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserGradualVotingPower", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "gradualMinters", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "indexedGlobalTranches", + "outputs": [ + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + } + ], + "internalType": "struct Tranche", + "name": "zero", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + } + ], + "internalType": "struct Tranche", + "name": "one", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + } + ], + "internalType": "struct Tranche", + "name": "two", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + } + ], + "internalType": "struct Tranche", + "name": "three", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + } + ], + "internalType": "struct Tranche", + "name": "four", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mintGradual", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "minters", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_gradualMinter", + "type": "address" + }, + { + "internalType": "bool", + "name": "_set", + "type": "bool" + } + ], + "name": "setGradualMinter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_minter", + "type": "address" + }, + { + "internalType": "bool", + "name": "_set", + "type": "bool" + } + ], + "name": "setMinter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalInstantPower", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "updateUserVotingPower", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "updateVotingPower", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "userInstantPower", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "userTranches", + "outputs": [ + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "internalType": "struct UserTranche", + "name": "zero", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "internalType": "struct UserTranche", + "name": "one", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "internalType": "struct UserTranche", + "name": "two", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint48", + "name": "amount", + "type": "uint48" + }, + { + "internalType": "uint16", + "name": "index", + "type": "uint16" + } + ], + "internalType": "struct UserTranche", + "name": "three", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/apps/spool-v2/contracts/abis/strategy-registry.json b/src/apps/spool-v2/contracts/abis/strategy-registry.json new file mode 100644 index 000000000..5c23a66e9 --- /dev/null +++ b/src/apps/spool-v2/contracts/abis/strategy-registry.json @@ -0,0 +1,801 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "ecosystemFeeReceiver", + "type": "address" + } + ], + "name": "EcosystemFeeReceiverSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "feePct", + "type": "uint256" + } + ], + "name": "EcosystemFeeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "EmergencyWithdrawalWalletSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "apy", + "type": "int256" + } + ], + "name": "StrategyApyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "dhwIndex", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "sharesMinted", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "assetsWithdrawn", + "type": "uint256[]" + }, + { + "internalType": "int256", + "name": "yieldPercentage", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "valueAtDhw", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSstsAtDhw", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct DhwInfo", + "name": "dhwInfo", + "type": "tuple" + } + ], + "name": "StrategyDhw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "StrategyRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "assetsWithdrawn", + "type": "uint256[]" + } + ], + "name": "StrategySharesFastRedeemed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "assetsWithdrawn", + "type": "uint256[]" + } + ], + "name": "StrategySharesRedeemed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "treasuryFeeReceiver", + "type": "address" + } + ], + "name": "TreasuryFeeReceiverSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "feePct", + "type": "uint256" + } + ], + "name": "TreasuryFeeSet", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint256[][]", + "name": "amounts", + "type": "uint256[][]" + } + ], + "name": "addDeposits", + "outputs": [ + { + "internalType": "uint16a16", + "name": "strategyIndexes", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "strategyShares", + "type": "uint256[]" + } + ], + "name": "addWithdrawals", + "outputs": [ + { + "internalType": "uint16a16", + "name": "strategyIndexes", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "assetRatioAtLastDhw", + "outputs": [ + { + "internalType": "uint256[]", + "name": "assetRatio", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint16a16", + "name": "dhwIndexes", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "strategyShares", + "type": "uint256[]" + } + ], + "name": "claimWithdrawals", + "outputs": [ + { + "internalType": "uint256[]", + "name": "assetsWithdrawn", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + } + ], + "name": "currentIndex", + "outputs": [ + { + "internalType": "uint256[]", + "name": "dhwIndexes", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "internalType": "uint256", + "name": "dhwIndex", + "type": "uint256" + } + ], + "name": "depositedAssets", + "outputs": [ + { + "internalType": "uint256[]", + "name": "assets", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint16a16", + "name": "dhwIndexes", + "type": "uint256" + } + ], + "name": "dhwTimestamps", + "outputs": [ + { + "internalType": "uint256[]", + "name": "timestamps", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address[][]", + "name": "strategies", + "type": "address[][]" + }, + { + "components": [ + { + "internalType": "address", + "name": "swapTarget", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "internalType": "struct SwapInfo[][][]", + "name": "swapInfo", + "type": "tuple[][][]" + }, + { + "components": [ + { + "internalType": "address", + "name": "swapTarget", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "bytes", + "name": "swapCallData", + "type": "bytes" + } + ], + "internalType": "struct SwapInfo[][][]", + "name": "compoundSwapInfo", + "type": "tuple[][][]" + }, + { + "internalType": "uint256[][][]", + "name": "strategySlippages", + "type": "uint256[][][]" + }, + { + "internalType": "int256[][]", + "name": "baseYields", + "type": "int256[][]" + }, + { + "internalType": "address[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[2][]", + "name": "exchangeRateSlippages", + "type": "uint256[2][]" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + } + ], + "internalType": "struct DoHardWorkParameterBag", + "name": "dhwParams", + "type": "tuple" + } + ], + "name": "doHardWork", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "emergencyWithdrawalWallet", + "outputs": [ + { + "internalType": "address", + "name": "emergencyWithdrawalWallet", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint16a16", + "name": "dhwIndexes", + "type": "uint256" + } + ], + "name": "getDhwYield", + "outputs": [ + { + "internalType": "int256[]", + "name": "yields", + "type": "int256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "platformFees", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "ecosystemFeeReceiver", + "type": "address" + }, + { + "internalType": "uint96", + "name": "ecosystemFeePct", + "type": "uint96" + }, + { + "internalType": "address", + "name": "treasuryFeeReceiver", + "type": "address" + }, + { + "internalType": "uint96", + "name": "treasuryFeePct", + "type": "uint96" + } + ], + "internalType": "struct PlatformFees", + "name": "fees", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "strategyShares", + "type": "uint256[]" + }, + { + "internalType": "address[]", + "name": "assetGroup", + "type": "address[]" + }, + { + "internalType": "uint256[][]", + "name": "withdrawalSlippages", + "type": "uint256[][]" + } + ], + "internalType": "struct RedeemFastParameterBag", + "name": "redeemFastParams", + "type": "tuple" + } + ], + "name": "redeemFast", + "outputs": [ + { + "internalType": "uint256[]", + "name": "withdrawnAssets", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "shares", + "type": "uint256[]" + }, + { + "internalType": "uint256[][]", + "name": "withdrawalSlippages", + "type": "uint256[][]" + } + ], + "name": "redeemStrategyShares", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "internalType": "int256", + "name": "apy", + "type": "int256" + } + ], + "name": "registerStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "strategy", + "type": "address" + } + ], + "name": "removeStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "ecosystemFeePct", + "type": "uint96" + } + ], + "name": "setEcosystemFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "ecosystemFeeReceiver", + "type": "address" + } + ], + "name": "setEcosystemFeeReceiver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint96", + "name": "treasuryFeePct", + "type": "uint96" + } + ], + "name": "setTreasuryFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "treasuryFeeReceiver", + "type": "address" + } + ], + "name": "setTreasuryFeeReceiver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "strategy", + "type": "address" + }, + { + "internalType": "uint256", + "name": "dhwIndex", + "type": "uint256" + } + ], + "name": "sharesRedeemed", + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + } + ], + "name": "strategyAPYs", + "outputs": [ + { + "internalType": "int256[]", + "name": "apys", + "type": "int256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "strategies", + "type": "address[]" + }, + { + "internalType": "uint16a16", + "name": "dhwIndexes", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "assetGroupLength", + "type": "uint256" + } + ], + "name": "strategyAtIndexBatch", + "outputs": [ + { + "components": [ + { + "internalType": "uint256[]", + "name": "exchangeRates", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "assetsDeposited", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "sharesMinted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSSTs", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalStrategyValue", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "dhwYields", + "type": "int256" + } + ], + "internalType": "struct StrategyAtIndex[]", + "name": "states", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/apps/spool-v2/contracts/index.ts b/src/apps/spool-v2/contracts/index.ts new file mode 100644 index 000000000..5dcfebdf6 --- /dev/null +++ b/src/apps/spool-v2/contracts/index.ts @@ -0,0 +1,4 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +export * from './viem.contract-factory'; diff --git a/src/apps/spool-v2/contracts/viem.contract-factory.ts b/src/apps/spool-v2/contracts/viem.contract-factory.ts new file mode 100644 index 000000000..541aef539 --- /dev/null +++ b/src/apps/spool-v2/contracts/viem.contract-factory.ts @@ -0,0 +1,35 @@ +import { Injectable, Inject } from '@nestjs/common'; + +import { IAppToolkit, APP_TOOLKIT } from '~app-toolkit/app-toolkit.interface'; +import { Network } from '~types/network.interface'; + +import { + SpoolLens__factory, + SpoolStaking__factory, + SpoolVault__factory, + SpoolVospool__factory, + StrategyRegistry__factory, +} from './viem'; + +type ContractOpts = { address: string; network: Network }; + +@Injectable() +export class SpoolV2ViemContractFactory { + constructor(@Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit) {} + + spoolLens({ address, network }: ContractOpts) { + return SpoolLens__factory.connect(address, this.appToolkit.getViemNetworkProvider(network)); + } + spoolStaking({ address, network }: ContractOpts) { + return SpoolStaking__factory.connect(address, this.appToolkit.getViemNetworkProvider(network)); + } + spoolVault({ address, network }: ContractOpts) { + return SpoolVault__factory.connect(address, this.appToolkit.getViemNetworkProvider(network)); + } + spoolVospool({ address, network }: ContractOpts) { + return SpoolVospool__factory.connect(address, this.appToolkit.getViemNetworkProvider(network)); + } + strategyRegistry({ address, network }: ContractOpts) { + return StrategyRegistry__factory.connect(address, this.appToolkit.getViemNetworkProvider(network)); + } +} diff --git a/src/apps/spool-v2/contracts/viem/SpoolLens.ts b/src/apps/spool-v2/contracts/viem/SpoolLens.ts new file mode 100644 index 000000000..05279d63c --- /dev/null +++ b/src/apps/spool-v2/contracts/viem/SpoolLens.ts @@ -0,0 +1,146 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { getContract, GetContractReturnType, PublicClient } from 'viem'; + +export const spoolLensAbi = [ + { + inputs: [ + { + internalType: 'address', + name: 'smartVault', + type: 'address', + }, + ], + name: 'getSVTTotalSupply', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'address', + name: 'riskProvider', + type: 'address', + }, + { + internalType: 'address', + name: 'allocationProvider', + type: 'address', + }, + ], + name: 'getSmartVaultAllocations', + outputs: [ + { + internalType: 'uint256[][]', + name: 'allocations', + type: 'uint256[][]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'smartVault', + type: 'address', + }, + { + internalType: 'bool', + name: 'doFlush', + type: 'bool', + }, + ], + name: 'getSmartVaultAssetBalances', + outputs: [ + { + internalType: 'uint256[]', + name: 'balances', + type: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'smartVault', + type: 'address', + }, + { + internalType: 'address', + name: 'user', + type: 'address', + }, + { + internalType: 'uint256[]', + name: 'nftIds', + type: 'uint256[]', + }, + ], + name: 'getUserSVTBalance', + outputs: [ + { + internalType: 'uint256', + name: 'currentBalance', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'smartVault', + type: 'address', + }, + { + internalType: 'address', + name: 'user', + type: 'address', + }, + { + internalType: 'uint256[]', + name: 'nftIds', + type: 'uint256[]', + }, + ], + name: 'getUserSVTsfromNFTs', + outputs: [ + { + internalType: 'uint256[]', + name: 'nftSvts', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export type SpoolLens = typeof spoolLensAbi; +export type SpoolLensContract = GetContractReturnType; + +export class SpoolLens__factory { + static connect(address: string, client: PublicClient) { + return getContract({ address, abi: spoolLensAbi, publicClient: client }); + } +} diff --git a/src/apps/spool-v2/contracts/viem/SpoolStaking.ts b/src/apps/spool-v2/contracts/viem/SpoolStaking.ts new file mode 100644 index 000000000..28c85b863 --- /dev/null +++ b/src/apps/spool-v2/contracts/viem/SpoolStaking.ts @@ -0,0 +1,824 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { getContract, GetContractReturnType, PublicClient } from 'viem'; + +export const spoolStakingAbi = [ + { + inputs: [ + { + internalType: 'contract IERC20', + name: '_stakingToken', + type: 'address', + }, + { + internalType: 'contract IVoSPOOL', + name: '_voSpool', + type: 'address', + }, + { + internalType: 'contract IVoSpoolRewards', + name: '_voSpoolRewards', + type: 'address', + }, + { + internalType: 'contract IRewardDistributor', + name: '_rewardDistributor', + type: 'address', + }, + { + internalType: 'contract ISpoolOwner', + name: '_spoolOwner', + type: 'address', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'canStakeFor', + type: 'bool', + }, + ], + name: 'CanStakeForSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint32', + name: 'periodFinish', + type: 'uint32', + }, + ], + name: 'PeriodFinishUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'duration', + type: 'uint256', + }, + ], + name: 'RewardAdded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'reward', + type: 'uint256', + }, + ], + name: 'RewardCompounded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'reward', + type: 'uint256', + }, + ], + name: 'RewardPaid', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'RewardRemoved', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'leftover', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'duration', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint32', + name: 'periodFinish', + type: 'uint32', + }, + ], + name: 'RewardUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'Staked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'stakedFor', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'stakedBy', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'StakedFor', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'Unstaked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'reward', + type: 'uint256', + }, + ], + name: 'VoRewardCompounded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'reward', + type: 'uint256', + }, + ], + name: 'VoSpoolRewardPaid', + type: 'event', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint32', + name: 'rewardsDuration', + type: 'uint32', + }, + { + internalType: 'uint256', + name: 'reward', + type: 'uint256', + }, + ], + name: 'addToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'allowFor', + type: 'address', + }, + ], + name: 'allowUnstakeFor', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'balances', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'canStakeFor', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bool', + name: 'doCompoundVoSpoolRewards', + type: 'bool', + }, + ], + name: 'compound', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'earned', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bool', + name: 'doClaimVoSpoolRewards', + type: 'bool', + }, + ], + name: 'getActiveRewards', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20[]', + name: 'tokens', + type: 'address[]', + }, + { + internalType: 'bool', + name: 'doClaimVoSpoolRewards', + type: 'bool', + }, + ], + name: 'getRewards', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'getUpdatedVoSpoolRewardAmount', + outputs: [ + { + internalType: 'uint256', + name: 'rewards', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'lastTimeRewardApplicable', + outputs: [ + { + internalType: 'uint32', + name: '', + type: 'uint32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint32', + name: '_rewardsDuration', + type: 'uint32', + }, + { + internalType: 'uint256', + name: 'reward', + type: 'uint256', + }, + ], + name: 'notifyRewardAmount', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'tokenAddress', + type: 'address', + }, + { + internalType: 'uint256', + name: 'tokenAmount', + type: 'uint256', + }, + { + internalType: 'address', + name: 'recoverTo', + type: 'address', + }, + ], + name: 'recoverERC20', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'removeReward', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: '', + type: 'address', + }, + ], + name: 'rewardConfiguration', + outputs: [ + { + internalType: 'uint32', + name: 'rewardsDuration', + type: 'uint32', + }, + { + internalType: 'uint32', + name: 'periodFinish', + type: 'uint32', + }, + { + internalType: 'uint192', + name: 'rewardRate', + type: 'uint192', + }, + { + internalType: 'uint32', + name: 'lastUpdateTime', + type: 'uint32', + }, + { + internalType: 'uint224', + name: 'rewardPerTokenStored', + type: 'uint224', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'rewardDistributor', + outputs: [ + { + internalType: 'contract IRewardDistributor', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + ], + name: 'rewardPerToken', + outputs: [ + { + internalType: 'uint224', + name: '', + type: 'uint224', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'rewardTokens', + outputs: [ + { + internalType: 'contract IERC20', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'rewardTokensCount', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'bool', + name: '_canStakeFor', + type: 'bool', + }, + ], + name: 'setCanStakeFor', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'stake', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'stakeFor', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'stakedBy', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'stakingToken', + outputs: [ + { + internalType: 'contract IERC20', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: '', + type: 'address', + }, + ], + name: 'tokenBlacklist', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalStaked', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'unstake', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract IERC20', + name: 'token', + type: 'address', + }, + { + internalType: 'uint32', + name: 'timestamp', + type: 'uint32', + }, + ], + name: 'updatePeriodFinish', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'voSpool', + outputs: [ + { + internalType: 'contract IVoSPOOL', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'voSpoolRewards', + outputs: [ + { + internalType: 'contract IVoSpoolRewards', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export type SpoolStaking = typeof spoolStakingAbi; +export type SpoolStakingContract = GetContractReturnType; + +export class SpoolStaking__factory { + static connect(address: string, client: PublicClient) { + return getContract({ address, abi: spoolStakingAbi, publicClient: client }); + } +} diff --git a/src/apps/spool-v2/contracts/viem/SpoolVault.ts b/src/apps/spool-v2/contracts/viem/SpoolVault.ts new file mode 100644 index 000000000..7c8fce631 --- /dev/null +++ b/src/apps/spool-v2/contracts/viem/SpoolVault.ts @@ -0,0 +1,829 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { getContract, GetContractReturnType, PublicClient } from 'viem'; + +export const spoolVaultAbi = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'operator', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'approved', + type: 'bool', + }, + ], + name: 'ApprovalForAll', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'string', + name: 'baseUri', + type: 'string', + }, + ], + name: 'BaseURIChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'from', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'to', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'operator', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'from', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'to', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256[]', + name: 'ids', + type: 'uint256[]', + }, + { + indexed: false, + internalType: 'uint256[]', + name: 'values', + type: 'uint256[]', + }, + ], + name: 'TransferBatch', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'operator', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'from', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'to', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'TransferSingle', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'string', + name: 'value', + type: 'string', + }, + { + indexed: true, + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + name: 'URI', + type: 'event', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + ], + name: 'allowance', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'approve', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'assetGroupId', + outputs: [ + { + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + name: 'balanceOf', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'balanceOf', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'accounts', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'ids', + type: 'uint256[]', + }, + ], + name: 'balanceOfBatch', + outputs: [ + { + internalType: 'uint256[]', + name: '', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + name: 'balanceOfFractional', + outputs: [ + { + internalType: 'uint256', + name: 'fractionalBalance', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'uint256[]', + name: 'ids', + type: 'uint256[]', + }, + ], + name: 'balanceOfFractionalBatch', + outputs: [ + { + internalType: 'uint256[]', + name: 'fractionalBalances', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'uint256[]', + name: 'nftIds', + type: 'uint256[]', + }, + { + internalType: 'uint256[]', + name: 'nftAmounts', + type: 'uint256[]', + }, + ], + name: 'burnNFTs', + outputs: [ + { + internalType: 'bytes[]', + name: 'metadata', + type: 'bytes[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + internalType: 'uint256', + name: 'vaultShares', + type: 'uint256', + }, + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'shares', + type: 'uint256[]', + }, + ], + name: 'burnVaultShares', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'claimer', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'claimShares', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256[]', + name: 'nftIds', + type: 'uint256[]', + }, + ], + name: 'getMetadata', + outputs: [ + { + internalType: 'bytes[]', + name: 'metadata', + type: 'bytes[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + { + internalType: 'address', + name: 'operator', + type: 'address', + }, + ], + name: 'isApprovedForAll', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'receiver', + type: 'address', + }, + { + components: [ + { + internalType: 'uint256[]', + name: 'assets', + type: 'uint256[]', + }, + { + internalType: 'uint256', + name: 'initiated', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'flushIndex', + type: 'uint256', + }, + ], + internalType: 'struct DepositMetadata', + name: 'metadata', + type: 'tuple', + }, + ], + name: 'mintDepositNFT', + outputs: [ + { + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'receiver', + type: 'address', + }, + { + internalType: 'uint256', + name: 'vaultShares', + type: 'uint256', + }, + ], + name: 'mintVaultShares', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'receiver', + type: 'address', + }, + { + components: [ + { + internalType: 'uint256', + name: 'vaultShares', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'flushIndex', + type: 'uint256', + }, + ], + internalType: 'struct WithdrawalMetadata', + name: 'metadata', + type: 'tuple', + }, + ], + name: 'mintWithdrawalNFT', + outputs: [ + { + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256[]', + name: 'ids', + type: 'uint256[]', + }, + { + internalType: 'uint256[]', + name: 'amounts', + type: 'uint256[]', + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes', + }, + ], + name: 'safeBatchTransferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'bytes', + name: 'data', + type: 'bytes', + }, + ], + name: 'safeTransferFrom', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'operator', + type: 'address', + }, + { + internalType: 'bool', + name: 'approved', + type: 'bool', + }, + ], + name: 'setApprovalForAll', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'string', + name: 'uri_', + type: 'string', + }, + ], + name: 'setBaseURI', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes4', + name: 'interfaceId', + type: 'bytes4', + }, + ], + name: 'supportsInterface', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalSupply', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'transfer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'transferFrom', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'transferFromSpender', + outputs: [ + { + internalType: 'bool', + name: 'success', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'id', + type: 'uint256', + }, + ], + name: 'uri', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'vaultName', + outputs: [ + { + internalType: 'string', + name: 'name', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export type SpoolVault = typeof spoolVaultAbi; +export type SpoolVaultContract = GetContractReturnType; + +export class SpoolVault__factory { + static connect(address: string, client: PublicClient) { + return getContract({ address, abi: spoolVaultAbi, publicClient: client }); + } +} diff --git a/src/apps/spool-v2/contracts/viem/SpoolVospool.ts b/src/apps/spool-v2/contracts/viem/SpoolVospool.ts new file mode 100644 index 000000000..2f07b5b1c --- /dev/null +++ b/src/apps/spool-v2/contracts/viem/SpoolVospool.ts @@ -0,0 +1,1187 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { getContract, GetContractReturnType, PublicClient } from 'viem'; + +export const spoolVospoolAbi = [ + { + inputs: [ + { + internalType: 'contract ISpoolOwner', + name: '_spoolOwner', + type: 'address', + }, + { + internalType: 'uint256', + name: '_firstTrancheEndTime', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Approval', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'source', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'Burned', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint16', + name: 'lastUpdatedTrancheIndex', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint48', + name: 'totalMaturedVotingPower', + type: 'uint48', + }, + { + indexed: false, + internalType: 'uint48', + name: 'totalMaturingAmount', + type: 'uint48', + }, + { + indexed: false, + internalType: 'uint56', + name: 'totalRawUnmaturedVotingPower', + type: 'uint56', + }, + ], + name: 'GlobalGradualUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'source', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'bool', + name: 'burnAll', + type: 'bool', + }, + ], + name: 'GradualBurned', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'GradualMinted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'minter', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'set', + type: 'bool', + }, + ], + name: 'GradualMinterSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'Minted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'minter', + type: 'address', + }, + { + indexed: false, + internalType: 'bool', + name: 'set', + type: 'bool', + }, + ], + name: 'MinterSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'from', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'to', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'value', + type: 'uint256', + }, + ], + name: 'Transfer', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'user', + type: 'address', + }, + { + indexed: true, + internalType: 'uint16', + name: 'lastUpdatedTrancheIndex', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint48', + name: 'maturedVotingPower', + type: 'uint48', + }, + { + indexed: false, + internalType: 'uint48', + name: 'maturingAmount', + type: 'uint48', + }, + { + indexed: false, + internalType: 'uint56', + name: 'rawUnmaturedVotingPower', + type: 'uint56', + }, + ], + name: 'UserGradualUpdated', + type: 'event', + }, + { + inputs: [], + name: 'FULL_POWER_TIME', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'FULL_POWER_TRANCHES_COUNT', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'TRANCHE_TIME', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'allowance', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'approve', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'balanceOf', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'burn', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'from', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'bool', + name: 'burnAll', + type: 'bool', + }, + ], + name: 'burnGradual', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'decimals', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'firstTrancheStartTime', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getCurrentTrancheIndex', + outputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getGlobalGradual', + outputs: [ + { + components: [ + { + internalType: 'uint48', + name: 'totalMaturedVotingPower', + type: 'uint48', + }, + { + internalType: 'uint48', + name: 'totalMaturingAmount', + type: 'uint48', + }, + { + internalType: 'uint56', + name: 'totalRawUnmaturedVotingPower', + type: 'uint56', + }, + { + internalType: 'uint16', + name: 'lastUpdatedTrancheIndex', + type: 'uint16', + }, + ], + internalType: 'struct GlobalGradual', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLastFinishedTrancheIndex', + outputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getNextTrancheEndTime', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getNotUpdatedGlobalGradual', + outputs: [ + { + components: [ + { + internalType: 'uint48', + name: 'totalMaturedVotingPower', + type: 'uint48', + }, + { + internalType: 'uint48', + name: 'totalMaturingAmount', + type: 'uint48', + }, + { + internalType: 'uint56', + name: 'totalRawUnmaturedVotingPower', + type: 'uint56', + }, + { + internalType: 'uint16', + name: 'lastUpdatedTrancheIndex', + type: 'uint16', + }, + ], + internalType: 'struct GlobalGradual', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + ], + name: 'getNotUpdatedUserGradual', + outputs: [ + { + components: [ + { + internalType: 'uint48', + name: 'maturedVotingPower', + type: 'uint48', + }, + { + internalType: 'uint48', + name: 'maturingAmount', + type: 'uint48', + }, + { + internalType: 'uint56', + name: 'rawUnmaturedVotingPower', + type: 'uint56', + }, + { + components: [ + { + internalType: 'uint16', + name: 'arrayIndex', + type: 'uint16', + }, + { + internalType: 'uint8', + name: 'position', + type: 'uint8', + }, + ], + internalType: 'struct UserTranchePosition', + name: 'oldestTranchePosition', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint16', + name: 'arrayIndex', + type: 'uint16', + }, + { + internalType: 'uint8', + name: 'position', + type: 'uint8', + }, + ], + internalType: 'struct UserTranchePosition', + name: 'latestTranchePosition', + type: 'tuple', + }, + { + internalType: 'uint16', + name: 'lastUpdatedTrancheIndex', + type: 'uint16', + }, + ], + internalType: 'struct UserGradual', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getTotalGradualVotingPower', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'trancheIndex', + type: 'uint256', + }, + ], + name: 'getTrancheEndTime', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'time', + type: 'uint256', + }, + ], + name: 'getTrancheIndex', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + ], + name: 'getUserGradual', + outputs: [ + { + components: [ + { + internalType: 'uint48', + name: 'maturedVotingPower', + type: 'uint48', + }, + { + internalType: 'uint48', + name: 'maturingAmount', + type: 'uint48', + }, + { + internalType: 'uint56', + name: 'rawUnmaturedVotingPower', + type: 'uint56', + }, + { + components: [ + { + internalType: 'uint16', + name: 'arrayIndex', + type: 'uint16', + }, + { + internalType: 'uint8', + name: 'position', + type: 'uint8', + }, + ], + internalType: 'struct UserTranchePosition', + name: 'oldestTranchePosition', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint16', + name: 'arrayIndex', + type: 'uint16', + }, + { + internalType: 'uint8', + name: 'position', + type: 'uint8', + }, + ], + internalType: 'struct UserTranchePosition', + name: 'latestTranchePosition', + type: 'tuple', + }, + { + internalType: 'uint16', + name: 'lastUpdatedTrancheIndex', + type: 'uint16', + }, + ], + internalType: 'struct UserGradual', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + ], + name: 'getUserGradualVotingPower', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'gradualMinters', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'indexedGlobalTranches', + outputs: [ + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + ], + internalType: 'struct Tranche', + name: 'zero', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + ], + internalType: 'struct Tranche', + name: 'one', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + ], + internalType: 'struct Tranche', + name: 'two', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + ], + internalType: 'struct Tranche', + name: 'three', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + ], + internalType: 'struct Tranche', + name: 'four', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'mint', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'to', + type: 'address', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'mintGradual', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'minters', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'name', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_gradualMinter', + type: 'address', + }, + { + internalType: 'bool', + name: '_set', + type: 'bool', + }, + ], + name: 'setGradualMinter', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_minter', + type: 'address', + }, + { + internalType: 'bool', + name: '_set', + type: 'bool', + }, + ], + name: 'setMinter', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'symbol', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalInstantPower', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'totalSupply', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'transfer', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'transferFrom', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'user', + type: 'address', + }, + ], + name: 'updateUserVotingPower', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'updateVotingPower', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + name: 'userInstantPower', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + name: 'userTranches', + outputs: [ + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + { + internalType: 'uint16', + name: 'index', + type: 'uint16', + }, + ], + internalType: 'struct UserTranche', + name: 'zero', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + { + internalType: 'uint16', + name: 'index', + type: 'uint16', + }, + ], + internalType: 'struct UserTranche', + name: 'one', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + { + internalType: 'uint16', + name: 'index', + type: 'uint16', + }, + ], + internalType: 'struct UserTranche', + name: 'two', + type: 'tuple', + }, + { + components: [ + { + internalType: 'uint48', + name: 'amount', + type: 'uint48', + }, + { + internalType: 'uint16', + name: 'index', + type: 'uint16', + }, + ], + internalType: 'struct UserTranche', + name: 'three', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export type SpoolVospool = typeof spoolVospoolAbi; +export type SpoolVospoolContract = GetContractReturnType; + +export class SpoolVospool__factory { + static connect(address: string, client: PublicClient) { + return getContract({ address, abi: spoolVospoolAbi, publicClient: client }); + } +} diff --git a/src/apps/spool-v2/contracts/viem/StrategyRegistry.ts b/src/apps/spool-v2/contracts/viem/StrategyRegistry.ts new file mode 100644 index 000000000..1d932441b --- /dev/null +++ b/src/apps/spool-v2/contracts/viem/StrategyRegistry.ts @@ -0,0 +1,815 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { getContract, GetContractReturnType, PublicClient } from 'viem'; + +export const strategyRegistryAbi = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'ecosystemFeeReceiver', + type: 'address', + }, + ], + name: 'EcosystemFeeReceiverSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'feePct', + type: 'uint256', + }, + ], + name: 'EcosystemFeeSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'wallet', + type: 'address', + }, + ], + name: 'EmergencyWithdrawalWalletSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + indexed: false, + internalType: 'int256', + name: 'apy', + type: 'int256', + }, + ], + name: 'StrategyApyUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'dhwIndex', + type: 'uint256', + }, + { + components: [ + { + internalType: 'uint256', + name: 'sharesMinted', + type: 'uint256', + }, + { + internalType: 'uint256[]', + name: 'assetsWithdrawn', + type: 'uint256[]', + }, + { + internalType: 'int256', + name: 'yieldPercentage', + type: 'int256', + }, + { + internalType: 'uint256', + name: 'valueAtDhw', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'totalSstsAtDhw', + type: 'uint256', + }, + ], + indexed: false, + internalType: 'struct DhwInfo', + name: 'dhwInfo', + type: 'tuple', + }, + ], + name: 'StrategyDhw', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'StrategyRegistered', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'StrategyRemoved', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'shares', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256[]', + name: 'assetsWithdrawn', + type: 'uint256[]', + }, + ], + name: 'StrategySharesFastRedeemed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'shares', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256[]', + name: 'assetsWithdrawn', + type: 'uint256[]', + }, + ], + name: 'StrategySharesRedeemed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'treasuryFeeReceiver', + type: 'address', + }, + ], + name: 'TreasuryFeeReceiverSet', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'feePct', + type: 'uint256', + }, + ], + name: 'TreasuryFeeSet', + type: 'event', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint256[][]', + name: 'amounts', + type: 'uint256[][]', + }, + ], + name: 'addDeposits', + outputs: [ + { + internalType: 'uint16a16', + name: 'strategyIndexes', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'strategyShares', + type: 'uint256[]', + }, + ], + name: 'addWithdrawals', + outputs: [ + { + internalType: 'uint16a16', + name: 'strategyIndexes', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'assetRatioAtLastDhw', + outputs: [ + { + internalType: 'uint256[]', + name: 'assetRatio', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint16a16', + name: 'dhwIndexes', + type: 'uint256', + }, + { + internalType: 'uint256[]', + name: 'strategyShares', + type: 'uint256[]', + }, + ], + name: 'claimWithdrawals', + outputs: [ + { + internalType: 'uint256[]', + name: 'assetsWithdrawn', + type: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + ], + name: 'currentIndex', + outputs: [ + { + internalType: 'uint256[]', + name: 'dhwIndexes', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + internalType: 'uint256', + name: 'dhwIndex', + type: 'uint256', + }, + ], + name: 'depositedAssets', + outputs: [ + { + internalType: 'uint256[]', + name: 'assets', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint16a16', + name: 'dhwIndexes', + type: 'uint256', + }, + ], + name: 'dhwTimestamps', + outputs: [ + { + internalType: 'uint256[]', + name: 'timestamps', + type: 'uint256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'address[][]', + name: 'strategies', + type: 'address[][]', + }, + { + components: [ + { + internalType: 'address', + name: 'swapTarget', + type: 'address', + }, + { + internalType: 'address', + name: 'token', + type: 'address', + }, + { + internalType: 'bytes', + name: 'swapCallData', + type: 'bytes', + }, + ], + internalType: 'struct SwapInfo[][][]', + name: 'swapInfo', + type: 'tuple[][][]', + }, + { + components: [ + { + internalType: 'address', + name: 'swapTarget', + type: 'address', + }, + { + internalType: 'address', + name: 'token', + type: 'address', + }, + { + internalType: 'bytes', + name: 'swapCallData', + type: 'bytes', + }, + ], + internalType: 'struct SwapInfo[][][]', + name: 'compoundSwapInfo', + type: 'tuple[][][]', + }, + { + internalType: 'uint256[][][]', + name: 'strategySlippages', + type: 'uint256[][][]', + }, + { + internalType: 'int256[][]', + name: 'baseYields', + type: 'int256[][]', + }, + { + internalType: 'address[]', + name: 'tokens', + type: 'address[]', + }, + { + internalType: 'uint256[2][]', + name: 'exchangeRateSlippages', + type: 'uint256[2][]', + }, + { + internalType: 'uint256', + name: 'validUntil', + type: 'uint256', + }, + ], + internalType: 'struct DoHardWorkParameterBag', + name: 'dhwParams', + type: 'tuple', + }, + ], + name: 'doHardWork', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'emergencyWithdrawalWallet', + outputs: [ + { + internalType: 'address', + name: 'emergencyWithdrawalWallet', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint16a16', + name: 'dhwIndexes', + type: 'uint256', + }, + ], + name: 'getDhwYield', + outputs: [ + { + internalType: 'int256[]', + name: 'yields', + type: 'int256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'platformFees', + outputs: [ + { + components: [ + { + internalType: 'address', + name: 'ecosystemFeeReceiver', + type: 'address', + }, + { + internalType: 'uint96', + name: 'ecosystemFeePct', + type: 'uint96', + }, + { + internalType: 'address', + name: 'treasuryFeeReceiver', + type: 'address', + }, + { + internalType: 'uint96', + name: 'treasuryFeePct', + type: 'uint96', + }, + ], + internalType: 'struct PlatformFees', + name: 'fees', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'strategyShares', + type: 'uint256[]', + }, + { + internalType: 'address[]', + name: 'assetGroup', + type: 'address[]', + }, + { + internalType: 'uint256[][]', + name: 'withdrawalSlippages', + type: 'uint256[][]', + }, + ], + internalType: 'struct RedeemFastParameterBag', + name: 'redeemFastParams', + type: 'tuple', + }, + ], + name: 'redeemFast', + outputs: [ + { + internalType: 'uint256[]', + name: 'withdrawnAssets', + type: 'uint256[]', + }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint256[]', + name: 'shares', + type: 'uint256[]', + }, + { + internalType: 'uint256[][]', + name: 'withdrawalSlippages', + type: 'uint256[][]', + }, + ], + name: 'redeemStrategyShares', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + internalType: 'int256', + name: 'apy', + type: 'int256', + }, + ], + name: 'registerStrategy', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'strategy', + type: 'address', + }, + ], + name: 'removeStrategy', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint96', + name: 'ecosystemFeePct', + type: 'uint96', + }, + ], + name: 'setEcosystemFee', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'ecosystemFeeReceiver', + type: 'address', + }, + ], + name: 'setEcosystemFeeReceiver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint96', + name: 'treasuryFeePct', + type: 'uint96', + }, + ], + name: 'setTreasuryFee', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'treasuryFeeReceiver', + type: 'address', + }, + ], + name: 'setTreasuryFeeReceiver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'strategy', + type: 'address', + }, + { + internalType: 'uint256', + name: 'dhwIndex', + type: 'uint256', + }, + ], + name: 'sharesRedeemed', + outputs: [ + { + internalType: 'uint256', + name: 'shares', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + ], + name: 'strategyAPYs', + outputs: [ + { + internalType: 'int256[]', + name: 'apys', + type: 'int256[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address[]', + name: 'strategies', + type: 'address[]', + }, + { + internalType: 'uint16a16', + name: 'dhwIndexes', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'assetGroupLength', + type: 'uint256', + }, + ], + name: 'strategyAtIndexBatch', + outputs: [ + { + components: [ + { + internalType: 'uint256[]', + name: 'exchangeRates', + type: 'uint256[]', + }, + { + internalType: 'uint256[]', + name: 'assetsDeposited', + type: 'uint256[]', + }, + { + internalType: 'uint256', + name: 'sharesMinted', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'totalSSTs', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'totalStrategyValue', + type: 'uint256', + }, + { + internalType: 'int256', + name: 'dhwYields', + type: 'int256', + }, + ], + internalType: 'struct StrategyAtIndex[]', + name: 'states', + type: 'tuple[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +export type StrategyRegistry = typeof strategyRegistryAbi; +export type StrategyRegistryContract = GetContractReturnType; + +export class StrategyRegistry__factory { + static connect(address: string, client: PublicClient) { + return getContract({ address, abi: strategyRegistryAbi, publicClient: client }); + } +} diff --git a/src/apps/spool-v2/contracts/viem/index.ts b/src/apps/spool-v2/contracts/viem/index.ts new file mode 100644 index 000000000..404621243 --- /dev/null +++ b/src/apps/spool-v2/contracts/viem/index.ts @@ -0,0 +1,15 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ + +export type { SpoolLens } from './SpoolLens'; +export type { SpoolStaking } from './SpoolStaking'; +export type { SpoolVault } from './SpoolVault'; +export type { SpoolVospool } from './SpoolVospool'; +export type { StrategyRegistry } from './StrategyRegistry'; + +export { SpoolLens__factory } from './SpoolLens'; +export { SpoolStaking__factory } from './SpoolStaking'; +export { SpoolVault__factory } from './SpoolVault'; +export { SpoolVospool__factory } from './SpoolVospool'; +export { StrategyRegistry__factory } from './StrategyRegistry'; diff --git a/src/apps/spool-v2/ethereum/spool-v2.constants.ts b/src/apps/spool-v2/ethereum/spool-v2.constants.ts new file mode 100644 index 000000000..5340b59ac --- /dev/null +++ b/src/apps/spool-v2/ethereum/spool-v2.constants.ts @@ -0,0 +1,208 @@ +import { gql } from 'graphql-request'; + +export const SPOOL_ADDRESS = '0x40803cea2b2a32bda1be61d3604af6a814e70976'; +export const VOSPOOL_ADDRESS = '0xaf56d16a7fe479f2fcd48ff567ff589cb2d2a0e9'; +export const STAKING_ADDRESS = '0xc3160c5cc63b6116dd182faa8393d3ad9313e213'; +export const SUBGRAPH_API_BASE_URL = 'https://api.studio.thegraph.com/query/41372/spool-v2_mainnet/version/latest'; +export const SUBGRAPH_V1_API_BASE_URL = 'https://api.thegraph.com/subgraphs/name/spoolfi/spool?source=zapper'; +export const SPOOL_LENS_ADDRESS = '0x8aa6174333f75421903b2b5c70ddf8da5d84f74f'; +export const STRATEGY_REGISTRY_ADDRESS = '0x554c6bcb54656390aca0a0af38ca954dbe653f15'; +export const APY_DECIMALS = '18'; + +export const SPOOL_STAKED_QUERY = gql` + query getStaking($address: String!) { + userSpoolStaking(id: $address) { + id + spoolStaked + } + } +`; + +export const REWARDS_QUERY = gql` + query { + smartVaultRewardTokens { + id + token { + id + name + symbol + decimals + } + isRemoved + endTime + startTime + } + } +`; + +export const VAULTS_AND_STRATEGIES_QUERY = gql` + { + smartVaults { + id + smartVaultStrategies { + id + isRemoved + strategy { + id + } + } + } + } +`; + +export const ASSET_GROUPS_QUERY = gql` + { + smartVaults { + assetGroup { + id + assetGroupTokens { + token { + id + decimals + } + } + } + } + } +`; + +export const STRATEGY_ALLOCATION_QUERY = gql` + { + smartVaults { + id + smartVaultStrategies { + id + allocation + isRemoved + strategy { + id + } + } + } + } +`; + +export const GLOBAL_FEES_QUERY = gql` + { + globals { + treasuryFee + ecosystemFee + } + } +`; + +export const FEES_QUERY = gql` + { + smartVaults { + id + smartVaultFees { + performanceFeePercentage + depositFeePercentage + managementFeePercentage + } + } + } +`; + +export const DEPOSITS_QUERY = gql` + { + smartVaults { + smartVaultFlushes { + SmartVaultWithdrawalNFTs { + svtWithdrawn + } + id + isExecuted + strategyDHWs { + isExecuted + id + } + } + } + } +`; + +export const REWARDS_APY_QUERY = gql` + query ($now: String!) { + smartVaults { + smartVaultRewardTokens(where: { endTime_gt: $now }) { + id + rewardRate + token { + decimals + id + } + } + id + } + } +`; + +export const USER_NFTS_QUERY = gql` + query ($address: String!, $smartVault: String!) { + user(id: $address) { + id + smartVaultDepositNFTs(where: { smartVault: $smartVault }) { + smartVault { + id + } + nftId + } + } + } +`; + +export const VAULTS_QUERY = gql` + query { + smartVaults { + id + assetGroup { + assetGroupTokens { + token { + id + } + } + } + id + name + riskTolerance + riskProvider { + id + } + smartVaultFees { + id + depositFeePercentage + managementFeePercentage + performanceFeePercentage + } + smartVaultStrategies { + id + isRemoved + allocation + strategy { + id + riskScores { + riskScore + riskProvider { + id + } + } + } + } + smartVaultRewardTokens { + id + token { + id + name + symbol + decimals + } + rewardRate + startTime + endTime + totalAmount + isRemoved + } + } + } +`; diff --git a/src/apps/spool-v2/ethereum/spool-v2.staking.contract-position-fetcher.ts b/src/apps/spool-v2/ethereum/spool-v2.staking.contract-position-fetcher.ts new file mode 100644 index 000000000..4d454a84b --- /dev/null +++ b/src/apps/spool-v2/ethereum/spool-v2.staking.contract-position-fetcher.ts @@ -0,0 +1,154 @@ +import { Inject } from '@nestjs/common'; +import { BigNumber } from 'ethers'; +import { parseEther } from 'ethers/lib/utils'; +import _ from 'lodash'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; +import { buildDollarDisplayItem, buildNumberDisplayItem } from '~app-toolkit/helpers/presentation/display-item.present'; +import { getLabelFromToken } from '~app-toolkit/helpers/presentation/image.present'; +import { gqlFetch } from '~app-toolkit/helpers/the-graph.helper'; +import { MetaType } from '~position/position.interface'; +import { isClaimable, isSupplied } from '~position/position.utils'; +import { ContractPositionTemplatePositionFetcher } from '~position/template/contract-position.template.position-fetcher'; +import { + GetDataPropsParams, + GetDisplayPropsParams, + GetTokenBalancesParams, +} from '~position/template/contract-position.template.types'; + +import { SpoolV2ViemContractFactory } from '../contracts'; +import { SpoolStaking } from '../contracts/viem'; + +import { + REWARDS_QUERY, + SPOOL_ADDRESS, + SPOOL_STAKED_QUERY, + STAKING_ADDRESS, + SUBGRAPH_API_BASE_URL, + SUBGRAPH_V1_API_BASE_URL, + VOSPOOL_ADDRESS, +} from './spool-v2.constants'; +import { StakingReward, UserSpoolStaking } from './spool-v2.types'; + +type SpoolStakingDataProps = { + tvl: number; + spoolStaked: number; + totalAccVoSpool: number; +}; + +@PositionTemplate() +export class EthereumSpoolV2StakingContractPositionFetcher extends ContractPositionTemplatePositionFetcher< + SpoolStaking, + SpoolStakingDataProps +> { + groupLabel = 'Staking'; + + constructor( + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(SpoolV2ViemContractFactory) protected readonly contractFactory: SpoolV2ViemContractFactory, + ) { + super(appToolkit); + } + + getContract(address: string) { + return this.contractFactory.spoolStaking({ address, network: this.network }); + } + + async getDefinitions() { + return [{ address: STAKING_ADDRESS }]; + } + + async getTokenDefinitions() { + const now = parseInt(String(new Date().getTime() / 1000)); + const stakingRewards = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: REWARDS_QUERY, + }); + + let rewardAddresses: string[] = []; + // SpoolStaking can emit arbitrary tokens; VoSpoolRewards always emits SPOOL + if (stakingRewards.stakingRewardTokens) { + rewardAddresses = _.uniq( + stakingRewards.stakingRewardTokens + .filter(reward => !reward.isRemoved && parseInt(reward.startTime) >= now && parseInt(reward.endTime) <= now) + .map(reward => reward.token.id) + .concat([SPOOL_ADDRESS]), + ); + } + + return [ + { metaType: MetaType.SUPPLIED, address: SPOOL_ADDRESS, network: this.network }, + { metaType: MetaType.VESTING, address: VOSPOOL_ADDRESS, network: this.network }, + ...rewardAddresses.map(address => ({ metaType: MetaType.CLAIMABLE, address, network: this.network })), + ]; + } + + async getDataProps({ multicall, contractPosition }: GetDataPropsParams) { + const [spoolToken, voSpoolToken] = contractPosition.tokens; + const spoolContract = this.appToolkit.globalViemContracts.erc20(spoolToken); + const voSpoolContract = this.contractFactory.spoolVospool(voSpoolToken); + + const totalStakedRaw = await multicall.wrap(spoolContract).read.balanceOf([STAKING_ADDRESS]); + const votingPowerRaw = await multicall.wrap(voSpoolContract).read.getTotalGradualVotingPower(); + + const totalAccVoSpool = Number(votingPowerRaw) / 10 ** voSpoolToken.decimals; + const spoolStaked = Number(totalStakedRaw) / 10 ** spoolToken.decimals; + const tvl = spoolStaked * spoolToken.price; + + return { tvl, spoolStaked, totalAccVoSpool }; + } + + async getLabel({ contractPosition }: GetDisplayPropsParams) { + return `${getLabelFromToken(contractPosition.tokens[0])} Staking`; + } + + async getStatsItems({ contractPosition }: GetDisplayPropsParams) { + return [ + { label: 'TVL', value: buildDollarDisplayItem(contractPosition.dataProps.tvl) }, + { label: 'SPOOL Staked', value: buildNumberDisplayItem(contractPosition.dataProps.spoolStaked) }, + { label: 'voSPOOL Accumulated', value: buildNumberDisplayItem(contractPosition.dataProps.totalAccVoSpool) }, + ]; + } + + async getTokenBalancesPerPosition({ + address, + contractPosition, + multicall, + }: GetTokenBalancesParams) { + const suppliedToken = contractPosition.tokens.find(isSupplied)!; + const rewardTokens = contractPosition.tokens.filter(isClaimable); + + const staking = this.contractFactory.spoolStaking({ address: STAKING_ADDRESS, network: this.network }); + const voSpool = this.contractFactory.spoolVospool({ address: VOSPOOL_ADDRESS, network: this.network }); + + const [votingPowerRaw, voSpoolRewards, ...tokenRewards] = await Promise.all([ + multicall.wrap(voSpool).read.getUserGradualVotingPower([address]), + multicall + .wrap(staking) + .simulate.getUpdatedVoSpoolRewardAmount({ account: address }) + .then(v => v.result), + ...rewardTokens.map(reward => multicall.wrap(staking).read.earned([reward.address, address])), + ]); + + const stakedSpool = await gqlFetch({ + endpoint: SUBGRAPH_V1_API_BASE_URL, + query: SPOOL_STAKED_QUERY, + variables: { address }, + }); + + const stakedAmount = parseEther(stakedSpool?.userSpoolStaking?.spoolStaked || '0').toString(); + + const rewardBalances = rewardTokens.map((token, idx) => { + // Add voSPOOL rewards (always in SPOOL) + const rewards = + token.address.toLowerCase() == suppliedToken.address.toLowerCase() + ? BigNumber.from(tokenRewards[idx]).add(voSpoolRewards) + : BigNumber.from(tokenRewards[idx]); + + return rewards.toString(); + }); + + return [stakedAmount, votingPowerRaw.toString(), ...rewardBalances]; + } +} diff --git a/src/apps/spool-v2/ethereum/spool-v2.types.ts b/src/apps/spool-v2/ethereum/spool-v2.types.ts new file mode 100644 index 000000000..41f2ddd61 --- /dev/null +++ b/src/apps/spool-v2/ethereum/spool-v2.types.ts @@ -0,0 +1,291 @@ +import { BigNumber } from 'ethers'; + +export type SpoolVaults = { + smartVaults: VaultDetails[]; +}; + +export type VaultDetails = { + id: string; + name: string; + riskTolerance: string; + riskProvider: { + id: string; + }; + assetGroup: { + assetGroupTokens: { + token: { + id: string; + }; + }[]; + }; + smartVaultFees: { + depositFeePercentage: string; + managementFeePercentage: string; + performanceFeePercentage: string; + }; + smartVaultStrategies: { + id: string; + allocation: string; + isRemoved: boolean; + strategy: { + id: string; + riskScores: { + riskScore: string; + riskProvider: { + id: string; + }; + }[]; + }; + }[]; + smartVaultRewardTokens: { + id: string; + token: { + id: string; + name: string; + symbol: string; + decimals: number; + }; + rewardRate: number; + totalAmount: string; + isRemoved: number; + }[]; +}; + +export type Globals = { + globals: { + treasuryFee: string; + ecosystemFee: string; + }; +}; + +export type VaultsAnalytics = { + smartVaults: { + id: string; + tvr: string; + tvrUsd: string; + baseApy: string; + rewardsApy: string[]; + adjustedApy: string; + vaultAssets: BigNumber[]; + }[]; +}; + +export type StakingReward = { + stakingRewardTokens: { + id: string; + token: { + id: string; + name: string; + symbol: string; + decimals: number; + }; + isRemoved: boolean; + endTime: string; + startTime: string; + }[]; +}; + +export type UserSpoolStaking = { + userSpoolStaking: { + id: string; + spoolStaked: string; + }; +}; + +export interface RewardApyReturnType { + vaultId: string; + rewardTokens: { + id: string; + rewardRate: string; + decimals: number; + apy: string; + }[]; +} +export interface SmartVaultRewards { + smartVaultRewardTokens: RewardToken[]; + id: string; +} + +interface RewardToken { + id: string; + rewardRate: string; + token: { + decimals: number; + id: string; + }; +} + +export type rewardsApyQueryData = { + smartVaults: { + smartVaultRewardTokens: { + id: string; + rewardRate: string; + token: { + decimals: number; + id: string; + }; + }[]; + id: string; + }[]; +}; + +export type userNftQueryData = { + user: { + id: string; + smartVaultDepositNFTs: { + smartVault: { + id: string; + }; + nftId: string; + }[]; + }; +}; + +export type SpoolVaultDefinition = { + address: string; + name: string; + riskModel: string; + suppliedTokenAddresses: string[]; + rewardTokenAddresses: string[] | null; + strategyAddresses: string[]; + stats: { + riskTolerance: number; + tvr: string; + tvrUsd: string; + fees: number; + adjustedApy: number; + rewardsApy: string[]; + baseApy: number; + vaultAssets: string[]; + }; +}; + +export type SpoolVaultDataProps = { + totalValueRoutedUsd: string; + baseApy: number; + adjustedApy: number; + totalValueRoutedSvt: string; + rewardsApy: string[]; + strategies: string[]; + riskScore: string; + riskTolerance: number; + vaultAssets: string[]; +}; + +export interface TvrType { + vault: Vault; + tvr: BigNumber; +} + +export interface vaultQueryData { + smartVaults: { + id: string; + smartVaultStrategies: { + id: string; + isRemoved: boolean; + strategy: { + id: string; + }; + }[]; + }[]; +} + +export interface depositsQueryData { + smartVaults: { + smartVaultFlushes: { + SmartVaultWithdrawalNFTs: { + svtWithdrawn: string; + }[]; + id: string; + isExecuted: boolean; + strategyDHWs: { + isExecuted: boolean; + id: string; + }[]; + }[]; + }[]; +} + +export interface BaseApy { + vault: Vault; + baseApy: string; +} + +export interface RewardsApy { + vault: Vault; + rewardsApys: string[]; +} +export interface assetGroupsData { + smartVaults: { + assetGroup: { + id: string; + assetGroupTokens: { + token: { + id: string; + decimals: number; + }; + }[]; + }; + }[]; +} +export interface VaultAsset { + vault: Vault; + assets: BigNumber[]; +} + +export interface allocationData { + smartVaults: { + id: string; + smartVaultStrategies: { + id: string; + allocation: string; + isRemoved: boolean; + strategy: { + id: string; + }; + }[]; + }[]; +} +export interface AdjustedApy { + vault: Vault; + adjustedApy: string; +} +export interface TokenPrice { + token: string; + price: string; +} +export interface globalFeesData { + globals: { + treasuryFee: string; + ecosystemFee: string; + }[]; +} +export interface feesData { + smartVaults: { + id: string; + smartVaultFees: { + performanceFeePercentage: string; + depositFeePercentage: string; + managementFeePercentage: string; + }; + }[]; +} +export interface TvrUsd { + vault: Vault; + tvrUsd: string; +} + +export interface StrategyAllocation { + vault: Vault; + strategies: Strategy[]; + allocations: string[]; +} + +export interface Strategy { + id: string; +} + +export interface Vault { + id: string; + strategies: Strategy[]; +} diff --git a/src/apps/spool-v2/ethereum/spool-v2.vault.contract-position-fetcher.ts b/src/apps/spool-v2/ethereum/spool-v2.vault.contract-position-fetcher.ts new file mode 100644 index 000000000..834b0b819 --- /dev/null +++ b/src/apps/spool-v2/ethereum/spool-v2.vault.contract-position-fetcher.ts @@ -0,0 +1,739 @@ +import { Inject } from '@nestjs/common'; +import { BigNumber, BigNumberish, ethers } from 'ethers'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; +import { + buildDollarDisplayItem, + buildPercentageDisplayItem, + buildStringDisplayItem, +} from '~app-toolkit/helpers/presentation/display-item.present'; +import { getLabelFromToken } from '~app-toolkit/helpers/presentation/image.present'; +import { gqlFetch } from '~app-toolkit/helpers/the-graph.helper'; +import { StrategyRegistryContract } from '~apps/spool-v2/contracts/viem/StrategyRegistry'; +import { MetaType } from '~position/position.interface'; +import { ContractPositionTemplatePositionFetcher } from '~position/template/contract-position.template.position-fetcher'; +import { + GetDataPropsParams, + GetDisplayPropsParams, + GetTokenBalancesParams, + GetTokenDefinitionsParams, +} from '~position/template/contract-position.template.types'; + +import { SpoolV2ViemContractFactory } from '../contracts'; +import { SpoolVault } from '../contracts/viem'; + +import { + APY_DECIMALS, + ASSET_GROUPS_QUERY, + DEPOSITS_QUERY, + FEES_QUERY, + GLOBAL_FEES_QUERY, + REWARDS_APY_QUERY, + SPOOL_LENS_ADDRESS, + STRATEGY_ALLOCATION_QUERY, + STRATEGY_REGISTRY_ADDRESS, + SUBGRAPH_API_BASE_URL, + USER_NFTS_QUERY, + VAULTS_AND_STRATEGIES_QUERY, + VAULTS_QUERY, +} from './spool-v2.constants'; +import { + AdjustedApy, + allocationData, + assetGroupsData, + BaseApy, + depositsQueryData, + feesData, + globalFeesData, + Globals, + RewardApyReturnType, + RewardsApy, + rewardsApyQueryData, + SmartVaultRewards, + SpoolVaultDataProps, + SpoolVaultDefinition, + SpoolVaults, + Strategy, + StrategyAllocation, + TokenPrice, + TvrType, + TvrUsd, + userNftQueryData, + Vault, + VaultAsset, + VaultDetails, + vaultQueryData, + VaultsAnalytics, +} from './spool-v2.types'; + +const riskModels = { '0xc216ad6280f4fa92a5159ef383a1206d432481c8': 'Spool Labs' }; + +@PositionTemplate() +export class EthereumSpoolV2VaultContractPositionFetcher extends ContractPositionTemplatePositionFetcher< + SpoolVault, + SpoolVaultDataProps, + SpoolVaultDefinition +> { + groupLabel = 'Vaults'; + + constructor( + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(SpoolV2ViemContractFactory) protected readonly spoolV2ContractFactory: SpoolV2ViemContractFactory, + ) { + super(appToolkit); + } + + getContract(_address: string) { + return this.spoolV2ContractFactory.spoolVault({ address: _address, network: this.network }); + } + + async getDataProps({ definition }: GetDataPropsParams) { + return { + totalValueRoutedSvt: definition.stats.tvr, + totalValueRoutedUsd: definition.stats.tvrUsd, + liquidity: parseFloat(definition.stats.tvrUsd), + baseApy: definition.stats.baseApy, + adjustedApy: definition.stats.adjustedApy, + rewardsApy: definition.stats.rewardsApy, + strategies: definition.strategyAddresses, + riskScore: this.getRiskScoreString(definition.stats.riskTolerance), + riskTolerance: definition.stats.riskTolerance, + vaultAssets: definition.stats.vaultAssets, + }; + } + + async getDefinitions() { + const vaultsData = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: VAULTS_QUERY, + }); + + const fees = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: GLOBAL_FEES_QUERY, + }); + + const vaultAnalytics = await this.getVaultsAnalytics(); + + return vaultsData.smartVaults.map(vault => ({ + address: vault.id.toLowerCase(), + name: vault.name, + riskModel: vault.riskProvider ? vault.riskProvider.id : '', + suppliedTokenAddresses: vault.assetGroup.assetGroupTokens.map(agt => agt.token.id.toLowerCase()), + rewardTokenAddresses: + vault.smartVaultRewardTokens.length > 0 + ? vault.smartVaultRewardTokens.map(rt => rt.token.id.toLowerCase()) + : null, + strategyAddresses: vault.smartVaultStrategies.map(s => s.strategy.id.toLowerCase()), + stats: this.getVaultStats( + vault, + vaultAnalytics.smartVaults.find(va => va.id == vault.id), + fees, + ), + })); + } + async getTokenBalancesPerPosition({ + address, + contractPosition, + }: GetTokenBalancesParams): Promise { + const spoolLens = await this.spoolV2ContractFactory.spoolLens({ + address: SPOOL_LENS_ADDRESS, + network: this.network, + }); + + const userSvtBalance = await spoolLens.read.getUserSVTBalance([ + contractPosition.address, + address, + await this.getUserDepositNFTs(address, contractPosition.address), + ]); + await userSvtBalance; + const userSvtBalanceString = userSvtBalance.toString(); + const tvrSvt = contractPosition.dataProps.totalValueRoutedSvt; + const vaultAssets = contractPosition.dataProps.vaultAssets; + if (vaultAssets.length == 0 || tvrSvt == '0') return new Array(0); + const percentage = parseFloat(userSvtBalanceString) / parseFloat(tvrSvt); + return vaultAssets.map(va => { + const calculated = percentage * parseFloat(ethers.utils.formatEther(va)); + // below the threshold + if (calculated < 1) { + return ethers.constants.Zero; + } + return ethers.utils.parseEther((percentage * parseFloat(ethers.utils.formatEther(va))).toString()); + }); + } + + async getUserDepositNFTs(address: string, smartVault: string): Promise { + const res = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: USER_NFTS_QUERY, + variables: { address, smartVault }, + }); + return res.user.smartVaultDepositNFTs.map(nft => BigInt(nft.nftId)); + } + async getTokenDefinitions({ definition }: GetTokenDefinitionsParams) { + return [ + ...definition.suppliedTokenAddresses.map(address => ({ + metaType: MetaType.SUPPLIED, + address, + network: this.network, + })), + ]; + } + + async getLabel({ definition }: GetDisplayPropsParams) { + return definition.name; + } + + async getSecondaryLabel({ + contractPosition, + }: GetDisplayPropsParams) { + return contractPosition.tokens.map(token => getLabelFromToken(token)).join(', '); + } + async getStatsItems({ definition }: GetDisplayPropsParams) { + return [ + { label: 'APY', value: buildPercentageDisplayItem(definition.stats.adjustedApy) }, + { label: 'TVR', value: buildDollarDisplayItem(Number(definition.stats.tvrUsd)) }, + { label: 'Risk Model', value: buildStringDisplayItem(riskModels[definition.riskModel]) }, + { + label: 'Risk Appetite', + value: buildStringDisplayItem( + `${this.getRiskScoreString(definition.stats.riskTolerance)} (${this.mapRiskToRange( + definition.stats.riskTolerance, + )})`, + ), + }, + { + label: 'Reward Tokens', + value: buildStringDisplayItem( + definition.rewardTokenAddresses?.map(address => address.toString()).join(', '), + ), + }, + ]; + } + /** + * Calculate APY, TVR (total value routed) and fees. + * @param vault + * @param vaultAnalytics + * @param platformFees + */ + getVaultStats( + vault: VaultDetails, + vaultAnalytics: + | { + id: string; + tvr: string; + baseApy: string; + rewardsApy: string[]; + adjustedApy: string; + tvrUsd: string; + vaultAssets: BigNumber[]; + } + | undefined, + platformFees: Globals, + ) { + const fees = + parseFloat(platformFees.globals.ecosystemFee) + + parseFloat(platformFees.globals.treasuryFee) + + parseFloat(vault.smartVaultFees.performanceFeePercentage) + + parseFloat(vault.smartVaultFees.managementFeePercentage); + + let tvr = '0'; + let tvrUsd = '0'; + let adjustedApy = 0.0; + let rewardsApys = ['0']; + let baseApy = 0.0; + let vaultAssets = ['0']; + if (vaultAnalytics) { + tvr = vaultAnalytics.tvr; + tvrUsd = vaultAnalytics.tvrUsd; + adjustedApy = parseFloat(vaultAnalytics.adjustedApy) * 100; + rewardsApys = vaultAnalytics.rewardsApy; + baseApy = parseFloat(vaultAnalytics.baseApy) * 100; + vaultAssets = vaultAnalytics.vaultAssets.map(va => va.toString()); + } + + const riskTolerance = Number(vault.riskTolerance); + + return { + riskTolerance: riskTolerance, + tvr: tvr, + tvrUsd: tvrUsd, + fees: fees, + adjustedApy: adjustedApy, + rewardsApy: rewardsApys, + baseApy: baseApy, + vaultAssets: vaultAssets, + }; + } + + /** + * Fetch vault analytics + */ + async getVaultsAnalytics(): Promise { + const vaults = await this.getVaults(); + + const tvrSvts = await this.getTvrSvts(vaults); + const baseApys = await this.getBaseApys(vaults); + const tvrUsds = await this.getTvrUsds(vaults); + const rewardsApys = await this.getRewardsApys(vaults, tvrUsds); + const adjustedApys = await this.getAdjustedApys(vaults, baseApys, rewardsApys); + const vaultAssets: VaultAsset[] = await this.getVaultAssets(vaults); + const smartVaults = vaults.map(vault => { + const tvrUsd = tvrUsds.find(tvrUsd => tvrUsd.vault.id == vault.id)?.tvrUsd; + const tvr = tvrSvts.find(tvrSvt => tvrSvt.vault.id == vault.id)?.tvr; + const baseApy = baseApys.find(baseApy => baseApy.vault.id == vault.id)?.baseApy; + const rewardsApy = rewardsApys.find(rewardsApy => rewardsApy.vault.id == vault.id)?.rewardsApys; + const adjustedApy = adjustedApys.find(adjustedApy => adjustedApy.vault.id == vault.id)?.adjustedApy; + const vaultAsset = vaultAssets.find(vaultAsset => vaultAsset.vault.id == vault.id); + if (!tvrUsd || !tvr || !baseApy || !rewardsApy || !adjustedApy || !vaultAsset) { + throw new Error('Missing data for vault analytics'); + } + return { + id: vault.id, + tvr: tvr.toString(), + tvrUsd: tvrUsd.toString(), + baseApy: baseApy, + rewardsApy: rewardsApy, + adjustedApy: adjustedApy, + vaultAssets: vaultAsset.assets, + }; + }); + return { + smartVaults: smartVaults, + }; + } + + async getVaults(): Promise { + const data = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: VAULTS_AND_STRATEGIES_QUERY, + }); + if (data === undefined) { + throw new Error('No data returned from queryVaultData'); + } + return this.processVaultData(data); + } + + processVaultData(vaultQueryData: vaultQueryData): Vault[] { + const vaults: Vault[] = []; + for (const vault of vaultQueryData.smartVaults) { + const tempVault: Vault = { id: '', strategies: [] }; + tempVault.id = vault.id.toLowerCase(); + for (const strategy of vault.smartVaultStrategies) { + tempVault.strategies.push({ + id: strategy.strategy.id.toLowerCase(), + }); + } + vaults.push(tempVault); + } + return vaults; + } + + async getTokenPrices(assetGroupsData: assetGroupsData): Promise { + const tokenPrices: TokenPrice[] = []; + for (const smartVault of assetGroupsData.smartVaults) { + for (const token of smartVault.assetGroup.assetGroupTokens) { + const baseToken = await this.appToolkit.getBaseTokenPrice({ + network: this.network, + address: token.token.id, + }); + if (baseToken === null) { + tokenPrices.push({ token: token.token.id, price: '0' }); + } else { + tokenPrices.push({ token: token.token.id, price: baseToken.price.toString() }); + } + } + } + return tokenPrices; + } + + async getTvrUsds(vaults: Vault[]): Promise { + const assetGroupsData = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: ASSET_GROUPS_QUERY, + }); + if (assetGroupsData === undefined) { + throw new Error('No data returned from queryAssetGroupData'); + } + const vaultAssets: VaultAsset[] = await this.getVaultAssets(vaults); + const prices: TokenPrice[] = await this.getTokenPrices(assetGroupsData); + return this.processTvrUsds(vaults, assetGroupsData, vaultAssets, prices); + } + + async getVaultAssets(vaults: Vault[]): Promise { + const spoolLens = this.spoolV2ContractFactory.spoolLens({ address: SPOOL_LENS_ADDRESS, network: this.network }); + const vaultAssets: VaultAsset[] = []; + for (const vault of vaults) { + const assetsBigint: readonly bigint[] = (await spoolLens.simulate.getSmartVaultAssetBalances([vault.id, false])) + .result; + const assets: BigNumber[] = assetsBigint.map(asset => ethers.BigNumber.from(asset)); + vaultAssets.push({ vault: vault, assets: assets }); + } + return vaultAssets; + } + + processTvrUsds( + vaults: Vault[], + assetGroupData: assetGroupsData, + vaultAssets: VaultAsset[], + prices: TokenPrice[], + ): TvrUsd[] { + const tvrUsds: TvrUsd[] = []; + for (const vault of vaults) { + let sum = 0; + for (const assetAmount of vaultAssets[vaults.indexOf(vault)].assets) { + const token = + assetGroupData.smartVaults[vaults.indexOf(vault)].assetGroup.assetGroupTokens[ + vaultAssets[vaults.indexOf(vault)].assets.indexOf(assetAmount) + ].token; + const tokenId = token.id; + const tokenDecimals = token.decimals; + const assetAmountFormatted = Number(ethers.utils.formatUnits(assetAmount, tokenDecimals)); + const tokenPrice = prices.find(tokenPrice => tokenPrice.token == tokenId); + if (tokenPrice == undefined) { + continue; + } + sum += assetAmountFormatted * parseFloat(tokenPrice.price); + } + tvrUsds.push({ vault: vault, tvrUsd: sum.toString() }); + } + return tvrUsds; + } + + async getBaseApys(vaults: Vault[]): Promise { + const decimals = APY_DECIMALS; + + const strategyRegistry = this.spoolV2ContractFactory.strategyRegistry({ + address: STRATEGY_REGISTRY_ADDRESS, + network: this.network, + }); + const strategyAllocations = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: STRATEGY_ALLOCATION_QUERY, + }); + if (strategyAllocations === undefined) { + throw new Error('No data returned from getAllocations'); + } + const strategiesRetn = this.getUniqueStrategyAddresses(vaults); + const stratAPYs = await this.getStrategyApys(strategiesRetn, strategyRegistry); + return this.processBaseApyData(vaults, strategyAllocations, stratAPYs, decimals); + } + + getUniqueStrategyAddresses(vaults: Vault[]): Strategy[] { + const strategiesRetn: Strategy[] = []; + for (const vault of vaults) { + for (const strategy of vault.strategies) { + if (!strategiesRetn.includes(strategy)) { + strategiesRetn.push(strategy); + } + } + } + return strategiesRetn; + } + + async getStrategyApys( + strategyAddresses: Strategy[], + strategyRegistry: StrategyRegistryContract, + ): Promise> { + const strategyIds = strategyAddresses.map(strategy => strategy.id); + const stratAPYsBigint: readonly bigint[] = await strategyRegistry.read.strategyAPYs([strategyIds]); + const stratAPYs: BigNumber[] = stratAPYsBigint.map(stratApy => ethers.BigNumber.from(stratApy)); + const mapReturn = new Map(); + for (const stratApy of stratAPYs) { + mapReturn.set(strategyIds[stratAPYs.indexOf(stratApy)], stratApy); + } + return mapReturn; + } + + processBaseApyData( + vaults: Vault[], + strategyAllocationDatas: allocationData, + strategyAPYs: Map, + decimals: string, + ): BaseApy[] { + const strategyAllocations: StrategyAllocation[] = []; + for (const vault of vaults) { + const allocations: string[] = []; + const strategies: Strategy[] = []; + for (const strategyAllocationData of strategyAllocationDatas.smartVaults) { + if (vault.id == strategyAllocationData.id) { + for (const strategyAllocationDataStrategy of strategyAllocationData.smartVaultStrategies) { + if (!strategyAllocationDataStrategy.isRemoved) { + allocations.push(strategyAllocationDataStrategy.allocation); + } else { + allocations.push('0'); + } + strategies.push({ + id: strategyAllocationDataStrategy.strategy.id.toLowerCase(), + }); + } + } + } + strategyAllocations.push({ + vault: vault, + allocations: allocations, + strategies: strategies, + }); + } + const apys: BigNumber[] = []; + for (const vault of vaults) { + apys[vaults.indexOf(vault)] = ethers.constants.Zero; + for (const strategy of vault.strategies) { + const strategyAPY = strategyAPYs.get(strategy.id); + if (strategyAPY == undefined) { + throw new Error('Strategy APY is undefined'); + } + const x = BigNumber.from( + strategyAllocations[vaults.indexOf(vault)].allocations[vault.strategies.indexOf(strategy)], + ); + apys[vaults.indexOf(vault)] = apys[vaults.indexOf(vault)].add(strategyAPY.mul(x)); + } + } + const baseApys: BaseApy[] = []; + + for (const vault of vaults) { + baseApys.push({ + vault: vault, + baseApy: ((Number(apys[vaults.indexOf(vault)]) / Math.pow(10, Number.parseInt(decimals))) * 100).toString(), + }); + } + + return baseApys; + } + async getRewardsApys(vaults: Vault[], tvrUsds: TvrUsd[]): Promise { + const rewardApysFirstPart = await this.getRewardApys(); + return this.processRewardsApys(rewardApysFirstPart, vaults, tvrUsds); + } + async getRewardApys(): Promise { + const now = Math.floor(Date.now() / 1000).toString(); + const res = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: REWARDS_APY_QUERY, + variables: { now }, + }); + + const data = res.smartVaults as SmartVaultRewards[]; + + return await Promise.all( + data + .filter(vault => vault.smartVaultRewardTokens.length > 0) + .map(async vault => { + return { + vaultId: vault.id, + rewardTokens: await Promise.all( + vault.smartVaultRewardTokens.map(async reward => { + const baseToken = await this.appToolkit.getBaseTokenPrice({ + network: this.network, + address: reward.token.id as string, + }); + let apy = '0'; + if (baseToken != undefined) { + apy = ( + parseFloat( + BigNumber.from(reward.rewardRate) + .div(BigNumber.from(10).pow(reward.token.decimals)) + .mul(BigNumber.from(60)) + .mul(BigNumber.from(60)) + .mul(BigNumber.from(24)) + .mul(BigNumber.from(365)) + .toString(), + ) * baseToken.price + ).toString(); + } + return { + id: reward.token.id, + rewardRate: reward.rewardRate, + decimals: reward.token.decimals, + apy: apy, + }; + }), + ), + }; + }), + ); + } + + processRewardsApys(data: RewardApyReturnType[], vaults: Vault[], tvrUsds: TvrUsd[]): RewardsApy[] { + const decimals = APY_DECIMALS; + const calculationHelper = new Map(); + const result: string[][] = []; + for (const rewardManagerApy of data) { + const vaultAddress = rewardManagerApy.vaultId; + if (calculationHelper.get(vaultAddress) == undefined) { + calculationHelper.set(vaultAddress, []); + } + const insertHelper = calculationHelper.get(vaultAddress); + for (const rewardToken of rewardManagerApy.rewardTokens) { + if (insertHelper != null) { + insertHelper.push(BigNumber.from(BigInt(parseFloat(rewardToken.apy)))); + calculationHelper.set(vaultAddress, insertHelper); + } + } + } + + for (const vault of vaults) { + result[vaults.indexOf(vault)] = []; + for (const rewardManagerApy of data) { + if (vault.id == rewardManagerApy.vaultId) { + const calculationHelperValues = calculationHelper.get(vault.id); + if (calculationHelperValues == undefined) { + throw new Error('calculationHelperValues is undefined'); + } + for (const apy of calculationHelperValues) { + if (tvrUsds[vaults.indexOf(vault)].tvrUsd == '0') { + result[vaults.indexOf(vault)].push('0'); + continue; + } + result[vaults.indexOf(vault)].push( + ( + Number( + apy + .mul(BigNumber.from(10).pow(Number.parseInt(decimals))) + .div(BigNumber.from(tvrUsds[vaults.indexOf(vault)].tvrUsd.split('.')[0])) + .div(BigNumber.from(10).pow(Number.parseInt(decimals))), + ) / Math.pow(10, 18) + ).toString(), + ); + } + } + } + } + + const rewardsApys: RewardsApy[] = []; + for (const vault of vaults) { + rewardsApys[vaults.indexOf(vault)] = { + vault: vault, + rewardsApys: result[vaults.indexOf(vault)], + }; + } + return rewardsApys; + } + + async getAdjustedApys(vaults: Vault[], baseApys: BaseApy[], rewardsApys: RewardsApy[]): Promise { + const globalFeesData = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: GLOBAL_FEES_QUERY, + }); + + const feesData = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: FEES_QUERY, + }); + + if (globalFeesData === undefined) { + throw new Error('No data returned from queryGlobalFeesData'); + } + if (feesData === undefined) { + throw new Error('No data returned from queryFeesData'); + } + return this.processAdjustedApys(vaults, baseApys, rewardsApys, globalFeesData, feesData); + } + + processAdjustedApys( + vaults: Vault[], + baseApys: BaseApy[], + rewardsApys: RewardsApy[], + globalFeesData: globalFeesData, + feesData: feesData, + ): AdjustedApy[] { + const adjustedApys: AdjustedApy[] = []; + + const globalFee = + parseInt(globalFeesData.globals[0].treasuryFee) + parseInt(globalFeesData.globals[0].ecosystemFee); + for (const vault of vaults) { + let apySum = 0; + + const fees = feesData.smartVaults.find(v => v.id == vault.id); + if (fees == undefined) { + throw new Error('No fees found for vault ' + vault.id); + } + const feesSmartVaultFees = fees.smartVaultFees; + + const baseApy = baseApys.find(apy => apy.vault.id == vault.id); + if (baseApy == undefined) { + throw new Error('No baseApy found for vault ' + vault.id); + } + const baseApyData = Number(baseApy.baseApy); + + apySum = + baseApyData * + ((100 - + (parseFloat(feesSmartVaultFees.managementFeePercentage) + + parseFloat(feesSmartVaultFees.performanceFeePercentage) + + globalFee)) / + 100); + + const rewardApy = rewardsApys.find(apy => apy.vault.id == vault.id); + if (rewardApy != undefined) { + for (const apy of rewardApy.rewardsApys) { + apySum += Number(apy); + } + } + + adjustedApys.push({ vault: vault, adjustedApy: apySum.toString() }); + } + return adjustedApys; + } + + async getTvrSvts(vaults: Vault[]): Promise { + const smartVaultDeposits = await gqlFetch({ + endpoint: SUBGRAPH_API_BASE_URL, + query: DEPOSITS_QUERY, + }); + + return this.processTvrSvts(vaults, smartVaultDeposits); + } + + async processTvrSvts(vaults: Vault[], smartVaultDeposits: depositsQueryData | undefined): Promise { + const spoolLens = this.spoolV2ContractFactory.spoolLens({ address: SPOOL_LENS_ADDRESS, network: this.network }); + const tvrs: TvrType[] = []; + if (smartVaultDeposits == undefined) { + return tvrs; + } + const tvrCalculationsBigint: readonly bigint[] = await Promise.all( + vaults.map(vault => spoolLens.read.getSVTTotalSupply([vault.id])), + ); + const tvrCalculations: BigNumber[] = tvrCalculationsBigint.map(asset => ethers.BigNumber.from(asset)); + + for (const vault of smartVaultDeposits.smartVaults) { + for (const smartVaultDeposit of vault.smartVaultFlushes) { + if (smartVaultDeposit.isExecuted && !smartVaultDeposit.strategyDHWs[0].isExecuted) { + for (const flush of vault.smartVaultFlushes) { + if (smartVaultDeposit.id == flush.id) { + for (const smartVaultWithdrawalNft of flush.SmartVaultWithdrawalNFTs) { + tvrCalculations[smartVaultDeposits.smartVaults.indexOf(vault)] = tvrCalculations[ + smartVaultDeposits.smartVaults.indexOf(vault) + ].add(smartVaultWithdrawalNft.svtWithdrawn); + } + } + } + } + } + } + for (const vault of vaults) { + tvrs.push({ + vault: vault, + tvr: tvrCalculations[vaults.indexOf(vault)], + }); + } + return tvrs; + } + + getRiskScoreString(riskTolerance: number) { + const risk = this.mapRiskToRange(riskTolerance); + switch (true) { + case risk > 5: + return 'Risk Seeking'; + case risk === 5: + return 'Risk Neutral'; + default: + return 'Risk Averse'; + } + } + mapRiskToRange(risk: number) { + const [inMin, inMax] = [-10, 10]; + const [outMin, outMax] = [0, 10]; + return ((risk - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin; + } +} diff --git a/src/apps/spool-v2/ethereum/spool-v2.vo-spool.token-fetcher.ts b/src/apps/spool-v2/ethereum/spool-v2.vo-spool.token-fetcher.ts new file mode 100644 index 000000000..b8ba77089 --- /dev/null +++ b/src/apps/spool-v2/ethereum/spool-v2.vo-spool.token-fetcher.ts @@ -0,0 +1,44 @@ +import { Inject } from '@nestjs/common'; + +import { APP_TOOLKIT, IAppToolkit } from '~app-toolkit/app-toolkit.interface'; +import { PositionTemplate } from '~app-toolkit/decorators/position-template.decorator'; +import { AppTokenTemplatePositionFetcher } from '~position/template/app-token.template.position-fetcher'; + +import { SpoolV2ViemContractFactory } from '../contracts'; +import { SpoolVospool } from '../contracts/viem'; + +import { VOSPOOL_ADDRESS } from './spool-v2.constants'; +@PositionTemplate() +export class EthereumSpoolV2VoSpoolTokenFetcher extends AppTokenTemplatePositionFetcher { + groupLabel = 'voSPOOL'; + isExcludedFromBalances = true; + isExcludedFromExplore = true; + isExcludedFromTvl = true; + + constructor( + @Inject(APP_TOOLKIT) protected readonly appToolkit: IAppToolkit, + @Inject(SpoolV2ViemContractFactory) protected readonly contractFactory: SpoolV2ViemContractFactory, + ) { + super(appToolkit); + } + + getContract(address: string) { + return this.contractFactory.spoolVospool({ address, network: this.network }); + } + + async getAddresses(): Promise { + return [VOSPOOL_ADDRESS]; + } + + async getUnderlyingTokenDefinitions() { + return []; + } + + async getPricePerShare() { + return [1]; + } + + async getPrice() { + return 0; // Valueless token + } +} diff --git a/src/apps/spool-v2/spool-v2.module.ts b/src/apps/spool-v2/spool-v2.module.ts new file mode 100644 index 000000000..663b05a63 --- /dev/null +++ b/src/apps/spool-v2/spool-v2.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; + +import { AbstractApp } from '../../../../studio/src/app/app.dynamic-module'; + +import { SpoolV2ViemContractFactory } from './contracts'; +import { EthereumSpoolV2StakingContractPositionFetcher } from './ethereum/spool-v2.staking.contract-position-fetcher'; +import { EthereumSpoolV2VaultContractPositionFetcher } from './ethereum/spool-v2.vault.contract-position-fetcher'; +import { EthereumSpoolV2VoSpoolTokenFetcher } from './ethereum/spool-v2.vo-spool.token-fetcher'; + +@Module({ + providers: [ + EthereumSpoolV2StakingContractPositionFetcher, + EthereumSpoolV2VaultContractPositionFetcher, + EthereumSpoolV2VoSpoolTokenFetcher, + SpoolV2ViemContractFactory, + ], +}) +export class SpoolV2AppModule extends AbstractApp() {}