Smart Contract
aiSportsSwapper
A.46df6b5eeec6103a.aiSportsSwapper
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}