TransactionSEALED

#*%▒&●▪▒^#@&╳$◆&▪╲@■!▫╳!▓^#░╳□%●&^%~@╱╱&~◆▫%&╲╱◆○$╱$■^╳~*@╳■○○~#

Transaction ID

Timestamp

Feb 13, 2026, 03:20:56 AM UTC
2w ago

Block Height

142,041,699

Computation

0

Execution Fee

0.00498 FLOW

Transaction Summary

Contract Call

Called FungibleToken, NonFungibleToken, MetadataViews +6 more

Script Arguments

0listItemIDUInt64
500261
1amountUFix64
400.00000000
2interestRateUFix64
0.22750000
3termUFix64
6480000.00000000
4autoRepaymentEnabledBool
true
5loanExpiresAfterUFix64
2592000.00000000
6ftProviderControllerIDUInt64
0
7nftProviderControllerIDUInt64
167
8collectionIdentifierString
NBATopShot
9nftProviderAddressAddress
10ftReceiverAddressAddress
11paymentTokenIdentifierString
A.f1ab99c82dee3526.USDCFlow.Vault

Cadence Script

1import FungibleToken from 0xf233dcee88fe0abe
2import NonFungibleToken from 0x1d7e57aa55817448
3import MetadataViews from 0x1d7e57aa55817448
4import NFTCatalog from 0x49a7cda3a1eecc29
5import Flowty from 0x5c57f79c6694797f
6import HybridCustody from 0xd8a7e05a7ac670c0
7import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
8import CapabilityCache from 0xacc5081c003e24cf
9import AddressUtils from 0xa340dc0a4ec828ab
10
11transaction(
12    listItemID: UInt64,
13    amount: UFix64,
14    interestRate: UFix64,
15    term: UFix64,
16    autoRepaymentEnabled: Bool,
17    loanExpiresAfter: UFix64,
18    ftProviderControllerID: UInt64,
19    nftProviderControllerID: UInt64,
20    collectionIdentifier: String,
21    nftProviderAddress: Address,
22    ftReceiverAddress: Address,
23    paymentTokenIdentifier: String
24) {
25    let tokenReceiver: Capability<&{FungibleToken.Receiver}>
26    let nftProvider: Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>
27    let storefront: auth(Flowty.List) &Flowty.FlowtyStorefront
28    let nftReceiver: Capability<&{NonFungibleToken.CollectionPublic}>
29    let paymentVault: @{FungibleToken.Vault}
30    let tokenProvider: Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>?
31    let paymentTokenType: Type
32    let nftType: Type
33
34    prepare(acct: auth(Storage, Capabilities) &Account) {
35        self.paymentTokenType = CompositeType(paymentTokenIdentifier) ?? panic("invalid payment token type identifier")
36
37        if(acct.storage.borrow<&Flowty.FlowtyStorefront>(from: Flowty.FlowtyStorefrontStoragePath) == nil) {
38            // Create a new empty .Storefront
39            let storefront <- Flowty.createStorefront()
40
41            // save it to the account
42            acct.storage.save(<-storefront, to: Flowty.FlowtyStorefrontStoragePath)
43            acct.capabilities.publish(
44                acct.capabilities.storage.issue<&{Flowty.FlowtyStorefrontPublic}>(Flowty.FlowtyStorefrontStoragePath),
45                at: Flowty.FlowtyStorefrontPublicPath
46            )
47        }
48
49        let namespace = "flowty"
50        let cacheStoragePath = CapabilityCache.getPathForCache(namespace)
51        if acct.storage.borrow<&CapabilityCache.Cache>(from: cacheStoragePath) == nil {
52            let c <- CapabilityCache.createCache(namespace: namespace)
53            acct.storage.save(<-c, to: cacheStoragePath)
54        }
55        let cache = acct.storage.borrow<auth(CapabilityCache.Add, CapabilityCache.Get) &CapabilityCache.Cache>(from: cacheStoragePath)
56            ?? panic("capability cache not found")
57
58        let catalogEntry = NFTCatalog.getCatalogEntry(collectionIdentifier: collectionIdentifier) ?? panic("Provided collection is not in the NFT Catalog.")
59        self.nftType = catalogEntry.nftType
60
61        // We need a provider capability, but one is not provided by default so we create one if needed.
62        let publicCollectionPath = catalogEntry.collectionData.publicPath
63        let storageCollectionPath = catalogEntry.collectionData.storagePath
64
65        let ftAddress = AddressUtils.parseAddress(self.paymentTokenType)!
66        let contractName = self.paymentTokenType.identifier.split(separator: ".")[2]
67        let ftContract = getAccount(ftAddress).contracts.borrow<&{FungibleToken}>(name: contractName)
68            ?? panic("could not borrow fungible token contract")
69
70        let ftVaultData = ftContract.resolveContractView(resourceType: self.paymentTokenType, viewType: Type<FungibleTokenMetadataViews.FTVaultData>())! as! FungibleTokenMetadataViews.FTVaultData 
71
72        // Do we need to setup this vault for the signing account?
73        if ftReceiverAddress == acct.address && acct.storage.type(at: ftVaultData.storagePath) == nil {
74            acct.storage.save(<- ftVaultData.createEmptyVault(), to: ftVaultData.storagePath)
75            acct.capabilities.unpublish(ftVaultData.receiverPath)
76            acct.capabilities.unpublish(ftVaultData.metadataPath)
77
78            acct.capabilities.publish(acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(ftVaultData.storagePath), at: ftVaultData.receiverPath)
79            acct.capabilities.publish(acct.capabilities.storage.issue<&{FungibleToken.Vault}>(ftVaultData.storagePath), at: ftVaultData.metadataPath)
80        }
81
82        if autoRepaymentEnabled {
83            if ftReceiverAddress == acct.address {
84                let borrowType = Type<auth(FungibleToken.Withdraw) &{FungibleToken.Provider, FungibleToken.Balance, FungibleToken.Receiver}>()
85                if let provider = cache.getCapabilityByType(resourceType: self.paymentTokenType, capabilityType: CapabilityType(borrowType)!) {
86                    self.tokenProvider = provider as! Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider, FungibleToken.Balance, FungibleToken.Receiver}>
87                } else {
88                    self.tokenProvider = acct.capabilities.storage.issueWithType(ftVaultData.storagePath, type: borrowType) as! Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider, FungibleToken.Balance, FungibleToken.Receiver}>
89                    cache.addCapability(resourceType: self.paymentTokenType, cap: self.tokenProvider!)
90                }
91            } else {
92                let manager = acct.storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
93                ?? panic("Missing or mis-typed HybridCustody Manager")
94
95                let child = manager.borrowAccount(addr: ftReceiverAddress) ?? panic("no child account with that address")
96                let ftProviderCap = child.getCapability(controllerID: ftProviderControllerID, type: Type<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>()) ?? panic("no ft provider found")
97                self.tokenProvider = ftProviderCap as! Capability<auth(FungibleToken.Withdraw) &{FungibleToken.Provider}>?
98            }
99
100            assert(self.tokenProvider!.check(), message: "Missing or mis-typed token provider")
101        } else {
102            self.tokenProvider = nil
103        }
104
105        if ftReceiverAddress == acct.address {
106            if acct.storage.borrow<&{FungibleToken.Vault}>(from: ftVaultData.storagePath) == nil {
107                acct.storage.save(<- ftVaultData.createEmptyVault(), to: ftVaultData.storagePath)
108                acct.capabilities.publish(
109                    acct.capabilities.storage.issue<&{FungibleToken.Receiver}>(ftVaultData.storagePath),
110                    at: ftVaultData.receiverPath
111                )
112
113                acct.capabilities.publish(
114                    acct.capabilities.storage.issue<&{FungibleToken.Balance}>(ftVaultData.storagePath),
115                    at: ftVaultData.metadataPath
116                )
117            }
118
119            self.tokenReceiver = acct.capabilities.get<&{FungibleToken.Receiver}>(ftVaultData.receiverPath)!
120        } else {
121            let manager = acct.storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
122                ?? panic("Missing or mis-typed HybridCustody Manager")
123
124            let child = manager.borrowAccount(addr: ftReceiverAddress) ?? panic("no child account with that address")
125            self.tokenReceiver = getAccount(ftReceiverAddress).capabilities.get<&{FungibleToken.Receiver}>(ftVaultData.receiverPath)!
126        }
127
128        assert(self.tokenReceiver.check(), message: "Missing or mis-typed token receiver")
129
130        if nftProviderAddress == acct.address {
131            if !acct.capabilities.get<&{NonFungibleToken.CollectionPublic}>(publicCollectionPath).check() {
132                acct.capabilities.unpublish(publicCollectionPath)
133
134                acct.capabilities.publish(
135                    acct.capabilities.storage.issue<&{NonFungibleToken.CollectionPublic}>(storageCollectionPath),
136                    at: publicCollectionPath
137                )
138            }
139
140            let borrowType = Type<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>()
141            if let provider = cache.getCapabilityByType(resourceType: self.nftType, capabilityType: CapabilityType(borrowType)!) {
142                self.nftProvider = provider as! Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>
143            } else {
144                self.nftProvider = acct.capabilities.storage.issueWithType(catalogEntry.collectionData.storagePath, type: borrowType) as! Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>
145                cache.addCapability(resourceType: self.nftType, cap: self.nftProvider!)
146            }
147
148            self.nftReceiver = acct.capabilities.get<&{NonFungibleToken.CollectionPublic}>(publicCollectionPath)!
149        } else {
150            let manager = acct.storage.borrow<auth(HybridCustody.Manage) &HybridCustody.Manager>(from: HybridCustody.ManagerStoragePath)
151                ?? panic("Missing or mis-typed HybridCustody Manager")
152
153            let child = manager.borrowAccount(addr: nftProviderAddress) ?? panic("no child account with that address")
154
155            let providerCap = child.getCapability(controllerID: nftProviderControllerID, type: Type<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>()) ?? panic("no nft provider found")
156            self.nftProvider = providerCap as! Capability<auth(NonFungibleToken.Withdraw) &{NonFungibleToken.Provider, NonFungibleToken.CollectionPublic}>
157
158            let receiverCap = child.getPublicCapability(path: publicCollectionPath, type: Type<&{NonFungibleToken.CollectionPublic}>()) ?? panic("no nft collection public found")
159            self.nftReceiver = receiverCap as! Capability<&{NonFungibleToken.CollectionPublic}>
160        }
161
162        assert(self.nftProvider.check(), message: "Missing or mis-typed NFT Provider")
163        assert(self.nftReceiver.check(), message: "Missing or mis-typed NFT Receiver")
164
165        self.storefront = acct.storage.borrow<auth(Flowty.List) &Flowty.FlowtyStorefront>(from: Flowty.FlowtyStorefrontStoragePath)
166            ?? panic("Missing or mis-typed Flowty FlowtyStorefront")
167
168        let tokenVault = acct.storage.borrow<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>(from: ftVaultData.storagePath)
169            ?? panic("Cannot borrow token vault from acct storage")
170
171        self.paymentVault <- tokenVault.withdraw(amount: Flowty.ListingFee)
172    }
173
174    execute {
175        let paymentCut = Flowty.PaymentCut(
176            receiver: self.tokenReceiver,
177            amount: amount
178        )
179        self.storefront.createListing(
180            payment: <-self.paymentVault,
181            nftProviderCapability: self.nftProvider,
182            nftPublicCollectionCapability: self.nftReceiver,
183            fusdProviderCapability: self.tokenProvider,
184            nftType: self.nftType,
185            nftID: listItemID,
186            amount: amount,
187            interestRate: interestRate,
188            term: term,
189            paymentVaultType: self.paymentTokenType,
190            paymentCuts: [paymentCut],
191            expiresAfter: loanExpiresAfter
192        )
193    }
194}