simple_collectible.sol 4.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // SPDX-License-Identifier: Apache-2.0
  2. // DISCLAIMER: This file is an example of how to mint and transfer NFTs on Solana. It is not production ready and has not been audited for security.
  3. // Use it at your own risk.
  4. import '../../solana-library/spl_token.sol';
  5. contract SimpleCollectible {
  6. // On Solana, the mintAccount represents the type of token created. It saves how many tokens exist in circulation.
  7. address private mintAccount;
  8. // The public key for the authority that should sign every change to the NFT's URI
  9. address private metadataAuthority;
  10. // A resource identifier to access the NFT. It could be any other data to be saved on the blockchain
  11. string private uri;
  12. // These events log on the blockchain transactions made with this NFT
  13. event NFTMinted(address owner, address mintAccount);
  14. event NFTSold(address from, address to);
  15. // The mint account will identify the NFT in this example
  16. constructor (address _mintAccount, address _metadataAuthority) {
  17. mintAccount = _mintAccount;
  18. metadataAuthority = _metadataAuthority;
  19. }
  20. /// Create a new NFT
  21. @mutableAccount(mintAccount) // The account of the mint. Its address must be the same as that of the 'mintAccount' contract variable.
  22. @mutableAccount(ownerTokenAccount) // The owner's associated token account
  23. @signer(mintAuthority) // The account that signs each new mint
  24. function createCollectible() external {
  25. SplToken.TokenAccountData token_data = SplToken.get_token_account_data(tx.accounts.ownerTokenAccount);
  26. SplToken.MintAccountData mint_data = SplToken.get_mint_account_data(tx.accounts.mintAccount);
  27. // Ensure the supply is zero. Otherwise, this is not an NFT.
  28. assert(mint_data.supply == 0);
  29. // An NFT on Solana is a SPL-Token with only one minted token.
  30. // The token account saves the owner of the tokens minted with the mint account, the respective mint account and the number
  31. // of tokens the owner account owns
  32. SplToken.mint_to(tx.accounts.mintAccount.key, tx.accounts.ownerTokenAccount.key, tx.accounts.mintAuthority.key, 1);
  33. // Set the mint authority to null. This prevents that any other new tokens be minted, ensuring we have an NFT.
  34. SplToken.remove_mint_authority(tx.accounts.mintAccount.key, tx.accounts.mintAuthority.key);
  35. // Log on blockchain records information about the created token
  36. emit NFTMinted(token_data.owner, token_data.mintAccount);
  37. }
  38. /// Transfer ownership of this NFT from one account to another
  39. /// This function only wraps the innate SPL transfer, which can be used outside this contract.
  40. /// However, the difference here is the event 'NFTSold' exclusive to this function
  41. @mutableAccount(oldTokenAccount) // The token account for the current owner
  42. @mutableAccount(newTokenAccount) // The token account for the new owner
  43. @signer(oldOwner)
  44. function transferOwnership() external {
  45. // The current owner does not need to be the caller of this functions, but they need to sign the transaction
  46. // with their private key.
  47. SplToken.TokenAccountData old_data = SplToken.get_token_account_data(tx.accounts.oldTokenAccount);
  48. SplToken.TokenAccountData new_data = SplToken.get_token_account_data(tx.accounts.newTokenAccount);
  49. // To transfer the ownership of a token, we need the current owner and the new owner. The payer account is the account used to derive
  50. // the correspondent token account in TypeScript.
  51. SplToken.transfer(
  52. tx.accounts.oldTokenAccount.key,
  53. tx.accounts.newTokenAccount.key,
  54. tx.accounts.oldOwner.key, 1);
  55. emit NFTSold(old_data.owner, new_data.owner);
  56. }
  57. /// Return the URI of this NFT
  58. function getNftUri() public view returns (string memory) {
  59. return uri;
  60. }
  61. /// Check if an NFT is owned by @param owner
  62. ///
  63. /// @param owner the account whose ownership we want to verify
  64. @account(tokenAccount) // The owner's associated token account
  65. function isOwner(address owner) external view returns (bool) {
  66. SplToken.TokenAccountData data = SplToken.get_token_account_data(tx.accounts.tokenAccount);
  67. return owner == data.owner && mintAccount == data.mintAccount && data.balance == 1;
  68. }
  69. /// Updates the NFT URI
  70. /// The metadata authority must sign the transaction so that the update can succeed.
  71. ///
  72. /// @param newUri a new URI for the NFT
  73. @signer(metadataSigner) // The metadata authority that can authorize changes in the NFT data.
  74. function updateNftUri(string newUri) external {
  75. require(tx.accounts.metadataSigner.is_signer, "the metadata authority must sign the transaction");
  76. assert(tx.accounts.metadataSigner.key == metadataAuthority);
  77. uri = newUri;
  78. }
  79. }