Smart Contract

aiSportsSwapper

A.46df6b5eeec6103a.aiSportsSwapper

Valid From

130,593,594

Deployed

1w ago
Feb 21, 2026, 02:48:36 PM UTC

Dependents

2 imports
1/*
2================================================================================
3File: FastBreakVaultsCloser.cdc
4Project: aiSports - Flow Forte Hackathon Upgrade
5
6Development Plan & Testing Strategy (REVISED)
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 hold the $JUICE within this contract's account for eventual distribution
14// or further use in the aiSports ecosystem.
15
16// ==============================================================================
17// CURRENT STATUS & VALIDATION
18// ==============================================================================
19// Development has been split into two parallel streams which are now ready for
20// integration.
21
22// 1. MAINNET SWAP LOGIC - VALIDATED:
23//    - A prototype contract, `aiSportsSwapper`, has been successfully
24//      deployed and tested directly on Mainnet (account 0x46df6b5eeec6103a).
25//    - This version confirms the core swap logic using Increment.fi's Action
26//      is fully functional.
27//    - The contract correctly checks its own balance, retains 0.5 $FLOW for
28//      gas, and swaps the remaining balance into $JUICE.
29//    - The architecture has been simplified: swaps are in-place, and the
30//      $JUICE remains in the contract's account, removing transfer steps.
31
32// 2. SCHEDULED TRANSACTION LOGIC - VALIDATED ON EMULATOR:
33//    - The logic for scheduling, querying, and canceling the transaction
34//      has been fully developed and tested on the Flow Emulator.
35//    - Helper scripts to manage the scheduled transaction lifecycle (especially
36//      for cancellation during testing) are ready.
37
38// ==============================================================================
39// NEXT STEPS: INTEGRATION AND FINAL TESTING
40// ==============================================================================
41// The primary task is to combine the two validated components and test them
42// together on Mainnet in a controlled environment.
43
44// 1. IMPLEMENT BLOCK TIMESTAMP LOGIC:
45//    - Finalize the Cadence code in the scheduling transaction
46//      (`ScheduleFastBreakVaultsCloser.cdc`) & contract (`aiSportsSwapperTransactionHandler`)
47//    - Correctly use `getCurrentBlock().timestamp` to calculate and set the
48//      `startTime` and `interval` parameters for the job to ensure it runs
49//      at the desired daily cadence.
50
51// 2. Deploy Transaction Handler:
52//    - Use the update block timestamp info to deploy the Transaction Handler to the Mainnet test account
53//      (0x46df6b5eeec6103a)
54//    - Verify on-chain that the job executes at the correct time and that the
55//      $FLOW is successfully swapped to $JUICE.
56//    - Use the get_transaction_data.cdc & cancellation tx (CancelScheduledTransaction.cdc) to find and stop the test job.
57
58// 4. PRODUCTION DEPLOYMENT:
59//    - Once the end-to-end test is successful, deploy the final, production-ready
60//      contract to the primary aiSports account (0x254b32edc33e5bc3).
61//    - Schedule the transaction to run once per day to automate the conversion
62//      of FastBreak Vault payouts.
63
64// ==============================================================================
65// Account Notes:
66// - LIVE Mainnet Account (Final Destination): 0x254b32edc33e5bc3
67// - Test Mainnet Account (Current Testing): 0x46df6b5eeec6103a
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) contract aiSportsSwapper {
77
78      access(all) let SwapManagerStoragePath: StoragePath
79
80    //add an array of token types that we should scan and swap
81    access(all) let tokenStorageVaultPaths: [StoragePath]
82    access(all) let swappers: [IncrementFiSwapConnectors.Swapper]
83
84    access(all) fun swapToJuice() {
85
86        var vaultPathIndex = 0
87
88        // Deposit JUICE into the token holder's JUICE receiver
89        let juiceReceiver = self.account.capabilities.get<&{FungibleToken.Receiver}>(/public/aiSportsJuiceReceiver).borrow()
90            ?? panic("Missing /public/aiSportsJuiceReceiver capability on signer")
91
92        //loop through the array of token public vault paths and check the balance of each token
93        for tokenStorageVaultPath in self.tokenStorageVaultPaths {
94            let vaultRef = self.account.storage.borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(from:tokenStorageVaultPath) ?? panic("Vault not found")
95
96            //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
97            if vaultRef.getType() == Type<@FlowToken.Vault>() {
98                let balance = vaultRef.balance
99
100                if balance > 0.5 {
101                    let balanceToSwap = balance - 0.5
102                    let flowToWithdraw  <- vaultRef.withdraw(amount: balanceToSwap)
103                
104                    // Perform the swap; the swapper internally quotes amountOutMin
105                    let juiceVault <- self.swappers[0].swap(quote: nil, inVault: <-flowToWithdraw)
106
107                    juiceReceiver.deposit(from: <-juiceVault)
108                }
109            } else { //once we add more tokens, will need to add else logic here to swap
110                let balance = vaultRef.balance
111                if balance > 0.0 {
112                    let balanceToSwap = balance
113                    let tokenToWithdraw  <- vaultRef.withdraw(amount: balanceToSwap)
114                    let juiceVault <- self.swappers[vaultPathIndex].swap(quote: nil, inVault: <-tokenToWithdraw)
115                    juiceReceiver.deposit(from: <-juiceVault)
116
117                }
118            }
119            vaultPathIndex = vaultPathIndex + 1
120        }
121    }
122
123    access(all) resource SwapManager{
124        //this function is called by the contract admin to add a token type to the array of tokens to add/swap
125        access(all) fun addTokenType(vaultStoragePath: StoragePath, swapper: IncrementFiSwapConnectors.Swapper) {
126            aiSportsSwapper.tokenStorageVaultPaths.append(vaultStoragePath)
127            aiSportsSwapper.swappers.append(swapper)
128        }
129    }
130
131    init() {
132        let vaultData = FlowToken.resolveContractView(
133            resourceType: nil, 
134            viewType: Type<FungibleTokenMetadataViews.FTVaultData>()
135            ) as! FungibleTokenMetadataViews.FTVaultData
136        self.tokenStorageVaultPaths = [ vaultData.storagePath ]
137
138        // Initialize with IncrementFi FLOW -> JUICE (via stFLOW) swapper
139        // Provide concrete token vault types for validation against the path
140        self.swappers = [IncrementFiSwapConnectors.Swapper(
141            path: [
142            "A.1654653399040a61.FlowToken",
143            "A.d6f80565193ad727.stFlowToken",
144            "A.9db94c9564243ba7.aiSportsJuice"
145        ],
146            inVault: Type<@FlowToken.Vault>(),
147            outVault: Type<@aiSportsJuice.Vault>(),
148            uniqueID: nil
149        )]
150
151        //create the swap manager resource
152        self.SwapManagerStoragePath = /storage/aiSportsSwapperSwapManager
153        self.account.storage.save(<-create SwapManager(), to: self.SwapManagerStoragePath)
154    }
155}