Smart Contract

ERC4626PriceOracles

A.04f5ae6bef48c1fc.ERC4626PriceOracles

Valid From

139,096,899

Deployed

1w ago
Feb 16, 2026, 03:39:14 AM UTC

Dependents

0 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import EVM from 0xe467b9dd11fa00df
3import FlowEVMBridgeConfig from 0x1e4aa0b87d10b141
4import FlowEVMBridgeUtils from 0x1e4aa0b87d10b141
5import DeFiActions from 0x6d888f175c158410
6import ERC4626Utils from 0x04f5ae6bef48c1fc
7
8/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
9/// THIS CONTRACT IS IN BETA AND IS NOT FINALIZED - INTERFACES MAY CHANGE AND/OR PENDING CHANGES MAY REQUIRE REDEPLOYMENT
10/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
11///
12/// ERC4626PriceOracles
13///
14/// Implements the DeFiActions.PriceOracle interface to get share prices of ERC4626 vaults denominated in the underlying
15/// asset type.
16///
17access(all) contract ERC4626PriceOracles {
18
19    /// PriceOracle
20    ///
21    /// An implementation of the DeFiActions.PriceOracle interface to get share prices of ERC4626 vaults denominated in
22    /// the underlying asset type. The calculated price is normalized to 18 decimals and represents the current net 
23    /// asset value (NAV) per share.
24    ///
25    access(all) struct PriceOracle : DeFiActions.PriceOracle {
26        /// The address of the ERC4626 vault
27        access(all) let vault: EVM.EVMAddress
28        /// The asset type serving as the price basis in the ERC4626 vault
29        access(self) let asset: Type
30        /// The EVM address of the asset ERC20 asset underlying the ERC4626 vault
31        access(self) let assetEVMAddress: EVM.EVMAddress
32        /// The UniqueIdentifier of this component
33        access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
34
35        init(vault: EVM.EVMAddress, asset: Type, uniqueID: DeFiActions.UniqueIdentifier?) {
36            pre {
37                asset.isSubtype(of: Type<@{FungibleToken.Vault}>()):
38                "Provided asset \(asset.identifier) is not a Vault type"
39            }
40            let actualUnderlyingAddress = ERC4626Utils.underlyingAssetEVMAddress(vault: vault)
41            assert(
42                actualUnderlyingAddress?.equals(FlowEVMBridgeConfig.getEVMAddressAssociated(with: asset)!) ?? false,
43                message: "Provided asset \(asset.identifier) does not underly ERC4626 vault \(vault.toString()) - found \(actualUnderlyingAddress?.toString() ?? "nil") but expected \(FlowEVMBridgeConfig.getEVMAddressAssociated(with: asset)?.toString() ?? "nil")"
44            )
45
46            self.asset = asset
47            self.assetEVMAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: asset)
48                ?? panic("Provided asset \(asset.identifier) is not associated with ERC20 - ensure the type & ERC20 contracts are associated via the VM bridge")
49            self.vault = vault
50            self.uniqueID = uniqueID
51        }
52
53        /// Returns the asset type serving as the price basis in the ERC4626 vault
54        ///
55        /// @return The asset type serving as the price basis in the ERC4626 vault
56        ///
57        access(all) view fun unitOfAccount(): Type {
58            return self.asset
59        }
60        /// Returns the current price of the ERC4626 vault denominated in the underlying asset type
61        ///
62        /// @param ofToken The ERC4626 share token type to get the price of
63        ///
64        /// @return The current price of the ERC4626 vault denominated in the underlying asset type
65        access(all) fun price(ofToken: Type): UFix64? {
66            if let vaultType = FlowEVMBridgeConfig.getTypeAssociated(with: self.vault) {
67                if ofToken != vaultType {
68                    return nil
69                }
70            } else {
71                return nil
72            }
73            let totalAssets = ERC4626Utils.totalAssets(vault: self.vault)
74            let totalShares = ERC4626Utils.totalShares(vault: self.vault)
75            if totalAssets == nil || totalShares == nil || totalShares == UInt256(0) {
76                return nil
77            }
78
79            // normalize the total assets and total shares to 18 decimals
80            let totalAssetsNorm = ERC4626Utils.normalizeDecimals(amount: totalAssets!,
81                    originalDecimals: FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: self.assetEVMAddress),
82                    targetDecimals: 18
83                )
84            let totalSharesNorm = ERC4626Utils.normalizeDecimals(amount: totalShares!,
85                    originalDecimals: FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: self.vault),
86                    targetDecimals: 18
87                )
88            
89            // perform uint256 division to get the price
90            let factor = FlowEVMBridgeUtils.pow(base: 10, exponent: 18)
91            let price = (totalAssetsNorm * factor) / totalSharesNorm
92
93            return FlowEVMBridgeUtils.uint256ToUFix64(value: price, decimals: 18)
94        }
95        /// Returns a ComponentInfo struct containing information about this component and a list of ComponentInfo for
96        /// each inner component in the stack.
97        access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
98            return DeFiActions.ComponentInfo(
99                type: self.getType(),
100                id: self.id(),
101                innerComponents: []
102            )
103        }
104        /// Returns a copy of the struct's UniqueIdentifier, used in extending a stack to identify another connector in
105        /// a DeFiActions stack. See DeFiActions.align() for more information.
106        ///
107        /// @return a copy of the struct's UniqueIdentifier
108        ///
109        access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
110            return self.uniqueID
111        }
112        /// Sets the UniqueIdentifier of this component to the provided UniqueIdentifier, used in extending a stack to
113        /// identify another connector in a DeFiActions stack. See DeFiActions.align() for more information.
114        ///
115        /// @param id: the UniqueIdentifier to set for this component
116        ///
117        access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
118            self.uniqueID = id
119        }
120    }
121}
122