Smart Contract

MockSwapper

A.b1d63873c3cc9f79.MockSwapper

Valid From

143,217,030

Deployed

1w ago
Feb 19, 2026, 10:35:24 AM UTC

Dependents

1 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import Burner from 0xf233dcee88fe0abe
3
4import MockOracle from 0xb1d63873c3cc9f79
5
6import DeFiActions from 0x6d888f175c158410
7import SwapConnectors from 0xe1a479f0cb911df9
8import FlowALPMath from 0x6b00ff876c299c61
9
10///
11/// THIS CONTRACT IS A MOCK AND IS NOT INTENDED FOR USE IN PRODUCTION
12/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
13///
14access(all) contract MockSwapper {
15
16    /// Mocked liquidity sources
17    access(self) let liquidityConnectors: {Type: {DeFiActions.Sink, DeFiActions.Source}}
18
19    /// Mock setter enabling the configuration of liquidity sources used by mock swappers
20    access(all) fun setLiquidityConnector(_ connector: {DeFiActions.Sink, DeFiActions.Source}) {
21        pre {
22            connector.getSinkType() == connector.getSourceType():
23            "Connector sink Type \(connector.getSinkType().identifier) != connector source Type \(connector.getSourceType().identifier)"
24        }
25        self.liquidityConnectors[connector.getSinkType()] = connector
26    }
27
28    // Swapper
29    //
30    /// Mocked DeFiActions Swapper implementation. Be sure to set connectors for Vaults you wish to handle via this mock
31    /// in MockSwapper.liquidityConnectors via .setLiquidityConnector before instantiating mocks
32    access(all) struct Swapper : DeFiActions.Swapper {
33        access(contract) var uniqueID: DeFiActions.UniqueIdentifier?
34        access(self) let inVault: Type
35        access(self) let outVault: Type
36        access(self) let oracle: {DeFiActions.PriceOracle}
37
38        init(inVault: Type, outVault: Type, uniqueID: DeFiActions.UniqueIdentifier?) {
39            pre {
40                MockSwapper.liquidityConnectors[inVault] != nil:
41                "Invalid inVault - \(inVault.identifier) does not have a MockSwapper connector to handle funds"
42                MockSwapper.liquidityConnectors[outVault] != nil:
43                "Invalid outVault - \(outVault.identifier) does not have a MockSwapper connector to handle funds"
44            }
45            self.inVault = inVault
46            self.outVault = outVault
47            self.oracle = MockOracle.PriceOracle()
48            self.uniqueID = uniqueID
49        }
50
51        /// The type of Vault this Swapper accepts when performing a swap
52        access(all) view fun inType(): Type {
53            return self.inVault
54        }
55
56        /// The type of Vault this Swapper provides when performing a swap
57        access(all) view fun outType(): Type {
58            return self.outVault
59        }
60
61        /// The estimated amount required to provide a Vault with the desired output balance, sourcing pricing from the
62        /// mocked oracle
63        access(all) fun quoteIn(forDesired: UFix64, reverse: Bool): {DeFiActions.Quote} {
64            return self._estimate(amount: forDesired, out: false, reverse: reverse)
65        }
66
67        /// The estimated amount delivered out for a provided input balance, sourcing pricing from the mocked oracle
68        access(all) fun quoteOut(forProvided: UFix64, reverse: Bool): {DeFiActions.Quote} {
69            return self._estimate(amount: forProvided, out: true, reverse: reverse)
70        }
71
72        /// Performs a swap taking a Vault of type inVault, outputting a resulting outVault. Implementations may choose
73        /// to swap along a pre-set path or an optimal path of a set of paths or even set of contained Swappers adapted
74        /// to use multiple Flow swap protocols.
75        /// NOTE: This mock sources pricing data from the mocked oracle, allowing for pricing to be manually manipulated
76        /// for testing and demonstration purposes
77        access(all) fun swap(quote: {DeFiActions.Quote}?, inVault: @{FungibleToken.Vault}): @{FungibleToken.Vault} {
78            return <- self._swap(<-inVault, reverse: false)
79        }
80
81        /// Performs a swap taking a Vault of type outVault, outputting a resulting inVault. Implementations may choose
82        /// to swap along a pre-set path or an optimal path of a set of paths or even set of contained Swappers adapted
83        /// to use multiple Flow swap protocols.
84        /// NOTE: This mock sources pricing data from the mocked oracle, allowing for pricing to be manually manipulated
85        /// for testing and demonstration purposes
86        access(all) fun swapBack(quote: {DeFiActions.Quote}?, residual: @{FungibleToken.Vault}): @{FungibleToken.Vault} {
87            return <- self._swap(<-residual, reverse: true)
88        }
89
90        /// Internal estimator returning a quote for the amount in/out and in the desired direction
91        access(self) fun _estimate(amount: UFix64, out: Bool, reverse: Bool): {DeFiActions.Quote} {
92            let outTokenPrice = self.oracle.price(ofToken: self.outType())
93            ?? panic("Price for token \(self.outType().identifier) is currently unavailable")
94            let inTokenPrice = self.oracle.price(ofToken: self.inType())
95            ?? panic("Price for token \(self.inType().identifier) is currently unavailable")
96
97            let uintOutTokenPrice = UFix128(outTokenPrice)
98            let uintInTokenPrice = UFix128(inTokenPrice)
99
100            // the original formula is correct, but lacks precision
101            // let price = reverse  ? outTokenPrice / inTokenPrice : inTokenPrice / outTokenPrice
102            let uintPrice = reverse ? (uintOutTokenPrice / uintInTokenPrice) : (uintInTokenPrice / uintOutTokenPrice)
103
104            if amount == UFix64.max {
105                return SwapConnectors.BasicQuote(
106                    inType: reverse ? self.outType() : self.inType(),
107                    outType: reverse ? self.inType() : self.outType(),
108                    inAmount: UFix64.max,
109                    outAmount: UFix64.max
110                )
111            }
112
113            let uintAmount = UFix128(amount)
114            let uintInAmount = out ? uintAmount : (uintAmount / uintPrice)
115            let uintOutAmount = out ? uintAmount * uintPrice : uintAmount
116
117            let inAmount = FlowALPMath.toUFix64Round(uintInAmount)
118            let outAmount = FlowALPMath.toUFix64Round(uintOutAmount)
119
120            return SwapConnectors.BasicQuote(
121                inType: reverse ? self.outVault : self.inVault,
122                outType: reverse ? self.inVault : self.outVault,
123                inAmount: inAmount,
124                outAmount: outAmount
125            )
126        }
127
128        access(self) fun _swap(_ from: @{FungibleToken.Vault}, reverse: Bool): @{FungibleToken.Vault} {
129            let inAmount = from.balance
130            var swapInVault = reverse ? MockSwapper.liquidityConnectors[from.getType()]! : MockSwapper.liquidityConnectors[self.inType()]!
131            var swapOutVault = reverse ? MockSwapper.liquidityConnectors[self.inType()]! : MockSwapper.liquidityConnectors[self.outType()]!
132            swapInVault.depositCapacity(from: &from as auth(FungibleToken.Withdraw) &{FungibleToken.Vault})            
133            Burner.burn(<-from)
134            let outAmount = self.quoteOut(forProvided: inAmount, reverse: reverse).outAmount
135            var outVault <- swapOutVault.withdrawAvailable(maxAmount: outAmount)
136
137            assert(outVault.balance == outAmount,
138            message: "MockSwapper outVault returned invalid balance - expected \(outAmount), received \(outVault.balance)")
139
140            return <- outVault
141        }
142        access(all) fun getComponentInfo(): DeFiActions.ComponentInfo {
143            return DeFiActions.ComponentInfo(
144                type: self.getType(),
145                id: self.id(),
146                innerComponents: []
147            )
148        }
149        access(contract) view fun copyID(): DeFiActions.UniqueIdentifier? {
150            return self.uniqueID
151        }
152        access(contract) fun setID(_ id: DeFiActions.UniqueIdentifier?) {
153            self.uniqueID = id
154        }
155    }
156
157    init() {
158        self.liquidityConnectors = {}
159    }    
160}
161
162