Smart Contract

StreamVestScheduler

A.5ec90e3dcf0067c4.StreamVestScheduler

Valid From

141,791,147

Deployed

2w ago
Feb 10, 2026, 07:33:32 PM UTC

Dependents

1 imports
1// StreamVestScheduler.cdc
2// Companion contract that integrates StreamVest with Flow's
3// scheduled-transaction infrastructure for autonomous streaming.
4//
5// Deployed alongside StreamVest — calls the public triggerStream()
6// function on schedule so tokens flow automatically.
7
8import FlowTransactionScheduler from 0xe467b9dd11fa00df
9import StreamVest from 0x5ec90e3dcf0067c4
10import FlowToken from 0x1654653399040a61
11import FungibleToken from 0xf233dcee88fe0abe
12
13access(all) contract StreamVestScheduler {
14
15    // ───────── Events ─────────
16    access(all) event HandlerCreated(nftID: UInt64, collectionAddress: Address, intervalSeconds: UFix64)
17    access(all) event StreamTriggered(nftID: UInt64)
18    access(all) event HandlerCompleted(nftID: UInt64)
19    access(all) event FeesLow(nftID: UInt64, remainingFees: UFix64)
20
21    // ═══════════════════════════════════════════════════════════════════
22    //  ScheduledStreamHandler
23    // ═══════════════════════════════════════════════════════════════════
24
25    access(all) resource ScheduledStreamHandler: FlowTransactionScheduler.TransactionHandler {
26        access(self) let collectionAddress: Address
27        access(self) let nftID: UInt64
28        access(self) let intervalSeconds: UFix64
29        access(all)  var isComplete: Bool
30        access(self) var feeVault: @FlowToken.Vault
31        access(self) var selfCap: Capability<auth(FlowTransactionScheduler.Execute) &{FlowTransactionScheduler.TransactionHandler}>?
32
33        init(
34            collectionAddress: Address,
35            nftID: UInt64,
36            intervalSeconds: UFix64,
37            feeVault: @FlowToken.Vault
38        ) {
39            self.collectionAddress = collectionAddress
40            self.nftID             = nftID
41            self.intervalSeconds   = intervalSeconds
42            self.isComplete        = false
43            self.feeVault          <- feeVault
44            self.selfCap           = nil
45        }
46
47        /// Must be called once after storing the handler and issuing its capability.
48        access(all) fun setSelfCapability(
49            cap: Capability<auth(FlowTransactionScheduler.Execute) &{FlowTransactionScheduler.TransactionHandler}>
50        ) {
51            pre { self.selfCap == nil: "Self capability already set" }
52            self.selfCap = cap
53        }
54
55        /// Called by the Flow blockchain at each scheduled interval.
56        access(FlowTransactionScheduler.Execute) fun executeTransaction(id: UInt64, data: AnyStruct?) {
57            if self.isComplete { return }
58
59            // Borrow the public collection from the owner's account.
60            let account = getAccount(self.collectionAddress)
61            let collection = account.capabilities
62                .get<&StreamVest.Collection>(StreamVest.CollectionPublicPath)
63                .borrow()
64                ?? panic("StreamVestScheduler: cannot borrow collection at ".concat(self.collectionAddress.toString()))
65
66            // Deliver vested tokens to the destination.
67            collection.triggerStream(nftID: self.nftID)
68            emit StreamTriggered(nftID: self.nftID)
69
70            // Check if the stream is finished.
71            if let nft = collection.borrowStreamVestNFT(id: self.nftID) {
72                if !nft.isActive {
73                    self.isComplete = true
74                    emit HandlerCompleted(nftID: self.nftID)
75                    return
76                }
77            } else {
78                // NFT no longer in collection.
79                self.isComplete = true
80                emit HandlerCompleted(nftID: self.nftID)
81                return
82            }
83
84            // ── Reschedule for the next interval ──
85            let nextTime = getCurrentBlock().timestamp + self.intervalSeconds
86            let priority = FlowTransactionScheduler.Priority(rawValue: 0)!
87
88            let est = FlowTransactionScheduler.estimate(
89                data: nil,
90                timestamp: nextTime,
91                priority: priority,
92                executionEffort: 1000
93            )
94
95            let fee = est.flowFee ?? 0.0
96            if fee == 0.0 || self.feeVault.balance < fee {
97                emit FeesLow(nftID: self.nftID, remainingFees: self.feeVault.balance)
98                return
99            }
100
101            let fees <- self.feeVault.withdraw(amount: fee) as! @FlowToken.Vault
102
103            let receipt <- FlowTransactionScheduler.schedule(
104                handlerCap: self.selfCap!,
105                data: nil,
106                timestamp: nextTime,
107                priority: priority,
108                executionEffort: 1000,
109                fees: <- fees
110            )
111            destroy receipt
112        }
113
114        /// How much FLOW remains for scheduling fees.
115        access(all) view fun getFeeBalance(): UFix64 {
116            return self.feeVault.balance
117        }
118
119        /// Top up the fee vault so the handler can keep rescheduling.
120        access(all) fun depositFees(from: @FlowToken.Vault) {
121            self.feeVault.deposit(from: <- from)
122        }
123    }
124
125    // ═══════════════════════════════════════════════════════════════════
126    //  Factory
127    // ═══════════════════════════════════════════════════════════════════
128
129    access(all) fun createHandler(
130        collectionAddress: Address,
131        nftID: UInt64,
132        intervalSeconds: UFix64,
133        feeVault: @FlowToken.Vault
134    ): @ScheduledStreamHandler {
135        emit HandlerCreated(
136            nftID: nftID,
137            collectionAddress: collectionAddress,
138            intervalSeconds: intervalSeconds
139        )
140        return <- create ScheduledStreamHandler(
141            collectionAddress: collectionAddress,
142            nftID: nftID,
143            intervalSeconds: intervalSeconds,
144            feeVault: <- feeVault
145        )
146    }
147
148    init() {}
149}
150