diff --git a/Cargo.lock b/Cargo.lock index 2cbc73b5..45b20351 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,7 @@ dependencies = [ [[package]] name = "nina" -version = "0.2.16" +version = "0.3.0" dependencies = [ "anchor-lang", "anchor-spl", diff --git a/js/package.json b/js/package.json index 16220acc..9e285b92 100644 --- a/js/package.json +++ b/js/package.json @@ -26,7 +26,7 @@ "@emotion/server": "^11.4.0", "@emotion/styled": "^11.6.0", "@imgix/js-core": "^3.6.0", - "@magic-ext/solana": "^12.1.0", + "@magic-ext/solana": "^15.3.1", "@material-ui/core": "^4.11.4", "@material-ui/icons": "^4.11.2", "@material-ui/system": "^4.11.3", @@ -42,7 +42,7 @@ "env-cmd": "^10.1.0", "ga-gtag": "^1.1.1", "glob-parent": "^5.1.2", - "magic-sdk": "^16.1.0", + "magic-sdk": "^19.3.1", "nanoid": "^3.1.31", "next": "12.2.0", "node-fetch": "^2.6.7", diff --git a/js/sdk/src/contexts/Hub/hub.js b/js/sdk/src/contexts/Hub/hub.js index 2afb22ab..b8c8ac7c 100644 --- a/js/sdk/src/contexts/Hub/hub.js +++ b/js/sdk/src/contexts/Hub/hub.js @@ -232,6 +232,7 @@ const hubContextHelper = ({ const tx = await program.methods .hubInit(hubParams) .accounts({ + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, hubSigner, @@ -287,6 +288,7 @@ const hubContextHelper = ({ new anchor.BN(referralFee * 10000), { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub: new anchor.web3.PublicKey(hubPubkey), }, @@ -356,6 +358,7 @@ const hubContextHelper = ({ hub.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityHubCollaborator, hub: hubPubkey, @@ -433,6 +436,7 @@ const hubContextHelper = ({ hub.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityHubCollaborator, hub: hubPubkey, @@ -508,6 +512,7 @@ const hubContextHelper = ({ const request = { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub: hubPubkey, hubRelease, @@ -588,6 +593,7 @@ const hubContextHelper = ({ const tx = await program.transaction.hubRemoveCollaborator(hub.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub: hubPubkey, hubCollaborator, @@ -743,6 +749,7 @@ const hubContextHelper = ({ hub.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub: hubPubkey, hubSigner, @@ -827,6 +834,7 @@ const hubContextHelper = ({ const params = [handle, slugHash, uri] const request = { accounts: { + payer: provider.wallet.publicKey, author: provider.wallet.publicKey, hub: hubPubkey, post, @@ -949,6 +957,7 @@ const hubContextHelper = ({ uri, { accounts: { + payer: provider.wallet.publicKey, author: provider.wallet.publicKey, hub: hubPubkey, post, @@ -1010,6 +1019,7 @@ const hubContextHelper = ({ const request = { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, royaltyTokenAccount: release.royaltyTokenAccount, release: releasePubkey, diff --git a/js/sdk/src/contexts/Release/release.js b/js/sdk/src/contexts/Release/release.js index 91beff5a..7213cef3 100644 --- a/js/sdk/src/contexts/Release/release.js +++ b/js/sdk/src/contexts/Release/release.js @@ -835,6 +835,7 @@ const releaseContextHelper = ({ try { const tx = await program.transaction.releaseCloseEdition({ accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, release: new anchor.web3.PublicKey(releasePubkey), releaseSigner: release.releaseSigner, @@ -967,6 +968,7 @@ const releaseContextHelper = ({ const request = { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount, release: new anchor.web3.PublicKey(releasePubkey), @@ -1045,6 +1047,7 @@ const releaseContextHelper = ({ const request = { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount, release: releasePublicKey, diff --git a/js/sdk/src/contexts/Wallet/wallet.js b/js/sdk/src/contexts/Wallet/wallet.js index f6b9c72c..58a44f3d 100644 --- a/js/sdk/src/contexts/Wallet/wallet.js +++ b/js/sdk/src/contexts/Wallet/wallet.js @@ -6,6 +6,7 @@ import { import { Magic } from 'magic-sdk' import { SolanaExtension } from '@magic-ext/solana' import * as anchor from '@coral-xyz/anchor' +import axios from 'axios' const WalletContext = createContext() const WalletContextProvider = ({ children }) => { @@ -106,7 +107,17 @@ const walletContextHelper = ({ const connectMagicWallet = async (magic) => { const isLoggedIn = await magic.user.isLoggedIn() if (isLoggedIn) { - const user = await magic.user.getMetadata() + const user = await magic.user.getInfo() + if (user) { + try { + await axios.post(`${process.env.NINA_IDENTITY_ENDPOINT}/login`, { + issuer: user.issuer, + publicKey: user.publicAddress, + }) + } catch (error) { + console.error(error) + } + } const wallet = { connected: true, connecting: false, diff --git a/js/sdk/src/utils/releasePurchaseHelperTransactionBuilder.js b/js/sdk/src/utils/releasePurchaseHelperTransactionBuilder.js index 69963f81..589b1dbe 100644 --- a/js/sdk/src/utils/releasePurchaseHelperTransactionBuilder.js +++ b/js/sdk/src/utils/releasePurchaseHelperTransactionBuilder.js @@ -1,4 +1,4 @@ -import * as anchor from '@project-serum/anchor' +import * as anchor from '@coral-xyz/anchor' import { findOrCreateAssociatedTokenAccount } from './web3' import { decodeNonEncryptedByteArray } from './encrypt' diff --git a/js/yarn.lock b/js/yarn.lock index 44c6d274..5dec62f3 100644 --- a/js/yarn.lock +++ b/js/yarn.lock @@ -2013,29 +2013,29 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.10.1.tgz#5bd16082261d7364eabb511c788f00937dac588d" integrity sha512-z+ILK8Q3y+nfUl43ctCPuR4Y2bIxk/ooCQFwZxhtci1EhAtMDzMAx2W25qx8G1PPL9UUOdnUax19+F0OjXoj4w== -"@magic-ext/solana@^12.1.0": - version "12.2.0" - resolved "https://registry.yarnpkg.com/@magic-ext/solana/-/solana-12.2.0.tgz#4a3faa3d66eed6b36b1779ca76b61c422d89b12d" - integrity sha512-cKennYgQZAzvSUoQ0aRc8Mm524bDpZGBki8F7TyLwJL0FhJeU1I071XwErDORqjCRPARpJ5PUhE6RIMA3/4vhA== - -"@magic-sdk/commons@^12.2.0": - version "12.2.0" - resolved "https://registry.yarnpkg.com/@magic-sdk/commons/-/commons-12.2.0.tgz#08a9c153f2e5068c13020b4e41e953b9a76c7cd3" - integrity sha512-wbbPY3D2C7EIYNkIhYu7ApGS4+7gE8YdO0AgKOwY+ShA/6vN3egeXygYMOcl4udC5LHrB8V4VcRLS3Mv96cD8Q== - -"@magic-sdk/provider@^16.2.0": - version "16.2.0" - resolved "https://registry.yarnpkg.com/@magic-sdk/provider/-/provider-16.2.0.tgz#d67a46a93f4d625f1055c93452e13c623e43827a" - integrity sha512-mZsHVqkV5NT2F+Rx2vS+lSVd1SwYlBp9YG3A69sv2a34uza4nCiL+4R1Xxd398u0aEpuzDqn+PyEk0pTVnUL+w== - dependencies: - "@magic-sdk/types" "^14.2.0" +"@magic-ext/solana@^15.3.1": + version "15.4.0" + resolved "https://registry.yarnpkg.com/@magic-ext/solana/-/solana-15.4.0.tgz#0c431f4cb0f374e77ec143c3dad2db01f00d3692" + integrity sha512-nMpExsGc9QuTk/jv4rL5XP41BSvTncSe7xjOg14N/b8vvtjmgoMPXZE8SM9t4Ms9YNaDMQApyaZLyMFWTtXMlQ== + +"@magic-sdk/commons@^15.4.0": + version "15.4.0" + resolved "https://registry.yarnpkg.com/@magic-sdk/commons/-/commons-15.4.0.tgz#335cc5c8602bca0fd6fdaee29bde0cb120fc6533" + integrity sha512-FawPr08tuAs0r0bOcfbmQuI3b0VGRBVLbh1+5DWJ0acdlIBv/4botqUGtfj22P6VIdOP+OawnlagcUac6oBi5Q== + +"@magic-sdk/provider@^19.4.0": + version "19.4.0" + resolved "https://registry.yarnpkg.com/@magic-sdk/provider/-/provider-19.4.0.tgz#600811170d75fa84e506a7f2243af850d2ca4048" + integrity sha512-A6vw0jnNbM5jrozel/da3W8zBEtqxTcyYc5/RKB7H5x03dPtvd4V3nQzvJCYq7d7ZudW8/5R/qg4DVkJILAzeg== + dependencies: + "@magic-sdk/types" "^16.4.0" eventemitter3 "^4.0.4" web3-core "1.5.2" -"@magic-sdk/types@^14.2.0": - version "14.2.0" - resolved "https://registry.yarnpkg.com/@magic-sdk/types/-/types-14.2.0.tgz#5eb2d611574e1e94875d72f8cf51be3529895d22" - integrity sha512-6zts9JLdNLj03xCf4GNjpihkSxAoC6m+UzGpARHUBecSkwp6ixrAioMhIdy+vjXF4+f7Ntb2tWcrMExIfA7hFg== +"@magic-sdk/types@^16.4.0": + version "16.4.0" + resolved "https://registry.yarnpkg.com/@magic-sdk/types/-/types-16.4.0.tgz#23e097d8bf2277cbed1b88065620b742493f3376" + integrity sha512-KHBfp0EYHxYBUJL/n//N3LF1oZ1qe1My2fDKTe6QiOSjx67xycDr8EmSqa+gbuIroS1uMTYzKlS1K5+RDiRhBQ== "@mapbox/hast-util-table-cell-style@^0.2.0": version "0.2.0" @@ -9277,14 +9277,14 @@ luxon@^2.3.0: resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.5.2.tgz#17ed497f0277e72d58a4756d6a9abee4681457b6" integrity sha512-Yg7/RDp4nedqmLgyH0LwgGRvMEKVzKbUdkBYyCosbHgJ+kaOUx0qzSiSatVc3DFygnirTPYnMM2P5dg2uH1WvA== -magic-sdk@^16.1.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/magic-sdk/-/magic-sdk-16.2.0.tgz#d4f30564f76539882ef0374434c3396f5a5f7860" - integrity sha512-ySnnMRGZ3Yj6QAiml4SYIdrG3GNI7CP90s+hjbUGSJ3oJlv3RiDzmwO8jgl4K7TcooZiR5pvhyNbuUhEwXnfyw== +magic-sdk@^19.3.1: + version "19.4.0" + resolved "https://registry.yarnpkg.com/magic-sdk/-/magic-sdk-19.4.0.tgz#9b126137cc370004bc7bce9589134fbdeee1eab3" + integrity sha512-i9K8OMH1HpNvwtrCOlMhRiwViGN7sWJPRxs9VNfSJAyDEn6xwVjbY2VgyHt9F/wUJUQAY1f94H+N37sT7dU6pw== dependencies: - "@magic-sdk/commons" "^12.2.0" - "@magic-sdk/provider" "^16.2.0" - "@magic-sdk/types" "^14.2.0" + "@magic-sdk/commons" "^15.4.0" + "@magic-sdk/provider" "^19.4.0" + "@magic-sdk/types" "^16.4.0" localforage "^1.7.4" magic-string@^0.25.0, magic-string@^0.25.2, magic-string@^0.25.7: diff --git a/programs/nina/Cargo.toml b/programs/nina/Cargo.toml index 35c252e2..5f6c26ce 100644 --- a/programs/nina/Cargo.toml +++ b/programs/nina/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nina" -version = "0.2.16" +version = "0.3.0" description = "Nina - A self-publishing protocol" edition = "2018" diff --git a/programs/nina/src/errors.rs b/programs/nina/src/errors.rs index 77058105..385242d6 100644 --- a/programs/nina/src/errors.rs +++ b/programs/nina/src/errors.rs @@ -77,5 +77,31 @@ pub enum ErrorCode { #[msg("Subscription Delegated Payer Mismatch")] SubscriptionDelegatedPayerMismatch, #[msg("Release Init Delegated Payer Mismatch")] - ReleaseInitDelegatedPayerMismatch + ReleaseInitDelegatedPayerMismatch, + #[msg("Hub Add Collaborator Delegated Payer Mismatch")] + HubAddCollaboratorDelegatedPayerMismatch, + #[msg("Hub Remove Collaborator Delegated Payer Mismatch")] + HubRemoveCollaboratorDelegatedPayerMismatch, + #[msg("Hub Add Release Delegated Payer Mismatch")] + HubAddReleaseDelegatedPayerMismatch, + #[msg("HubContentToggleVisibility Delegated Payer Mismatch")] + HubContentToggleVisibilityDelegatedPayerMismatch, + #[msg("Hub Withdraw Delegated Payer Mismatch")] + HubWithdrawDelegatedPayerMismatch, + #[msg("Release Revenue Share Collect Delegate Payer Mismatch")] + ReleaseRevenueShareCollectDelegatePayerMismatch, + #[msg("Hub Update Collaborator Permissions Delegated Payer Mismatch")] + HubUpdateCollaboratorPermissionsDelegatePayerMismatch, + #[msg("Hub Update Config Delegated Payer Mismatch")] + HubUpdateConfigDelegatePayerMismatch, + #[msg("Release Close Edition Delegated Payer Mismatch")] + ReleaseCloseEditionDelegatePayerMismatch, + #[msg("Release Revenue Share Transfer Delegate Payer Mismatch")] + ReleaseRevenueShareTransferDelegatePayerMismatch, + #[msg("Release Update Metadata Delegated Payer Mismatch")] + ReleaseUpdateMetadataDelegatePayerMismatch, + #[msg("Post Update Via Hub Delegated Payer Mismatch")] + PostUpdateViaHubPostDelegatePayerMismatch, + #[msg("Post Init Via Hub Delegated Payer Mismatch")] + PostInitViaHubDelegatePayerMismatch } \ No newline at end of file diff --git a/programs/nina/src/instructions/hub_add_collaborator.rs b/programs/nina/src/instructions/hub_add_collaborator.rs index 383994b1..842b4af5 100644 --- a/programs/nina/src/instructions/hub_add_collaborator.rs +++ b/programs/nina/src/instructions/hub_add_collaborator.rs @@ -1,6 +1,7 @@ use anchor_lang::prelude::*; use crate::state::*; +use crate::utils::{file_service_account}; use crate::errors::ErrorCode; #[derive(Accounts)] @@ -11,6 +12,8 @@ use crate::errors::ErrorCode; hub_handle: String, )] pub struct HubAddCollaborator<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -44,6 +47,12 @@ pub fn handler ( allowance: i8, _hub_handle: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::HubAddCollaboratorDelegatedPayerMismatch.into()); + } + } + let authority_hub_collaborator = &ctx.accounts.authority_hub_collaborator; if !authority_hub_collaborator.can_add_collaborator { diff --git a/programs/nina/src/instructions/hub_add_release.rs b/programs/nina/src/instructions/hub_add_release.rs index ab7d7300..8c781cc6 100644 --- a/programs/nina/src/instructions/hub_add_release.rs +++ b/programs/nina/src/instructions/hub_add_release.rs @@ -1,9 +1,13 @@ use anchor_lang::prelude::*; use crate::state::*; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; #[derive(Accounts)] #[instruction(hub_handle: String)] pub struct HubAddRelease<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -44,6 +48,12 @@ pub fn handler ( ctx: Context, _hub_handle: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::HubAddReleaseDelegatedPayerMismatch.into()); + } + } + Hub::hub_collaborator_can_add_or_publish_content( &mut ctx.accounts.hub_collaborator, false diff --git a/programs/nina/src/instructions/hub_content_toggle_visibility.rs b/programs/nina/src/instructions/hub_content_toggle_visibility.rs index 667df3f2..55ad3255 100644 --- a/programs/nina/src/instructions/hub_content_toggle_visibility.rs +++ b/programs/nina/src/instructions/hub_content_toggle_visibility.rs @@ -2,10 +2,13 @@ use anchor_lang::prelude::*; use crate::state::*; use crate::errors::ErrorCode; +use crate::utils::{file_service_account}; #[derive(Accounts)] #[instruction(hub_handle: String)] pub struct HubContentToggleVisibility<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -28,6 +31,12 @@ pub fn handler ( ctx: Context, _hub_handle: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::HubContentToggleVisibilityDelegatedPayerMismatch.into()); + } + } + let hub = ctx.accounts.hub.load()?; let hub_content = &mut ctx.accounts.hub_content; diff --git a/programs/nina/src/instructions/hub_remove_collaborator.rs b/programs/nina/src/instructions/hub_remove_collaborator.rs index 7d79d4f2..ae26fd38 100644 --- a/programs/nina/src/instructions/hub_remove_collaborator.rs +++ b/programs/nina/src/instructions/hub_remove_collaborator.rs @@ -2,10 +2,13 @@ use anchor_lang::prelude::*; use crate::state::*; use crate::errors::ErrorCode; +use crate::utils::{file_service_account}; #[derive(Accounts)] #[instruction(hub_handle: String)] pub struct HubRemoveCollaborator<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -32,6 +35,12 @@ pub fn handler ( ctx: Context, _hub_handle: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::HubRemoveCollaboratorDelegatedPayerMismatch.into()); + } + } + let hub = ctx.accounts.hub.load()?; // Only authority of hub and collaborator in the HubCollaborator account can remove the account diff --git a/programs/nina/src/instructions/hub_update_collaborator_permissions.rs b/programs/nina/src/instructions/hub_update_collaborator_permissions.rs index aaa93ea6..dab4a8a2 100644 --- a/programs/nina/src/instructions/hub_update_collaborator_permissions.rs +++ b/programs/nina/src/instructions/hub_update_collaborator_permissions.rs @@ -2,6 +2,7 @@ use anchor_lang::prelude::*; use crate::state::*; use crate::errors::ErrorCode; +use crate::utils::{file_service_account}; #[derive(Accounts)] #[instruction( @@ -11,6 +12,8 @@ use crate::errors::ErrorCode; hub_handle: String, )] pub struct HubUpdateCollaboratorPermissions<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -40,6 +43,12 @@ pub fn handler ( allowance: i8, _hub_handle: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::HubUpdateCollaboratorPermissionsDelegatePayerMismatch.into()); + } + } + if ctx.accounts.authority.key() != ctx.accounts.hub.load()?.authority { return Err(error!(ErrorCode::HubCollaboratorCannotUpdateHubCollaboratorUnauthorized)) } diff --git a/programs/nina/src/instructions/hub_update_config.rs b/programs/nina/src/instructions/hub_update_config.rs index d2a19f7a..c35b7fb5 100644 --- a/programs/nina/src/instructions/hub_update_config.rs +++ b/programs/nina/src/instructions/hub_update_config.rs @@ -1,5 +1,7 @@ use anchor_lang::prelude::*; use crate::state::*; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; #[derive(Accounts)] #[instruction( @@ -9,6 +11,8 @@ use crate::state::*; _referral_fee: u64, )] pub struct HubUpdateConfig<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -27,6 +31,12 @@ pub fn handler ( publish_fee: u64, referral_fee: u64, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::HubUpdateConfigDelegatePayerMismatch.into()); + } + } + Hub::check_hub_fees( publish_fee, referral_fee diff --git a/programs/nina/src/instructions/hub_withdraw.rs b/programs/nina/src/instructions/hub_withdraw.rs index e26d60a3..5a125733 100644 --- a/programs/nina/src/instructions/hub_withdraw.rs +++ b/programs/nina/src/instructions/hub_withdraw.rs @@ -1,6 +1,7 @@ use anchor_lang::prelude::*; use anchor_spl::token::{self, TokenAccount, Token, Transfer, Mint}; +use crate::utils::{file_service_account}; use crate::state::*; use crate::errors::ErrorCode; @@ -10,6 +11,8 @@ use crate::errors::ErrorCode; hub_handle: String )] pub struct HubWithdraw<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -47,6 +50,12 @@ pub fn handler( amount: u64, _hub_handle: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::HubWithdrawDelegatedPayerMismatch.into()); + } + } + if ctx.accounts.withdraw_target.amount < amount { return Err(error!(ErrorCode::HubWithdrawAmountTooHigh)); } diff --git a/programs/nina/src/instructions/post_init_via_hub.rs b/programs/nina/src/instructions/post_init_via_hub.rs index 721ba0cf..4071c04d 100644 --- a/programs/nina/src/instructions/post_init_via_hub.rs +++ b/programs/nina/src/instructions/post_init_via_hub.rs @@ -1,25 +1,23 @@ use anchor_lang::prelude::*; use crate::state::*; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; #[derive(Accounts)] #[instruction( - hub_handle: String, + _hub_handle: String, slug: String, _uri: String, )] pub struct PostInitViaHub<'info> { #[account(mut)] - pub author: Signer<'info>, - #[account( - seeds = [b"nina-hub".as_ref(), hub_handle.as_bytes()], - bump, - )] + pub payer: Signer<'info>, pub hub: AccountLoader<'info, Hub>, #[account( init, seeds = [b"nina-post".as_ref(), hub.key().as_ref(), slug.as_ref()], bump, - payer = author, + payer = payer, space = 328 )] pub post: AccountLoader<'info, Post>, @@ -27,7 +25,7 @@ pub struct PostInitViaHub<'info> { init, seeds = [b"nina-hub-post".as_ref(), hub.key().as_ref(), post.key().as_ref()], bump, - payer = author, + payer = payer, space = 244 )] pub hub_post: AccountLoader<'info, HubPost>, @@ -35,7 +33,7 @@ pub struct PostInitViaHub<'info> { init, seeds = [b"nina-hub-content".as_ref(), hub.key().as_ref(), post.key().as_ref()], bump, - payer = author, + payer = payer, space = 153 )] pub hub_content: Account<'info, HubContent>, @@ -47,6 +45,9 @@ pub struct PostInitViaHub<'info> { pub hub_collaborator: Account<'info, HubCollaborator>, pub system_program: Program<'info, System>, pub rent: Sysvar<'info, Rent>, + /// CHECK: This is safe because we check in the handler that author === payer + /// or that payer is nina operated file-service wallet + pub author: UncheckedAccount<'info>, } pub fn handler ( @@ -55,8 +56,14 @@ pub fn handler ( slug: String, uri: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.author.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::PostInitViaHubDelegatePayerMismatch.into()); + } + } + Post::post_init_helper( - &mut ctx.accounts.author, + ctx.accounts.author.key(), ctx.accounts.hub.clone(), &mut ctx.accounts.post, &mut ctx.accounts.hub_post, diff --git a/programs/nina/src/instructions/post_init_via_hub_with_reference_release.rs b/programs/nina/src/instructions/post_init_via_hub_with_reference_release.rs index 0cf2ca9b..e1b92a08 100644 --- a/programs/nina/src/instructions/post_init_via_hub_with_reference_release.rs +++ b/programs/nina/src/instructions/post_init_via_hub_with_reference_release.rs @@ -12,7 +12,7 @@ pub struct PostInitViaHubWithReferenceRelease<'info> { #[account(mut)] pub author: Signer<'info>, #[account( - seeds = [b"nina-hub".as_ref(), hub_handle.as_bytes()], + seeds = [b"nina-hub".as_ref(), hub_handle.as_ref()], bump, )] pub hub: AccountLoader<'info, Hub>, @@ -79,7 +79,7 @@ pub fn handler ( let release = &ctx.accounts.reference_release; Post::post_init_helper( - &mut ctx.accounts.author, + ctx.accounts.author.key(), ctx.accounts.hub.clone(), &mut ctx.accounts.post, &mut ctx.accounts.hub_post, diff --git a/programs/nina/src/instructions/post_update_via_hub_post.rs b/programs/nina/src/instructions/post_update_via_hub_post.rs index bb3ed0c9..21dced51 100644 --- a/programs/nina/src/instructions/post_update_via_hub_post.rs +++ b/programs/nina/src/instructions/post_update_via_hub_post.rs @@ -1,5 +1,7 @@ use anchor_lang::prelude::*; use crate::state::*; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; #[derive(Accounts)] #[instruction( @@ -8,11 +10,13 @@ use crate::state::*; _uri: String, )] pub struct PostUpdateViaHubPost<'info> { + #[account(mut)] + pub payer: Signer<'info>, + ///CHECK: This is safe bc we check constraints #[account( - mut, constraint = author.key() == post.load()?.author )] - pub author: Signer<'info>, + pub author: UncheckedAccount<'info>, #[account( seeds = [b"nina-hub".as_ref(), hub_handle.as_bytes()], bump, @@ -43,6 +47,12 @@ pub fn handler ( _slug: String, uri: String, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.author.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::PostUpdateViaHubPostDelegatePayerMismatch.into()); + } + } + let mut post = ctx.accounts.post.load_mut()?; let mut uri_array = [0u8; 100]; diff --git a/programs/nina/src/instructions/release_close_edition.rs b/programs/nina/src/instructions/release_close_edition.rs index a95a804d..f159920f 100644 --- a/programs/nina/src/instructions/release_close_edition.rs +++ b/programs/nina/src/instructions/release_close_edition.rs @@ -2,9 +2,13 @@ use anchor_lang::prelude::*; use anchor_spl::token::Mint; use solana_program::program_option::COption; use crate::state::*; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; #[derive(Accounts)] pub struct ReleaseCloseEdition<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -29,6 +33,12 @@ pub struct ReleaseCloseEdition<'info> { } pub fn handler(ctx: Context) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::ReleaseCloseEditionDelegatePayerMismatch.into()); + } + } + let mut release = ctx.accounts.release.load_mut()?; release.total_supply = release.sale_counter; release.remaining_supply = 0; diff --git a/programs/nina/src/instructions/release_revenue_share_collect.rs b/programs/nina/src/instructions/release_revenue_share_collect.rs index 961e92ff..b08cdc31 100644 --- a/programs/nina/src/instructions/release_revenue_share_collect.rs +++ b/programs/nina/src/instructions/release_revenue_share_collect.rs @@ -3,11 +3,16 @@ use anchor_lang::solana_program::{ program_option::{COption}, }; use anchor_spl::token::{self, Mint, Token, TokenAccount}; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; use crate::state::*; #[derive(Accounts)] pub struct ReleaseRevenueShareCollect<'info> { + #[account(mut)] + pub payer: Signer<'info>, + #[account(mut)] pub authority: Signer<'info>, #[account( mut, @@ -47,6 +52,11 @@ pub struct ReleaseRevenueShareCollect<'info> { pub fn handler( ctx: Context, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::ReleaseRevenueShareCollectDelegatePayerMismatch.into()); + } + } Release::release_revenue_share_collect_handler( &ctx.accounts.release, diff --git a/programs/nina/src/instructions/release_revenue_share_transfer.rs b/programs/nina/src/instructions/release_revenue_share_transfer.rs index c73035ce..2a953efb 100644 --- a/programs/nina/src/instructions/release_revenue_share_transfer.rs +++ b/programs/nina/src/instructions/release_revenue_share_transfer.rs @@ -3,11 +3,16 @@ use anchor_lang::solana_program::{ program_option::{COption}, }; use anchor_spl::token::{self, Mint, Token, TokenAccount}; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; use crate::state::*; #[derive(Accounts)] pub struct ReleaseRevenueShareTransfer<'info> { + #[account(mut)] + pub payer: Signer<'info>, + #[account(mut)] pub authority: Signer<'info>, #[account( mut, @@ -54,6 +59,12 @@ pub fn handler( ctx: Context, transfer_share: u64, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::ReleaseRevenueShareTransferDelegatePayerMismatch.into()); + } + } + // Collect Royalty so transferring user has no pending royalties Release::release_revenue_share_collect_handler( &ctx.accounts.release, diff --git a/programs/nina/src/instructions/release_update_metadata.rs b/programs/nina/src/instructions/release_update_metadata.rs index 3d03da21..63fb35b6 100644 --- a/programs/nina/src/instructions/release_update_metadata.rs +++ b/programs/nina/src/instructions/release_update_metadata.rs @@ -4,9 +4,13 @@ use solana_program::program_option::COption; use mpl_token_metadata::state::DataV2; use crate::state::*; +use crate::utils::{file_service_account}; +use crate::errors::ErrorCode; #[derive(Accounts)] pub struct ReleaseUpdateMetadata<'info> { + #[account(mut)] + pub payer: Signer<'info>, #[account(mut)] pub authority: Signer<'info>, #[account( @@ -44,6 +48,12 @@ pub fn handler( bumps: ReleaseBumps, metadata_data: ReleaseMetadataData, ) -> Result<()> { + if ctx.accounts.payer.key() != ctx.accounts.authority.key() { + if ctx.accounts.payer.key() != file_service_account::ID { + return Err(ErrorCode::ReleaseUpdateMetadataDelegatePayerMismatch.into()); + } + } + Release::update_metadata_handler( ctx.accounts.release_signer.to_account_info().clone(), ctx.accounts.metadata.to_account_info().clone(), diff --git a/programs/nina/src/state/hub.rs b/programs/nina/src/state/hub.rs index 9823b5ba..54ff0bc5 100644 --- a/programs/nina/src/state/hub.rs +++ b/programs/nina/src/state/hub.rs @@ -97,6 +97,7 @@ pub struct HubRelease { pub enum HubContentType { NinaReleaseV1 = 0, Post = 1, + PostV2 = 2 } impl Default for HubContentType { diff --git a/programs/nina/src/state/post.rs b/programs/nina/src/state/post.rs index 29d86385..af76d51c 100644 --- a/programs/nina/src/state/post.rs +++ b/programs/nina/src/state/post.rs @@ -14,7 +14,7 @@ pub struct Post { impl Post { pub fn post_init_helper<'info> ( - author: &mut Signer<'info>, + author: Pubkey, hub: AccountLoader<'info, Hub>, post_account_loader: &mut AccountLoader<'info, Post>, hub_post_account_loader: &mut AccountLoader<'info, HubPost>, @@ -30,7 +30,7 @@ impl Post { )?; let mut post = post_account_loader.load_init()?; - post.author = author.key(); + post.author = author; post.created_at = Clock::get()?.unix_timestamp; post.updated_at = post.created_at; @@ -43,10 +43,10 @@ impl Post { post.uri = uri_array; let hub_content = hub_content_account; - hub_content.added_by = author.key(); + hub_content.added_by = author; hub_content.hub = hub.key(); hub_content.child = hub_post_account_loader.key(); - hub_content.content_type = HubContentType::Post; + hub_content.content_type = HubContentType::PostV2; hub_content.datetime = post.created_at; hub_content.visible = true; hub_content.published_through_hub = true; diff --git a/tests/index.js b/tests/index.js index d0220248..98ef66f0 100644 --- a/tests/index.js +++ b/tests/index.js @@ -517,6 +517,7 @@ describe('Release', async () => { bumps, metadataData, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, release, releaseSigner, @@ -545,6 +546,7 @@ describe('Release', async () => { bumps, metadataData, { accounts: { + payer: user1.publicKey, authority: user1.publicKey, release, releaseSigner, @@ -1323,6 +1325,7 @@ describe('Release', async () => { await nina.rpc.releaseCloseEdition({ accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, release: releaseSellOut, releaseSigner: releaseSignerSellOut, @@ -1506,6 +1509,7 @@ describe("Revenue Share", async () => { await nina.rpc.releaseRevenueShareCollect({ accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount: usdcTokenAccount, release, @@ -1544,6 +1548,7 @@ describe("Revenue Share", async () => { async () => { await nina.rpc.releaseRevenueShareCollect({ accounts: { + payer: user1.publicKey, authority: user1.publicKey, authorityTokenAccount: user1UsdcTokenAccount, release, @@ -1575,6 +1580,7 @@ describe("Revenue Share", async () => { await nina.rpc.releaseRevenueShareCollect({ accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount: wrappedSolTokenAccount, release: release2, @@ -1625,6 +1631,7 @@ describe("Revenue Share", async () => { await nina.rpc.releaseRevenueShareTransfer( new anchor.BN(amountToTransfer), { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount: usdcTokenAccount, release, @@ -1667,6 +1674,7 @@ describe("Revenue Share", async () => { await nina.rpc.releaseRevenueShareTransfer( new anchor.BN(amountToTransfer), { accounts: { + payer: user3.publicKey, authority: user3.publicKey, authorityTokenAccount: user3UsdcTokenAccount, release, @@ -1698,6 +1706,7 @@ describe("Revenue Share", async () => { await nina.rpc.releaseRevenueShareTransfer( new anchor.BN(amountToTransfer), { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount: usdcTokenAccount, release, @@ -1741,6 +1750,7 @@ describe("Revenue Share", async () => { await nina.rpc.releaseRevenueShareTransfer( new anchor.BN(amountToTransfer), { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount: usdcTokenAccount, release, @@ -1773,6 +1783,7 @@ describe("Revenue Share", async () => { await nina.rpc.releaseRevenueShareTransfer( new anchor.BN(amountToTransfer), { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityTokenAccount: usdcTokenAccount, release, @@ -3863,6 +3874,7 @@ describe('Hub', async () => { new anchor.BN(10), hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityHubCollaborator, hub, @@ -3885,6 +3897,7 @@ describe('Hub', async () => { new anchor.BN(5), hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityHubCollaborator, hub, @@ -3928,6 +3941,7 @@ describe('Hub', async () => { new anchor.BN(10), hubParams.handle, { accounts: { + payer: user1.publicKey, authority: user1.publicKey, authorityHubCollaborator, hub, @@ -4026,6 +4040,7 @@ describe('Hub', async () => { await nina.rpc.hubAddRelease( hubParams.handle, { accounts: { + payer: user1.publicKey, authority: user1.publicKey, hub, hubRelease, @@ -4070,6 +4085,7 @@ describe('Hub', async () => { new anchor.BN(10), hubParams.handle, { accounts: { + payer: user2.publicKey, authority: user2.publicKey, authorityHubCollaborator, hub, @@ -4567,6 +4583,7 @@ describe('Hub', async () => { hubParams.publishFee, hubParams.referralFee, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, } @@ -4586,6 +4603,7 @@ describe('Hub', async () => { hubParams.publishFee, new anchor.BN(1000001), { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, } @@ -4608,6 +4626,7 @@ describe('Hub', async () => { new anchor.BN(1000001), hubParams.referralFee, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, } @@ -4630,6 +4649,7 @@ describe('Hub', async () => { hubParams.publishFee, hubParams.referralFee, { accounts: { + payer: user2.publicKey, authority: user2.publicKey, hub, }, @@ -4922,6 +4942,7 @@ describe('Hub', async () => { await nina.rpc.hubRemoveCollaborator( hubParams.handle, { accounts: { + payer: user2.publicKey, authority: user2.publicKey, authorityHubCollaborator, hub, @@ -4963,6 +4984,7 @@ describe('Hub', async () => { await nina.rpc.hubRemoveCollaborator( hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityHubCollaborator, hub, @@ -5000,6 +5022,7 @@ describe('Hub', async () => { await nina.rpc.hubRemoveCollaborator( hubParams.handle, { accounts: { + payer: user1.publicKey, authority: user1.publicKey, authorityHubCollaborator, hub, @@ -5038,6 +5061,7 @@ describe('Hub', async () => { await nina.rpc.hubContentToggleVisibility( hubParams.handle, { accounts: { + payer: user2.publicKey, authority: user2.publicKey, hub, hubContent, @@ -5079,6 +5103,7 @@ describe('Hub', async () => { new anchor.BN(10), hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, authorityHubCollaborator, hub, @@ -5093,6 +5118,7 @@ describe('Hub', async () => { await nina.rpc.hubRemoveCollaborator( hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, hubCollaborator, @@ -5139,6 +5165,7 @@ describe('Hub', async () => { await nina.rpc.hubAddRelease( hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, hubRelease, @@ -5154,6 +5181,7 @@ describe('Hub', async () => { await nina.rpc.hubContentToggleVisibility( hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, hubContent, @@ -5171,6 +5199,7 @@ describe('Hub', async () => { await nina.rpc.releaseRevenueShareCollectViaHub( hubParams.handle, { accounts: { + payer: user2.publicKey, authority: user2.publicKey, royaltyTokenAccount: hubRoyaltyTokenAccount, release:releaseAccount, @@ -5209,6 +5238,7 @@ describe('Hub', async () => { await nina.rpc.releaseRevenueShareCollectViaHub( hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, royaltyTokenAccount: hubRoyaltyTokenAccount, release:releaseAccount, @@ -5278,6 +5308,7 @@ describe('Hub', async () => { new anchor.BN(withdrawAmount), hubParams.handle, { accounts: { + payer: provider.wallet.publicKey, authority: provider.wallet.publicKey, hub, hubSigner, @@ -5349,6 +5380,7 @@ describe('Hub', async () => { slug, uri, { accounts: { + payer: provider.wallet.publicKey, author: provider.wallet.publicKey, hub, post, @@ -5415,6 +5447,7 @@ describe('Hub', async () => { slug, uri, { accounts: { + payer: provider.wallet.publicKey, author: provider.wallet.publicKey, hub, post, @@ -5441,6 +5474,7 @@ describe('Hub', async () => { slug, updatedUri, { accounts: { + payer: provider.wallet.publicKey, author: provider.wallet.publicKey, hub, post, @@ -5465,6 +5499,7 @@ describe('Hub', async () => { slug, updatedUri, { accounts: { + payer: user1.publicKey, author: user1.publicKey, hub, post,