Smart Contract
FastbreakVaultsCloser_V1
A.46df6b5eeec6103a.FastbreakVaultsCloser_V1
1/*
2================================================================================
3File: FastBreakVaultsCloser.cdc
4Project: aiSports - Flow Forte Hackathon Upgrade
5
6Development Plan & Testing Strategy
7================================================================================
8
9// OVERALL GOAL:
10// This contract is designed to be executed by a Flow Scheduled Transaction.
11// Its ultimate purpose is to take the $FLOW prize pool from a completed
12// FastBreak Vault, swap it for $JUICE using the Increment.fi DEX Action,
13// and prepare the $JUICE for distribution to the winners.
14
15// CORE CHALLENGE:
16// The Increment.fi swap Action is only available on Flow Mainnet. It cannot be
17// tested on the emulator or testnet. This requires a phased development
18// approach to validate our logic before deploying to mainnet with real funds.
19
20// ==============================================================================
21// PHASE 1: EMULATOR-FIRST MOCK LOGIC (CURRENT IMPLEMENTATION)
22// ==============================================================================
23// Objective:
24// To verify that the core Scheduled Transaction mechanism (scheduling, triggering,
25// and execution) works correctly in a controlled environment without the
26// mainnet dependency.
27
28// Implementation:
29// Instead of performing a real swap, we will implement a simple, verifiable
30// proxy action on the emulator:
31// 1. The scheduled job triggers and executes this contract's logic.
32// 2. The code checks this contract's own $FLOW balance.
33// 3. A conditional check is performed: IF the balance is above a threshold
34// (e.g., > 0.5 FLOW for testing), THEN proceed.
35// 4. If the condition is met, the contract sends a small, fixed amount
36// (e.g., 0.1 FLOW) to a designated recipient address. This address can
37// only be set by the contract admin. We may not need to send the tokens, the Juice tokens can just stay in the contract account.
38
39// Success Criteria for Phase 1:
40// We can successfully schedule the job, and the designated recipient account
41// receives 0.1 FLOW at the specified interval on the emulator. This proves
42// the scheduling and execution pipeline is working.
43
44// ==============================================================================
45// PHASE 2: MAINNET INTEGRATION & LIVE TESTING
46// ==============================================================================
47// Objective:
48// To replace the mock logic with the real swap functionality and deploy to
49// mainnet for a final, controlled test.
50
51// Implementation Steps:
52// 1. Comment out or remove the mock transfer logic from Phase 1.
53// 2. Integrate the Cadence code from our `swap_flow_to_juice.cdc` transaction
54// directly into this contract's execution logic.
55// 3. This new code will call the official Increment.fi swap Action to convert
56// this contract's entire $FLOW balance into $JUICE.
57// 4. Deploy the finalized contract to a mainnet account.
58// 5. Fund the account with a small, controlled amount of $FLOW for testing
59// (e.g., 1.0 FLOW).
60// 6. Schedule the transaction to run.
61
62// Success Criteria for Phase 2:
63// After the scheduled transaction executes on mainnet, we can verify on-chain
64// that the contract's $FLOW balance has been successfully swapped for $JUICE.
65
66Account notes:
67mainnet account holding Flow - 0x254b32edc33e5bc3
68
69*/
70import FungibleToken from 0xf233dcee88fe0abe
71import FlowToken from 0x1654653399040a61
72import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
73import aiSportsJuice from 0x9db94c9564243ba7
74import IncrementFiSwapConnectors from 0xefa9bd7d1b17f1ed
75
76access(all)
77contract FastbreakVaultsCloser_V1 {
78
79 //add an array of token types that we should scan and swap
80 access(all) let tokenStorageVaultPaths: [StoragePath]
81
82 access(all) fun swapToJuice() {
83
84 //loop through the array of token public vault paths and check the balance of each token
85 for tokenStorageVaultPath in self.tokenStorageVaultPaths {
86 let vaultRef = self.account.storage.borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(from:tokenStorageVaultPath) ?? panic("Vault not found")
87
88 //check the type of the vault - if it is a Flow Token Vault, we need to keep some Flow tokens in the contract account for fees
89 if vaultRef.getType() == Type<@FlowToken.Vault>() {
90 let balance = vaultRef.balance
91
92 if balance > 0.5 {
93 let balanceToSwap = balance - 0.5
94 log("Balance to Swap: \(balanceToSwap)")
95 let flowToWithdraw <- vaultRef.withdraw(amount: balanceToSwap)
96
97 //swap the balance to juice
98
99 // Router path for IncrementFi (FLOW -> stFLOW -> JUICE)
100 let path: [String] = [
101 "A.1654653399040a61.FlowToken",
102 "A.d6f80565193ad727.stFlowToken",
103 "A.9db94c9564243ba7.aiSportsJuice"
104 ]
105
106 // Build the IncrementFi swapper (FLOW -> JUICE via stFLOW)
107 // Provide concrete token vault types for validation against the path
108 let swapper = IncrementFiSwapConnectors.Swapper(
109 path: path,
110 inVault: Type<@FlowToken.Vault>(),
111 outVault: Type<@aiSportsJuice.Vault>(),
112 uniqueID: nil
113 )
114
115 // Perform the swap; the swapper internally quotes amountOutMin
116 let juiceVault <- swapper.swap(quote: nil, inVault: <-flowToWithdraw)
117
118 // Deposit JUICE into the token holder's JUICE receiver
119 let juiceReceiver = self.account
120 .capabilities
121 .get<&{FungibleToken.Receiver}>(/public/aiSportsJuiceReceiver)
122 .borrow()
123 ?? panic("Missing /public/aiSportsJuiceReceiver capability on signer")
124
125 juiceReceiver.deposit(from: <-juiceVault)
126 }
127 } //once we add more tokens, will need to add else logic here to swap
128 }
129 }
130
131 //this function is called by the contract admin to add a token type to the array of tokens to add/swap
132 access(account) fun addTokenType(vaultStoragePath: StoragePath) {
133 self.tokenStorageVaultPaths.append(vaultStoragePath)
134 }
135
136 init() {
137 let vaultData = FlowToken.resolveContractView(
138 resourceType: nil,
139 viewType: Type<FungibleTokenMetadataViews.FTVaultData>()
140 ) as! FungibleTokenMetadataViews.FTVaultData
141 self.tokenStorageVaultPaths = [ vaultData.storagePath ]
142 }
143}