Smart Contract
FRC20StakingForwarder
A.d2abb5dbf5e08666.FRC20StakingForwarder
1/**
2> Author: Fixes Lab <https://github.com/fixes-world/>
3
4# FRC20StakingForwarder
5
6The FRC20StakingForwarder contract is a forwarder contract that forwards the Fungible Token to the FRC20Staking Pool.
7
8*/
9import FungibleToken from 0xf233dcee88fe0abe
10import FlowToken from 0x1654653399040a61
11import Fixes from 0xd2abb5dbf5e08666
12import FRC20FTShared from 0xd2abb5dbf5e08666
13import FRC20Staking from 0xd2abb5dbf5e08666
14
15access(all) contract FRC20StakingForwarder {
16 /* --- Events --- */
17
18 // Event that is emitted when tokens are deposited to the target receiver
19 access(all) event ForwardedDeposit(amount: UFix64, from: Address?)
20
21 /* --- Variable, Enums and Structs --- */
22 access(all)
23 let StakingForwarderStoragePath: StoragePath
24 access(all)
25 let StakingForwarderPublicPath: PublicPath
26
27 /* --- Interfaces & Resources --- */
28
29 access(all) resource interface ForwarderPublic {
30 /// Helper function to check whether set `recipient` capability
31 /// is not latent or the capability tied to a type is valid.
32 access(all)
33 view fun check(): Bool
34
35 /// Gets the fallback receiver assigned to the account
36 access(all)
37 view fun fallbackBorrow(): &{FungibleToken.Receiver}?
38 }
39
40 access(all) resource Forwarder: FungibleToken.Receiver, ForwarderPublic {
41 /// The capability of staking pool
42 access(self) let pool: Capability<&FRC20Staking.Pool>
43
44 init(_ poolAddr: Address) {
45 post {
46 self.pool.check(): "Pool Capability is not valid"
47 }
48 self.pool = FRC20Staking.getPoolCap(poolAddr)
49 }
50
51 /// Helper function to check whether set `pool` capability
52 /// is not latent or the capability tied to a type is valid.
53 access(all)
54 view fun check(): Bool {
55 return self.pool.check()
56 }
57
58 /// Gets the fallback receiver assigned to the account
59 ///
60 view access(all) fun fallbackBorrow(): &{FungibleToken.Receiver}? {
61 let ownerAddress = self.owner?.address ?? panic("No owner set")
62 let cap = getAccount(ownerAddress)
63 .capabilities.get<&{FungibleToken.Receiver}>(Fixes.getFallbackFlowTokenPublicPath())
64 return cap.check() ? cap.borrow() : nil
65 }
66
67 /// A getter function that returns the token types supported by this resource,
68 /// which can be deposited using the 'deposit' function.
69 ///
70 /// @return Array of FT types that can be deposited.
71 access(all)
72 view fun getSupportedVaultTypes(): {Type: Bool} {
73 pre {
74 self.check(): "Forwarder capability is not valid"
75 }
76 let supportedVaults: {Type: Bool} = {}
77
78 let poolRef = self.pool.borrow()
79 ?? panic("Could not borrow pool reference")
80 let rewardTicks = poolRef.getRewardNames()
81 for rewardTick in rewardTicks {
82 if let rewardVault = poolRef.getRewardDetails(rewardTick) {
83 if rewardVault.rewardVaultType != nil {
84 supportedVaults[rewardVault.rewardVaultType!] = true
85 }
86 }
87 }
88 return supportedVaults
89 }
90
91 /// Returns whether or not the given type is accepted by the Receiver
92 /// A vault that can accept any type should just return true by default
93 access(all)
94 view fun isSupportedVaultType(type: Type): Bool {
95 pre {
96 self.check(): "Forwarder capability is not valid"
97 }
98 let supportedVaults = self.getSupportedVaultTypes()
99 return supportedVaults[type] ?? false
100 }
101
102 // deposit
103 //
104 // Function that takes a Vault object as an argument and forwards
105 // it to the recipient's Vault using the stored reference
106 //
107 access(all)
108 fun deposit(from: @{FungibleToken.Vault}) {
109 let poolRef = self.pool.borrow()
110 ?? panic("Could not borrow pool reference")
111
112 let forwarderAddr = self.owner?.address ?? panic("No owner set in Staking Forwarder")
113 let balance = from.balance
114 let rewardType = from.getType()
115
116 let poolDetails = poolRef.getDetails()
117 let totalStakedAmount = poolDetails.totalStaked
118
119 var rewardStrategyRef: &FRC20Staking.RewardStrategy? = poolRef.borrowRewardStrategy(rewardType.identifier)
120 if rewardStrategyRef == nil && from.isInstance(Type<@FlowToken.Vault>()) {
121 rewardStrategyRef = poolRef.borrowRewardStrategy("")
122 }
123
124 let fallbackReceiver = self.fallbackBorrow()
125 ?? panic("No fallback receiver set in Staking Forwarder")
126
127 let yieldValue = totalStakedAmount > 0.0 ? balance / totalStakedAmount : 0.0
128 // If the yield value is 0 or the reward strategy is not found
129 if yieldValue == 0.0 || rewardStrategyRef == nil {
130 fallbackReceiver.deposit(from: <- from)
131 } else {
132 // Forward the tokens to staking pool
133 let change <- FRC20FTShared.wrapFungibleVaultChange(
134 ftVault: <- from,
135 from: forwarderAddr,
136 )
137 rewardStrategyRef!.addIncome(income: <- change)
138 emit ForwardedDeposit(amount: balance, from: forwarderAddr)
139 }
140 }
141 }
142
143 // createNewForwarder creates a new Forwarder reference with the provided recipient
144 //
145 access(all)
146 fun createNewForwarder(_ poolAddr: Address): @Forwarder {
147 return <-create Forwarder(poolAddr)
148 }
149
150 init() {
151 let identifier = "FRC20StakingForwarder_".concat(self.account.address.toString())
152 self.StakingForwarderStoragePath = StoragePath(identifier: identifier)!
153 self.StakingForwarderPublicPath = PublicPath(identifier: identifier)!
154 }
155}
156