Smart Contract
ERC4626PriceOracles
A.04f5ae6bef48c1fc.ERC4626PriceOracles
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