diff --git a/packages/sdk/src/batchChangeCreators/batchChangeCreators.ts b/packages/sdk/src/batchChangeCreators/batchChangeCreators.ts index 9b770214d..5530e1747 100644 --- a/packages/sdk/src/batchChangeCreators/batchChangeCreators.ts +++ b/packages/sdk/src/batchChangeCreators/batchChangeCreators.ts @@ -13,15 +13,15 @@ export const batchChangeCreators = ( args: BatchChangeCreatorsArgs, ): NearContractCall => { const { addCreators = [], removeCreators = [], contractAddress = mbjs.keys.contractAddress } = args; + + if (!contractAddress) { + throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); + } if (!isStoreV2(contractAddress)) { throw new Error(ERROR_MESSAGES.ONLY_V2); } - if (contractAddress == null) { - throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); - } - if (addCreators.length === 0 && removeCreators.length === 0) { throw new Error(ERROR_MESSAGES.BATCH_CHANGE_CREATORS_NO_CHANGE); } diff --git a/packages/sdk/src/batchChangeMinters/batchChangeMinters.ts b/packages/sdk/src/batchChangeMinters/batchChangeMinters.ts index ebeb3b525..85b754e86 100644 --- a/packages/sdk/src/batchChangeMinters/batchChangeMinters.ts +++ b/packages/sdk/src/batchChangeMinters/batchChangeMinters.ts @@ -13,15 +13,15 @@ export const batchChangeMinters = ( args: BatchChangeMintersArgs, ): NearContractCall => { const { addMinters = [], removeMinters = [], contractAddress = mbjs.keys.contractAddress } = args; + + if (!contractAddress) { + throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); + } if (!isStoreV1(contractAddress)) { throw new Error(ERROR_MESSAGES.ONLY_V1); } - if (contractAddress == null) { - throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); - } - if (addMinters.length === 0 && removeMinters.length === 0) { throw new Error(ERROR_MESSAGES.BATCH_CHANGE_MINTERS_NO_CHANGE); } diff --git a/packages/sdk/src/burn/burn.ts b/packages/sdk/src/burn/burn.ts index 39614625d..d450b831e 100644 --- a/packages/sdk/src/burn/burn.ts +++ b/packages/sdk/src/burn/burn.ts @@ -11,7 +11,7 @@ import { ERROR_MESSAGES } from '../errorMessages'; */ export const burn = ({ tokenIds, contractAddress = mbjs.keys.contractAddress }: BurnArgs): NearContractCall => { - if (contractAddress == null) { + if (!contractAddress) { throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); } diff --git a/packages/sdk/src/buy/buy.ts b/packages/sdk/src/buy/buy.ts index 93eb935b9..ea54c9f90 100644 --- a/packages/sdk/src/buy/buy.ts +++ b/packages/sdk/src/buy/buy.ts @@ -14,7 +14,7 @@ import { BuyArgs, BuyArgsFtResponse, BuyArgsResponse, FT_METHOD_NAMES, MARKET_ME export const buy = (args: BuyArgs): NearContractCall => { const { contractAddress = mbjs.keys.contractAddress, tokenId, referrerId = null, marketId = mbjs.keys.marketAddress, price, affiliateAccount } = args; - if (contractAddress == null) { + if (!contractAddress) { throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); } diff --git a/packages/sdk/src/config/config.mocks.ts b/packages/sdk/src/config/config.mocks.ts index 2c10c2c95..939ff8141 100644 --- a/packages/sdk/src/config/config.mocks.ts +++ b/packages/sdk/src/config/config.mocks.ts @@ -14,6 +14,7 @@ export const TESTNET_MOCK = { network: NEAR_NETWORKS.TESTNET, connectProxyAddress: null, ftAddresses: { usdc: USDC_ADDRESS.testnet, usdt: USDT_ADDRESS.testnet }, + checkVersions: true, }; @@ -31,4 +32,5 @@ export const MAINNET_MOCK = { network: NEAR_NETWORKS.MAINNET, connectProxyAddress: null, ftAddresses: { usdc: USDC_ADDRESS.mainnet, usdt: USDT_ADDRESS.mainnet }, + checkVersions: true, }; diff --git a/packages/sdk/src/config/config.ts b/packages/sdk/src/config/config.ts index f274af493..f64f0b2c7 100644 --- a/packages/sdk/src/config/config.ts +++ b/packages/sdk/src/config/config.ts @@ -42,6 +42,7 @@ const startupConfig: MbJsKeysObject = { debugMode: isDebugMode ? true : false, ftAddresses: isProcessEnv ? FT_ADDRESSES[process.env.NEAR_NETWORK] : FT_ADDRESSES[NEAR_NETWORKS.MAINNET], isSet: isProcessEnv ? true : false, + checkVersions: true, }; // config is scoped globally as to avoid version mismatches from conflicting @@ -64,6 +65,7 @@ export const setGlobalEnv = (configObj: ConfigOptions): MbJsKeysObject => { connectProxyAddress: null, ftAddresses: FT_ADDRESSES[configObj.network], isSet: true, + checkVersions: true, }; config.network = globalConfig.network; diff --git a/packages/sdk/src/createMetadata/README.md b/packages/sdk/src/createMetadata/README.md index 17627323b..d0927c2f1 100644 --- a/packages/sdk/src/createMetadata/README.md +++ b/packages/sdk/src/createMetadata/README.md @@ -8,7 +8,7 @@ The reference material is typically uploaded to IPFS or Arweave and can be easil Royalties can be configured to provide a customized flow of funds as explained below. -You can restrict minting via an allowlist of NEAR account IDs that are allowed to mint (`minter_allowslist`), via a maximum supply that will be enforced by the smart contract (`max_supply`), and via an expiry date (`last_possible_mint`). +You can restrict minting via an allowlist of NEAR account IDs that are allowed to mint (`mintersAllowslist`), via a maximum supply that will be enforced by the smart contract (`maxSupply`), and via an expiry date (`lastPossibleMint`). You can opt-in to making an NFT dynamic (`isDynamic`) to allow [future updates](../updateMetadata/README.md), and [lock the metadata](../lockMetadata/README.md) at a later time. The `nftContactId` can be supplied as an argument or through the `TOKEN_CONTRACT` environment variable. @@ -44,6 +44,8 @@ export type CreateMetadataArgs = { noMedia?: boolean; // explicit opt-in to NFT without reference noReference?: boolean; + // explicit opt-in to make the NFT dynamic and allow future updates + isDynamic?: boolean; }; export type TokenMetadata = { @@ -65,7 +67,7 @@ export type TokenMetadata = { ## React example Example usage of mint method in a hypothetical React component: -{% code title="MintComponent.ts" overflow="wrap" lineNumbers="true" %} +{% code title="CreateMetadataComponent.ts" overflow="wrap" lineNumbers="true" %} ```typescript import { useState } from 'react'; diff --git a/packages/sdk/src/createMetadata/createMetadata.test.ts b/packages/sdk/src/createMetadata/createMetadata.test.ts index c4f8235e1..f1e497ff4 100644 --- a/packages/sdk/src/createMetadata/createMetadata.test.ts +++ b/packages/sdk/src/createMetadata/createMetadata.test.ts @@ -31,8 +31,9 @@ describe('createMetadata method tests', () => { minters_allowlist: null, max_supply: null, last_possible_mint: null, + is_dynamic: null, }, - deposit: '2270000000000000000000', + deposit: '2950000000000000000000', gas: GAS, }); }); @@ -59,8 +60,9 @@ describe('createMetadata method tests', () => { minters_allowlist: null, max_supply: null, last_possible_mint: null, + is_dynamic: null, }, - deposit: '2270000000000000000000', + deposit: '2950000000000000000000', gas: GAS, }); }); @@ -87,8 +89,9 @@ describe('createMetadata method tests', () => { minters_allowlist: ['foo', 'bar'], max_supply: null, last_possible_mint: null, + is_dynamic: null, }, - deposit: '2270000000000000000000', + deposit: '4550000000000000000000', gas: GAS, }); }); @@ -115,8 +118,9 @@ describe('createMetadata method tests', () => { minters_allowlist: null, max_supply: 10, last_possible_mint: null, + is_dynamic: null, }, - deposit: '2270000000000000000000', + deposit: '2950000000000000000000', gas: GAS, }); }); @@ -143,8 +147,38 @@ describe('createMetadata method tests', () => { minters_allowlist: null, max_supply: null, last_possible_mint: '1640995200000000000', + is_dynamic: null, }, - deposit: '2270000000000000000000', + deposit: '2950000000000000000000', + gas: GAS, + }); + }); + + test('createMetadata which for dynamic NFTs', () => { + const args = createMetadata({ + contractAddress: contractAddress, + metadata: { reference, media }, + isDynamic: true, + price: 1, + }); + + expect(args).toEqual({ + contractAddress: contractAddress, + methodName: TOKEN_METHOD_NAMES.CREATE_METADATA, + args: { + metadata: { + reference: reference, + media: media, + }, + price: `1${'0'.repeat(24)}`, + metadata_id: null, + royalty_args: null, + minters_allowlist: null, + max_supply: null, + is_dynamic: true, + last_possible_mint: null, + }, + deposit: '2950000000000000000000', gas: GAS, }); }); @@ -178,8 +212,9 @@ describe('createMetadata method tests', () => { minters_allowlist: null, max_supply: null, last_possible_mint: null, + is_dynamic: null, }, - deposit: '4670000000000000000000', + deposit: '5350000000000000000000', gas: GAS, }); }); diff --git a/packages/sdk/src/createMetadata/createMetadata.ts b/packages/sdk/src/createMetadata/createMetadata.ts index 5d309c578..ce1f3a003 100644 --- a/packages/sdk/src/createMetadata/createMetadata.ts +++ b/packages/sdk/src/createMetadata/createMetadata.ts @@ -22,18 +22,19 @@ export const createMetadata = ( mintersAllowlist = null, maxSupply = null, lastPossibleMint = null, + isDynamic = null, noMedia = false, noReference = false, } = args; + + if (!contractAddress) { + throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); + } if (!isStoreV2(contractAddress)) { throw new Error(ERROR_MESSAGES.ONLY_V2); } - if (contractAddress == null) { - throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); - } - // Reference and media need to be present or explicitly opted out of if (!noReference && !metadata.reference) { throw new Error(ERROR_MESSAGES.NO_REFERENCE); @@ -58,12 +59,14 @@ export const createMetadata = ( minters_allowlist: mintersAllowlist, max_supply: maxSupply, last_possible_mint: lastPossibleMint ? (+lastPossibleMint * 1e6).toString() : null, + is_dynamic: isDynamic, price: new BN(price * 1e6).mul(new BN(`1${'0'.repeat(18)}`)).toString(), }, methodName: TOKEN_METHOD_NAMES.CREATE_METADATA, gas: GAS, deposit: createMetadataDeposit({ nRoyalties: !royalties ? 0 : Object.keys(royalties)?.length, + nMinters: !mintersAllowlist? 0 : mintersAllowlist.length, metadata, }), }; @@ -71,17 +74,20 @@ export const createMetadata = ( export function createMetadataDeposit({ nRoyalties, + nMinters, metadata, }: { nRoyalties: number; + nMinters: number; metadata: TokenMetadata; }): string { const metadataBytesEstimate = JSON.stringify(metadata).length; - - const totalBytes = STORAGE_BYTES.MINTING_BASE + + // storage + nRoyalties * common + nMinters * common + 2 * common + const totalBytes = 2 * STORAGE_BYTES.COMMON + STORAGE_BYTES.MINTING_FEE + metadataBytesEstimate + - STORAGE_BYTES.COMMON * nRoyalties; + STORAGE_BYTES.COMMON * nRoyalties + + STORAGE_BYTES.COMMON * nMinters; return `${Math.ceil(totalBytes)}${'0'.repeat(STORAGE_PRICE_PER_BYTE_EXPONENT)}`; } diff --git a/packages/sdk/src/delist/delist.ts b/packages/sdk/src/delist/delist.ts index 401c62f23..ebc86ce62 100644 --- a/packages/sdk/src/delist/delist.ts +++ b/packages/sdk/src/delist/delist.ts @@ -14,9 +14,8 @@ export const delist = ( args: DelistArgs, ): NearContractCall => { const { contractAddress = mbjs.keys.contractAddress, tokenIds, marketAddress = mbjs.keys.marketAddress, oldMarket = false } = args; - - if (contractAddress == null) { + if (!contractAddress) { throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); } diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index d0870fa37..547a4884f 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,6 +1,9 @@ export * from './burn/burn'; export * from './constants'; export * from './createMetadata/createMetadata'; +export * from './mintOnMetadata/mintOnMetadata'; +export * from './updateMetadata/updateMetadata'; +export * from './lockMetadata/lockMetadata'; export * from './execute/execute'; export * from './execute/execute.utils'; export * from './transfer/transfer'; diff --git a/packages/sdk/src/list/list.ts b/packages/sdk/src/list/list.ts index 4f18497ab..95858cdbc 100644 --- a/packages/sdk/src/list/list.ts +++ b/packages/sdk/src/list/list.ts @@ -13,7 +13,7 @@ import { ListArgs, ListArgsResponse, MARKET_METHOD_NAMES, NearContractCall } fro export const list = (args: ListArgs): NearContractCall => { const { contractAddress = mbjs.keys.contractAddress, tokenId, marketAddress = mbjs.keys.marketAddress, price } = args; - if (contractAddress == null) { + if (!contractAddress) { throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); } diff --git a/packages/sdk/src/lockMetadata/README.md b/packages/sdk/src/lockMetadata/README.md new file mode 100644 index 000000000..a205dca98 --- /dev/null +++ b/packages/sdk/src/lockMetadata/README.md @@ -0,0 +1,60 @@ +[//]: # `{ "title": "lockMetadata", "order": 0.21 }` + +# Lock Metadata (v2) + +Lock [previously created metadata](../createMetadata/README.md). This is only possible if the metadata has been marked as dynamic while creating it, and will prevent any further updates. This operation is irrevertible. Only the creator of the metadata is allowed to lock it. + +The `nftContactId` can be supplied as an argument or through the `TOKEN_CONTRACT` environment variable. + +This only works on v2 smart contracts and no equivalent feature exists for v1. + +**As with all new SDK api methods, this call should be wrapped in [execute](../#execute) and passed a signing method. For a guide showing how to make a contract call with mintbase-js click [here](https://docs.mintbase.xyz/dev/getting-started/make-your-first-contract-call-deploycontract)** + +## lockMetadata(args: UpdateMetadataArgs): NearContractCall + +`lockMetadata` takes a single argument of type `LockMetadataArgs` + +```typescript +export type UpdateMetadataArgs = { + //the contractId from which you want to mint, this can be statically defined via the mbjs config file + contractAddress?: string; + //the ID of the metadata you wish to lock + metadataId: string; +}; +``` + +## React example + +Example usage of mint method in a hypothetical React component: +{% code title="LockMetadataComponent.ts" overflow="wrap" lineNumbers="true" %} + +```typescript +import { useState } from 'react'; +import { useWallet } from '@mintbase-js/react'; +import { execute, lockMetadata, LockMetadataArgs } from '@mintbase-js/sdk'; + + +export const LockMetadataComponent = ({ contractAddress, metadataId }: LockMetadataArgs): JSX.Element => { + + const { selector } = useWallet(); + + const handleLockMetadata = async (): Promise => { + + const wallet = await selector.wallet(); + + await execute( + lockMetadata({ contractAddress, metadataId }) + ); + + } + + return ( +
+ +
+ ); +}; +``` +{% endcode %} diff --git a/packages/sdk/src/lockMetadata/lockMetadata.test.ts b/packages/sdk/src/lockMetadata/lockMetadata.test.ts new file mode 100644 index 000000000..8bceac141 --- /dev/null +++ b/packages/sdk/src/lockMetadata/lockMetadata.test.ts @@ -0,0 +1,27 @@ +import { GAS } from '../constants'; +import { TOKEN_METHOD_NAMES } from '../types'; +import { lockMetadata } from './lockMetadata'; +import { mbjs } from '../config/config'; + +describe('updateMetadata method tests', () => { + const contractAddress = `test.${mbjs.keys.mbContractV2}`; + // const contractAddressV2 = 'test.mintbase2.near'; + const ownerId = 'test'; + + test('updateMetadata without options', () => { + const args = lockMetadata({ + contractAddress: contractAddress, + metadataId: '1', + }); + + expect(args).toEqual({ + contractAddress: contractAddress, + methodName: TOKEN_METHOD_NAMES.LOCK_METADATA, + args: { + metadata_id: '1', + }, + deposit: '1', + gas: GAS, + }); + }); +}); diff --git a/packages/sdk/src/lockMetadata/lockMetadata.ts b/packages/sdk/src/lockMetadata/lockMetadata.ts new file mode 100644 index 000000000..6868f81b4 --- /dev/null +++ b/packages/sdk/src/lockMetadata/lockMetadata.ts @@ -0,0 +1,42 @@ +import BN from 'bn.js'; +import { mbjs } from '../config/config'; +import { GAS, STORAGE_BYTES, STORAGE_PRICE_PER_BYTE_EXPONENT } from '../constants'; +import { ERROR_MESSAGES } from '../errorMessages'; +import { LockMetadataArgs, LockMetadataArgsResponse, NearContractCall, TOKEN_METHOD_NAMES } from '../types'; +import { isIntString, isStoreV2 } from '../utils'; + +/** + * Mint a token given via reference json on a given contract with a specified owner, amount of copies as well and royalties can be specified via options + * @param mintArguments {@link MintArgs} + * @returns contract call to be passed to @mintbase-js/sdk execute method + */ +export const lockMetadata = ( + args: LockMetadataArgs, +): NearContractCall => { + const { + contractAddress = mbjs.keys.contractAddress, + metadataId, + } = args; + + if (!contractAddress) { + throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); + } + + if (!isStoreV2(contractAddress)) { + throw new Error(ERROR_MESSAGES.ONLY_V2); + } + + if (!isIntString(metadataId)) { + throw new Error(ERROR_MESSAGES.METADATA_ID_NOT_INT); + } + + return { + contractAddress: contractAddress || mbjs.keys.contractAddress, + args: { + metadata_id: metadataId, + }, + methodName: TOKEN_METHOD_NAMES.LOCK_METADATA, + gas: GAS, + deposit: "1", + }; +}; diff --git a/packages/sdk/src/mint/mint.ts b/packages/sdk/src/mint/mint.ts index c34231fa0..35dfacfbd 100644 --- a/packages/sdk/src/mint/mint.ts +++ b/packages/sdk/src/mint/mint.ts @@ -21,15 +21,15 @@ export const mint = ( noMedia = false, noReference = false, } = args; + + if (!contractAddress) { + throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); + } if (!isStoreV1(contractAddress)) { throw new Error(ERROR_MESSAGES.ONLY_V1); } - if (contractAddress == null) { - throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); - } - // Reference and media need to be present or explicitly opted out of if (!noReference && !metadata.reference) { throw new Error(ERROR_MESSAGES.NO_REFERENCE); diff --git a/packages/sdk/src/mintOnMetadata/README.md b/packages/sdk/src/mintOnMetadata/README.md index 152274dfc..d23b036dd 100644 --- a/packages/sdk/src/mintOnMetadata/README.md +++ b/packages/sdk/src/mintOnMetadata/README.md @@ -36,7 +36,7 @@ export type MintOnMetadataArgs = { ## React example Example usage of mint method in a hypothetical React component: -{% code title="MintComponent.ts" overflow="wrap" lineNumbers="true" %} +{% code title="MintOnMetadataComponent.ts" overflow="wrap" lineNumbers="true" %} ```typescript import { useState } from 'react'; diff --git a/packages/sdk/src/mintOnMetadata/mintOnMetadata.ts b/packages/sdk/src/mintOnMetadata/mintOnMetadata.ts index fd580e0d8..38ccf21e5 100644 --- a/packages/sdk/src/mintOnMetadata/mintOnMetadata.ts +++ b/packages/sdk/src/mintOnMetadata/mintOnMetadata.ts @@ -22,6 +22,10 @@ export const mintOnMetadata = ( price, } = args; + if (!contractAddress) { + throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); + } + if (!isStoreV2(contractAddress)) { throw new Error(ERROR_MESSAGES.ONLY_V2); } @@ -30,10 +34,6 @@ export const mintOnMetadata = ( throw new Error(ERROR_MESSAGES.METADATA_ID_NOT_INT); } - if (contractAddress == null) { - throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); - } - if (tokenIds && tokenIds.length === 0) { throw new Error(ERROR_MESSAGES.EMPTY_TOKEN_IDS); } diff --git a/packages/sdk/src/setSplits/setSplits.ts b/packages/sdk/src/setSplits/setSplits.ts index 2ba29f48e..098212a27 100644 --- a/packages/sdk/src/setSplits/setSplits.ts +++ b/packages/sdk/src/setSplits/setSplits.ts @@ -17,7 +17,7 @@ export const setSplits = (args: SetSplitsArgs): NearContractCall; +export type ExecuteArgsResponse = TransferArgsResponse + | ListArgsResponse + | MintArgsV1Response + | MinterArgsResponse + | DeployContractArgsResponse + | DelistMultipleArgsResponse + | BuyArgsResponse + | BuyArgsFtResponse + | BurnArgsResponse + | TransferContractOwnershipArgsResponse + | ExecuteExtraArgsResponse + | FtTransferArgsResponse + | CreateMetadataArgsResponse + | MintOnMetadataArgsResponse + | UpdateMetadataArgsResponse + | LockMetadataArgsResponse + | BatchChangeMintersArgsResponse + | Record; export type FinalExecutionOutcome = FEO; diff --git a/packages/sdk/src/updateMetadata/README.md b/packages/sdk/src/updateMetadata/README.md new file mode 100644 index 000000000..545968ef2 --- /dev/null +++ b/packages/sdk/src/updateMetadata/README.md @@ -0,0 +1,69 @@ +[//]: # `{ "title": "updateMetadata", "order": 0.20 }` + +# Update Metadata (v2) + +Update [previously created metadata](../createMetadata/README.md). This is only possible if the metadata has been marked as dynamic while creating it. Only the creator of the metadata is allowed to update it. + +In order to keep a history and mark reference material as dynamic, insert a `history` field into your reference material. This starts as an empty array and should contain all historic reference URIs when updating to a new reference material in their historic order. + +The `nftContactId` can be supplied as an argument or through the `TOKEN_CONTRACT` environment variable. + +This only works on v2 smart contracts and no equivalent feature exists for v1. + +**As with all new SDK api methods, this call should be wrapped in [execute](../#execute) and passed a signing method. For a guide showing how to make a contract call with mintbase-js click [here](https://docs.mintbase.xyz/dev/getting-started/make-your-first-contract-call-deploycontract)** + +## updateMetadata(args: UpdateMetadataArgs): NearContractCall + +`updateMetadata` takes a single argument of type `UpdateMetadataArgs` + +```typescript +export type UpdateMetadataArgs = { + //the contractId from which you want to mint, this can be statically defined via the mbjs config file + contractAddress?: string; + //the ID of the metadata you wish to update + metadataId: string; + //on chain metadata, currently reference and media must be provided unless clearly opted out using the noMedia or noReference args + //the storage module returns the media hash to be provided to the media key in the metadata object when uploading as well as the referenceId which should be supplied to the reference key. + metadata: TokenMetadata; + // explicit opt-in to NFT without media, breaks wallets + noMedia?: boolean; + // explicit opt-in to NFT without reference + noReference?: boolean; +}; +``` + +## React example + +Example usage of mint method in a hypothetical React component: +{% code title="UpdateMetadataComponent.ts" overflow="wrap" lineNumbers="true" %} + +```typescript +import { useState } from 'react'; +import { useWallet } from '@mintbase-js/react'; +import { execute, updateMetadata, MintOnMetadataArgs } from '@mintbase-js/sdk'; + + +export const UpdateMetadataComponent = ({ contractAddress, metadataId, metadata }: UpdateMetadataArgs): JSX.Element => { + + const { selector } = useWallet(); + + const handleUpdateMetadata = async (): Promise => { + + const wallet = await selector.wallet(); + + await execute( + updateMetadata({ contractAddress, metadataId, metadata }) + ); + + } + + return ( +
+ +
+ ); +}; +``` +{% endcode %} diff --git a/packages/sdk/src/updateMetadata/updateMetadata.test.ts b/packages/sdk/src/updateMetadata/updateMetadata.test.ts new file mode 100644 index 000000000..bfd91d72e --- /dev/null +++ b/packages/sdk/src/updateMetadata/updateMetadata.test.ts @@ -0,0 +1,50 @@ +import { GAS } from '../constants'; +import { ERROR_MESSAGES } from '../errorMessages'; +import { TOKEN_METHOD_NAMES } from '../types'; +import { updateMetadata } from './updateMetadata'; +import { mbjs } from '../config/config'; + +describe('updateMetadata method tests', () => { + const contractAddress = `test.${mbjs.keys.mbContractV2}`; + // const contractAddressV2 = 'test.mintbase2.near'; + const ownerId = 'test'; + + test('updateMetadata without options', () => { + const args = updateMetadata({ + contractAddress: contractAddress, + metadataId: '1', + metadata: {reference: "foo", media: "bar"}, + }); + + expect(args).toEqual({ + contractAddress: contractAddress, + methodName: TOKEN_METHOD_NAMES.UPDATE_METADATA, + args: { + metadata_id: '1', + metadata: {reference: "foo", media: "bar"}, + }, + deposit: '1', + gas: GAS, + }); + }); + + test('updateMetadata with no reference', () => { + expect(() => { + updateMetadata({ + contractAddress: contractAddress, + metadataId: "1", + metadata: { media: "foo" }, + }); + }).toThrow(ERROR_MESSAGES.NO_REFERENCE); + }); + + test('updateMetadata with no media', () => { + expect(() => { + updateMetadata({ + contractAddress: contractAddress, + metadataId: "1", + metadata: { reference: "foo" }, + }); + }).toThrow(ERROR_MESSAGES.NO_MEDIA); + }); +}); diff --git a/packages/sdk/src/updateMetadata/updateMetadata.ts b/packages/sdk/src/updateMetadata/updateMetadata.ts new file mode 100644 index 000000000..dfd3eeca9 --- /dev/null +++ b/packages/sdk/src/updateMetadata/updateMetadata.ts @@ -0,0 +1,55 @@ +import BN from 'bn.js'; +import { mbjs } from '../config/config'; +import { GAS, STORAGE_BYTES, STORAGE_PRICE_PER_BYTE_EXPONENT } from '../constants'; +import { ERROR_MESSAGES } from '../errorMessages'; +import { UpdateMetadataArgs, UpdateMetadataArgsResponse, NearContractCall, TOKEN_METHOD_NAMES } from '../types'; +import { isIntString, isStoreV2 } from '../utils'; + +/** + * Mint a token given via reference json on a given contract with a specified owner, amount of copies as well and royalties can be specified via options + * @param mintArguments {@link MintArgs} + * @returns contract call to be passed to @mintbase-js/sdk execute method + */ +export const updateMetadata = ( + args: UpdateMetadataArgs, +): NearContractCall => { + const { + contractAddress = mbjs.keys.contractAddress, + metadataId, + metadata, + noReference = false, + noMedia = false, + } = args; + + if (!contractAddress) { + throw new Error(ERROR_MESSAGES.CONTRACT_ADDRESS); + } + + if (!isStoreV2(contractAddress)) { + throw new Error(ERROR_MESSAGES.ONLY_V2); + } + + if (!isIntString(metadataId)) { + throw new Error(ERROR_MESSAGES.METADATA_ID_NOT_INT); + } + + // Reference and media need to be present or explicitly opted out of + if (!noReference && !metadata.reference) { + throw new Error(ERROR_MESSAGES.NO_REFERENCE); + } + if (!noMedia && !metadata.media) { + throw new Error(ERROR_MESSAGES.NO_MEDIA); + } + + + return { + contractAddress: contractAddress || mbjs.keys.contractAddress, + args: { + metadata_id: metadataId, + metadata, + }, + methodName: TOKEN_METHOD_NAMES.UPDATE_METADATA, + gas: GAS, + deposit: "1", + }; +}; diff --git a/packages/sdk/src/utils.ts b/packages/sdk/src/utils.ts index 340557f6c..5628e4d2f 100644 --- a/packages/sdk/src/utils.ts +++ b/packages/sdk/src/utils.ts @@ -3,11 +3,11 @@ import { ERROR_MESSAGES } from './errorMessages'; import { Splits } from './types'; export function isStoreV1(name: string): boolean { - return name.endsWith(`.${mbjs.keys.mbContract}`); + return !mbjs.keys.checkVersions || name.endsWith(`.${mbjs.keys.mbContract}`); } export function isStoreV2(name: string): boolean { - return name.endsWith(`.${mbjs.keys.mbContractV2}`); + return !mbjs.keys.checkVersions || name.endsWith(`.${mbjs.keys.mbContractV2}`); } export function standardizeString(name: string): string { diff --git a/packages/storage/src/uploads.ts b/packages/storage/src/uploads.ts index 14ce6b5df..8b3a13854 100644 --- a/packages/storage/src/uploads.ts +++ b/packages/storage/src/uploads.ts @@ -34,6 +34,7 @@ type ReferenceObject = { category?: string; tags?: string[]; extra?: Trait[]; + history?: string[] } type Trait = { @@ -74,7 +75,7 @@ export const uploadFile = async ( if (request.status !== 200) { throw new Error( - `Error uploading via arweave service: ${await request.json()}`, + `Error uploading via arweave service: ${JSON.stringify(await request.json())}`, ); } @@ -117,7 +118,7 @@ export const uploadReference = async ( if (request.status !== 200) { throw new Error( - `Error uploading via arweave service: ${await request.json()}`, + `Error uploading via arweave service: ${JSON.stringify(await request.json())}`, ); }