Smart Contract

PublishedNFTDAO

A.d5dab99b4e7301ce.PublishedNFTDAO

Deployed

2d ago
Feb 25, 2026, 09:43:57 PM UTC

Dependents

2 imports
1import FungibleToken from 0xf233dcee88fe0abe
2import FungibleTokenMetadataViews from 0xf233dcee88fe0abe
3import MetadataViews from 0x1d7e57aa55817448
4import Toucans from 0x577a3c409c5dcb5e
5import ToucansTokens from 0x577a3c409c5dcb5e
6 
7pub contract PublishedNFTDAO: FungibleToken {
8
9    // The amount of tokens in existance
10    pub var totalSupply: UFix64
11    // nil if there is none
12    pub let maxSupply: UFix64?
13
14    // Paths
15    pub let VaultStoragePath: StoragePath
16    pub let ReceiverPublicPath: PublicPath
17    pub let VaultPublicPath: PublicPath
18    pub let MinterStoragePath: StoragePath
19    pub let AdministratorStoragePath: StoragePath
20
21    // Events
22    pub event TokensInitialized(initialSupply: UFix64)
23    pub event TokensWithdrawn(amount: UFix64, from: Address?)
24    pub event TokensDeposited(amount: UFix64, to: Address?)
25    pub event TokensTransferred(amount: UFix64, from: Address, to: Address)
26    pub event TokensMinted(amount: UFix64)
27    pub event TokensBurned(amount: UFix64)
28
29    pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance, MetadataViews.Resolver {
30        pub var balance: UFix64
31
32        pub fun withdraw(amount: UFix64): @FungibleToken.Vault {
33            self.balance = self.balance - amount
34            emit TokensWithdrawn(amount: amount, from: self.owner?.address)
35
36            if let owner: Address = self.owner?.address {
37                PublishedNFTDAO.setBalance(address: owner, balance: self.balance)
38            }
39            return <- create Vault(balance: amount)
40        }
41
42        pub fun deposit(from: @FungibleToken.Vault) {
43            let vault: @Vault <- from as! @Vault
44            self.balance = self.balance + vault.balance
45            emit TokensDeposited(amount: vault.balance, to: self.owner?.address)
46            
47            // We set the balance to 0.0 here so that it doesn't
48            // decrease the totalSupply in the `destroy` function.
49            vault.balance = 0.0
50            destroy vault
51
52            if let owner: Address = self.owner?.address {
53                PublishedNFTDAO.setBalance(address: owner, balance: self.balance)
54            }
55        }
56
57        pub fun getViews(): [Type]{
58            return [Type<FungibleTokenMetadataViews.FTView>(),
59                    Type<FungibleTokenMetadataViews.FTDisplay>(),
60                    Type<FungibleTokenMetadataViews.FTVaultData>()]
61        }
62
63        pub fun resolveView(_ view: Type): AnyStruct? {
64            switch view {
65                case Type<FungibleTokenMetadataViews.FTView>():
66                    return FungibleTokenMetadataViews.FTView(
67                        ftDisplay: self.resolveView(Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,
68                        ftVaultData: self.resolveView(Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?
69                    )
70                case Type<FungibleTokenMetadataViews.FTDisplay>():
71                    let media = MetadataViews.Media(
72                            file: MetadataViews.HTTPFile(
73                            url: "https://nftstorage.link/ipfs/bafybeifsbguk3gajq6hwmoc772wldkadgxicbq6kodxp4vfp772helmwyq"
74                        ),
75                        mediaType: "image"
76                    )
77                    let bannerMedia = MetadataViews.Media(
78                            file: MetadataViews.HTTPFile(
79                            url: "https://nftstorage.link/ipfs/bafybeib4tiwtu437dttkerj62g6sdlnq2jprhvzpskiidh6ywadg3nikgm"
80                        ),
81                        mediaType: "image"
82                    )
83                    let medias = MetadataViews.Medias([media, bannerMedia])
84                    return FungibleTokenMetadataViews.FTDisplay(
85                        name: "Published NFT DAO",
86                        symbol: "PAGE",
87                        description: "Published NFT is a decentralized autonomous organization (DAO) that revolutionizes the world of eBook publishing by leveraging the power of Non-Fungible Tokens (NFTs). As an innovative platform, it allows users to engage in various activities such as donating, staking, and burning eBook NFTs to earn rewards in the form of page tokens.By acquiring eBook NFTs, users gain ownership of unique digital books, granting them exclusive rights and benefits within the ecosystem. These NFTs can be utilized in different ways, including staking them to earn page tokens. The more eBook NFTs staked, the greater the potential rewards.Page tokens hold significant value within the Published NFT DAO, as they serve as a voting mechanism. Holders of page tokens have the opportunity to influence important decisions regarding the platform's operations, such as the selection of new eBook titles, marketing strategies, and community initiatives. This democratic approach ensures that the DAO represents the collective interests of its members.Additionally, users have the option to burn their eBook NFTs, essentially removing them from circulation. In return for this action, they receive page tokens as a reward. Burning NFTs not only reduces the supply but also increases the scarcity and value of the remaining eBook NFTs held by the community.Published NFT empowers both eBook authors and readers, creating an inclusive ecosystem where creators are fairly compensated for their work and readers have a say in shaping the platform's direction. Through the use of NFTs, staking, burning, and page tokens, Published NFT establishes an innovative model that combines ownership, rewards, and community governance, ensuring a vibrant and engaging experience for all participants.",
88                        externalURL: MetadataViews.ExternalURL("publishednft.io/"),
89                        logos: medias,
90                        socials: {
91                            "twitter": MetadataViews.ExternalURL("publishednft"),
92                            "discord": MetadataViews.ExternalURL("RHH5aH44k9")
93                        }
94                    )
95                case Type<FungibleTokenMetadataViews.FTVaultData>():
96                    return FungibleTokenMetadataViews.FTVaultData(
97                        storagePath: PublishedNFTDAO.VaultStoragePath,
98                        receiverPath: PublishedNFTDAO.ReceiverPublicPath,
99                        metadataPath: PublishedNFTDAO.VaultPublicPath,
100                        providerPath: /private/PublishedNFTDAOVault,
101                        receiverLinkedType: Type<&Vault{FungibleToken.Receiver}>(),
102                        metadataLinkedType: Type<&Vault{FungibleToken.Balance, MetadataViews.Resolver}>(),
103                        providerLinkedType: Type<&Vault{FungibleToken.Provider}>(),
104                        createEmptyVaultFunction: (fun (): @Vault {
105                            return <- PublishedNFTDAO.createEmptyVault()
106                        })
107                    )
108            }
109            return nil
110        }
111  
112        init(balance: UFix64) {
113            self.balance = balance
114        }
115
116        destroy() {
117            emit TokensBurned(amount: self.balance)
118            PublishedNFTDAO.totalSupply = PublishedNFTDAO.totalSupply - self.balance
119        }
120    }
121
122    pub fun createEmptyVault(): @Vault {
123        return <- create Vault(balance: 0.0)
124    }
125
126    pub resource Minter: Toucans.Minter {
127        pub fun mint(amount: UFix64): @Vault {
128            post {
129                PublishedNFTDAO.maxSupply == nil || PublishedNFTDAO.totalSupply <= PublishedNFTDAO.maxSupply!: 
130                    "Exceeded the max supply of tokens allowd."
131            }
132            PublishedNFTDAO.totalSupply = PublishedNFTDAO.totalSupply + amount
133            emit TokensMinted(amount: amount)
134            return <- create Vault(balance: amount)
135        }
136    }
137
138    // We follow this pattern of storage
139    // so the (potentially) huge dictionary 
140    // isn't loaded when the contract is imported
141    pub resource Administrator {
142        // This is an experimental index and should
143        // not be used for anything official
144        // or monetary related
145        access(self) let balances: {Address: UFix64}
146
147        access(contract) fun setBalance(address: Address, balance: UFix64) {
148            self.balances[address] = balance
149        }
150
151        pub fun getBalance(address: Address): UFix64 {
152            return self.balances[address] ?? 0.0
153        }
154
155        pub fun getBalances(): {Address: UFix64} {
156            return self.balances
157        }
158
159        init() {
160            self.balances = {}
161        }
162    }
163
164    access(contract) fun setBalance(address: Address, balance: UFix64) {
165        let admin: &Administrator = self.account.borrow<&Administrator>(from: self.AdministratorStoragePath)!
166        admin.setBalance(address: address, balance: balance)
167    }
168
169    pub fun getBalance(address: Address): UFix64 {
170        let admin: &Administrator = self.account.borrow<&Administrator>(from: self.AdministratorStoragePath)!
171        return admin.getBalance(address: address)
172    }
173
174    pub fun getBalances(): {Address: UFix64} {
175        let admin: &Administrator = self.account.borrow<&Administrator>(from: self.AdministratorStoragePath)!
176        return admin.getBalances()
177    }
178
179    init(
180      _paymentTokenInfo: ToucansTokens.TokenInfo,
181      _editDelay: UFix64,
182      _minting: Bool,
183      _initialTreasurySupply: UFix64,
184      _maxSupply: UFix64?,
185      _extra: {String: AnyStruct}
186    ) {
187
188      // Contract Variables
189      self.totalSupply = 0.0
190      self.maxSupply = _maxSupply
191
192      // Paths
193      self.VaultStoragePath = /storage/PublishedNFTDAOVault
194      self.ReceiverPublicPath = /public/PublishedNFTDAOReceiver
195      self.VaultPublicPath = /public/PublishedNFTDAOMetadata
196      self.MinterStoragePath = /storage/PublishedNFTDAOMinter
197      self.AdministratorStoragePath = /storage/PublishedNFTDAOAdmin
198 
199      // Admin Setup
200      let vault <- create Vault(balance: self.totalSupply)
201      self.account.save(<- vault, to: self.VaultStoragePath)
202
203      self.account.link<&Vault{FungibleToken.Receiver}>(
204          self.ReceiverPublicPath,
205          target: self.VaultStoragePath
206      )
207
208      self.account.link<&Vault{FungibleToken.Balance, MetadataViews.Resolver}>(
209          self.VaultPublicPath,
210          target: self.VaultStoragePath
211      )
212
213      if self.account.borrow<&Toucans.Collection>(from: Toucans.CollectionStoragePath) == nil {
214        self.account.save(<- Toucans.createCollection(), to: Toucans.CollectionStoragePath)
215        self.account.link<&Toucans.Collection{Toucans.CollectionPublic}>(Toucans.CollectionPublicPath, target: Toucans.CollectionStoragePath)
216      }
217
218      let toucansProjectCollection = self.account.borrow<&Toucans.Collection>(from: Toucans.CollectionStoragePath)!
219      toucansProjectCollection.createProject(
220        projectTokenInfo: ToucansTokens.TokenInfo("PublishedNFTDAO", self.account.address, "PAGE", self.ReceiverPublicPath, self.VaultPublicPath, self.VaultStoragePath), 
221        paymentTokenInfo: _paymentTokenInfo, 
222        minter: <- create Minter(), 
223        editDelay: _editDelay,
224        minting: _minting,
225        initialTreasurySupply: _initialTreasurySupply,
226        extra: _extra
227      )
228
229      self.account.save(<- create Administrator(), to: self.AdministratorStoragePath)
230
231      // Events
232      emit TokensInitialized(initialSupply: self.totalSupply)
233    }
234}
235