Smart Contract
ToucansUtils
A.577a3c409c5dcb5e.ToucansUtils
1import Crypto
2import FungibleToken from 0xf233dcee88fe0abe
3import NFTCatalog from 0x49a7cda3a1eecc29
4import NonFungibleToken from 0x1d7e57aa55817448
5import FIND from 0x097bafa4e0b48eef
6import EmeraldIdentity from 0x39e42c67cc851cfb
7import SwapInterfaces from 0xb78ef7afa52ff906
8import LiquidStaking from 0xd6f80565193ad727
9import FlowToken from 0x1654653399040a61
10import stFlowToken from 0xd6f80565193ad727
11
12access(all) contract ToucansUtils {
13 access(all) fun ownsNFTFromCatalogCollectionIdentifier(collectionIdentifier: String, user: Address): Bool {
14 if let entry: NFTCatalog.NFTCatalogMetadata = NFTCatalog.getCatalogEntry(collectionIdentifier: collectionIdentifier) {
15 let publicPath: PublicPath = entry.collectionData.publicPath
16 let contractAddressToString: String = entry.contractAddress.toString()
17 let constructedIdentifier: String = "A.".concat(contractAddressToString.slice(from: 2, upTo: contractAddressToString.length)).concat(".").concat(entry.contractName).concat(".Collection")
18
19 var addresses: [Address] = [user]
20 if let discordID: String = EmeraldIdentity.getDiscordFromAccount(account: user) {
21 addresses = EmeraldIdentity.getEmeraldIDs(discordID: discordID).values
22 }
23 assert(addresses.contains(user), message: "Should always be true. Just making sure so the user doesn't get punished accidentally ;)")
24 for address in addresses {
25 if let collection: &{NonFungibleToken.CollectionPublic} = getAccount(address).capabilities.borrow<&{NonFungibleToken.CollectionPublic}>(publicPath) {
26 let identifier: String = collection.getType().identifier
27 if identifier == constructedIdentifier && collection.getIDs().length > 0 {
28 return true
29 }
30 }
31 }
32 }
33
34 return false
35 }
36
37 access(all) fun depositTokensToAccount(funds: @{FungibleToken.Vault}, to: Address, publicPath: PublicPath) {
38 let vault = getAccount(to).capabilities.borrow<&{FungibleToken.Receiver}>(publicPath)
39 ?? panic("Account does not have a proper Vault set up.")
40 vault.deposit(from: <- funds)
41 }
42
43 access(all) fun rangeFunc(_ start: Int, _ end: Int, _ f : (fun (Int): Void) ) {
44 var current = start
45 while current < end{
46 f(current)
47 current = current + 1
48 }
49 }
50
51 access(all) fun range(_ start: Int, _ end: Int): [Int]{
52 var res:[Int] = []
53 self.rangeFunc(start, end, fun (i:Int){
54 res.append(i)
55 })
56 return res
57 }
58
59 access(all) fun index(_ s : String, _ substr : String, _ startIndex: Int): Int?{
60 for i in self.range(startIndex,s.length-substr.length+1){
61 if s[i]==substr[0] && s.slice(from:i, upTo:i+substr.length) == substr{
62 return i
63 }
64 }
65 return nil
66 }
67
68 access(all) fun getFind(_ address: Address): String {
69 if let name = FIND.reverseLookup(address) {
70 return name.concat(".find")
71 }
72 return address.toString()
73 }
74
75 access(all) fun fixToReadableString(num: UFix64): String {
76 let numToString: String = num.toString()
77 let indexOfDot: Int = ToucansUtils.index(numToString, ".", 1)!
78 return numToString.slice(from: 0, upTo: indexOfDot + 3)
79 }
80
81 // stringAddress DOES NOT include the `0x`
82 access(all) fun stringToAddress(stringAddress: String): Address {
83 var r: UInt64 = 0
84 var bytes: [UInt8] = stringAddress.decodeHex()
85
86 while bytes.length > 0 {
87 r = r + (UInt64(bytes.removeFirst()) << UInt64(bytes.length * 8))
88 }
89
90 return Address(r)
91 }
92
93 // returns:
94 // [address, contractname]
95 access(all) fun getAddressAndContractNameFromCollectionIdentifier(identifier: String): [AnyStruct] {
96 let address: Address = self.stringToAddress(stringAddress: identifier.slice(from: 2, upTo: 18))
97 let contractName: String = identifier.slice(from: 19, upTo: identifier.length - 11)
98 return [address, contractName]
99 }
100
101 access(all) fun getEstimatedOut(amountIn: UFix64, tokenInKey: String): UFix64 {
102 // normal xyk pool
103 let poolCapV1 = getAccount(0x396c0cda3302d8c5).capabilities.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)!
104 // stableswap pool with most liquidity
105 let poolCapStable = getAccount(0xc353b9d685ec427d).capabilities.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)!
106
107 let estimatedSwapOutV1 = poolCapV1.getAmountOut(amountIn: amountIn, tokenInKey: tokenInKey)
108 let estimatedSwapOutStable = poolCapStable.getAmountOut(amountIn: amountIn, tokenInKey: tokenInKey)
109 let estimatedSwapOut = (estimatedSwapOutStable > estimatedSwapOutV1) ? estimatedSwapOutStable : estimatedSwapOutV1
110
111 if tokenInKey == "A.1654653399040a61.FlowToken" {
112 let estimatedStakeOut = LiquidStaking.calcStFlowFromFlow(flowAmount: amountIn)
113 return (estimatedSwapOut > estimatedStakeOut) ? estimatedSwapOut : estimatedStakeOut
114 }
115
116 return estimatedSwapOut
117 }
118
119 access(all) fun swapTokensWithPotentialStake(inVault: @{FungibleToken.Vault}, tokenInKey: String): @{FungibleToken.Vault} {
120 let amountIn = inVault.balance
121 // normal xyk pool
122 let poolCapV1 = getAccount(0x396c0cda3302d8c5).capabilities.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)!
123 let estimatedSwapOutV1 = poolCapV1.getAmountOut(amountIn: amountIn, tokenInKey: tokenInKey)
124 // stableswap pool with most liquidity
125 let poolCapStable = getAccount(0xc353b9d685ec427d).capabilities.borrow<&{SwapInterfaces.PairPublic}>(/public/increment_swap_pair)!
126 let estimatedSwapOutStable = poolCapStable.getAmountOut(amountIn: amountIn, tokenInKey: tokenInKey)
127
128 let estimatedSwapPoolCap = (estimatedSwapOutStable > estimatedSwapOutV1) ? poolCapStable : poolCapV1
129
130 let estimatedSwapOut = (estimatedSwapOutStable > estimatedSwapOutV1) ? estimatedSwapOutStable : estimatedSwapOutV1
131 let estimatedStakeOut = LiquidStaking.calcStFlowFromFlow(flowAmount: amountIn)
132
133 if tokenInKey == "A.1654653399040a61.FlowToken" && estimatedStakeOut > estimatedSwapOut {
134 return <- LiquidStaking.stake(flowVault: <- (inVault as! @FlowToken.Vault))
135 }
136
137 return <- estimatedSwapPoolCap.swap(vaultIn: <- inVault, exactAmountOut: nil)
138 }
139
140 access(all) fun getNFTCatalogCollectionIdentifierFromCollectionIdentifier(collectionIdentifier: String): String {
141 let nftTypeIdentifier: String = collectionIdentifier.slice(from: 0, upTo: collectionIdentifier.length - 10).concat("NFT")
142 let collectionsForType: {String: Bool} = NFTCatalog.getCollectionsForType(nftTypeIdentifier: nftTypeIdentifier) ?? panic("This collection is not supported in the NFTCatalog.")
143 let collectionIdentifier: String = collectionsForType.keys[0]
144 return collectionIdentifier
145 }
146}