diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh deleted file mode 100644 index cec959a..0000000 --- a/.husky/_/husky.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env sh -if [ -z "$husky_skip_init" ]; then - debug () { - if [ "$HUSKY_DEBUG" = "1" ]; then - echo "husky (debug) - $1" - fi - } - - readonly hook_name="$(basename -- "$0")" - debug "starting $hook_name..." - - if [ "$HUSKY" = "0" ]; then - debug "HUSKY env variable is set to 0, skipping hook" - exit 0 - fi - - if [ -f ~/.huskyrc ]; then - debug "sourcing ~/.huskyrc" - . ~/.huskyrc - fi - - readonly husky_skip_init=1 - export husky_skip_init - sh -e "$0" "$@" - exitCode="$?" - - if [ $exitCode != 0 ]; then - echo "husky - $hook_name hook exited with code $exitCode (error)" - fi - - if [ $exitCode = 127 ]; then - echo "husky - command not found in PATH=$PATH" - fi - - exit $exitCode -fi diff --git a/.husky/commit-msg b/.husky/commit-msg index 0d30cdf..7e2cf34 100644 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,3 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" -npx --no-install commitlint --edit $1 \ No newline at end of file +npx --no-install commitlint --edit diff --git a/.husky/pre-commit b/.husky/pre-commit index 610c2a5..72c4429 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npm test diff --git a/package-lock.json b/package-lock.json index 23f9ab8..b170ac0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@itheum/sdk-mx-data-nft", - "version": "3.0.0", + "version": "2.7.0-beta.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@itheum/sdk-mx-data-nft", - "version": "3.0.0", + "version": "2.7.0-beta.11", "license": "GPL-3.0-only", "dependencies": { "@multiversx/sdk-core": "12.18.0", @@ -24,7 +24,7 @@ "@semantic-release/release-notes-generator": "12.1.0", "@types/jest": "29.5.11", "commitlint": "18.4.4", - "husky": "^8.0.0", + "husky": "9.0.11", "jest": "29.7.0", "semantic-release": "23.0.2", "ts-jest": "29.1.2", @@ -5032,15 +5032,15 @@ } }, "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", "dev": true, "bin": { - "husky": "lib/bin.js" + "husky": "bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" diff --git a/package.json b/package.json index 5818d45..48735b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@itheum/sdk-mx-data-nft", - "version": "3.0.0", + "version": "2.7.0-beta.11", "description": "SDK for Itheum's Data NFT Technology on MultiversX Blockchain", "main": "out/index.js", "types": "out/index.d.js", @@ -12,7 +12,7 @@ "lint": "tslint --project .", "lint:fix": "tslint --project . --fix", "build": "tsc -p tsconfig.json", - "prepare": "npm run build" + "prepare": "husky" }, "author": "Itheum Protocol", "license": "GPL-3.0-only", @@ -32,7 +32,7 @@ "@semantic-release/release-notes-generator": "12.1.0", "@types/jest": "29.5.11", "commitlint": "18.4.4", - "husky": "^8.0.0", + "husky": "9.0.11", "jest": "29.7.0", "semantic-release": "23.0.2", "ts-jest": "29.1.2", diff --git a/src/abis/core-mx-life-bonding-sc.abi.json b/src/abis/core-mx-life-bonding-sc.abi.json index d6b224e..3bf9d92 100644 --- a/src/abis/core-mx-life-bonding-sc.abi.json +++ b/src/abis/core-mx-life-bonding-sc.abi.json @@ -48,7 +48,7 @@ "type": "u64" }, { - "name": "lock_period", + "name": "lock_period_seconds", "type": "u64" } ], @@ -108,83 +108,6 @@ ], "outputs": [] }, - { - "name": "getAcceptedCallers", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "variadic
", - "multi_result": true - } - ] - }, - { - "name": "getBondPaymentToken", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "TokenIdentifier" - } - ] - }, - { - "name": "getLockPeriods", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "variadic", - "multi_result": true - } - ] - }, - { - "name": "getLockPeriodBondAmount", - "mutability": "readonly", - "inputs": [ - { - "name": "lock_period", - "type": "u64" - } - ], - "outputs": [ - { - "type": "BigUint" - } - ] - }, - { - "name": "getMinimumPenalty", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u64" - } - ] - }, - { - "name": "getMaximumPenalty", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u64" - } - ] - }, - { - "name": "getWithdrawPenalty", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u64" - } - ] - }, { "name": "getCompensationBlacklist", "mutability": "readonly", @@ -395,6 +318,35 @@ } ] }, + { + "name": "getContractConfiguration", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "ContractConfiguration" + } + ] + }, + { + "name": "initiateBond", + "mutability": "mutable", + "inputs": [ + { + "name": "address", + "type": "Address" + }, + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "nonce", + "type": "u64" + } + ], + "outputs": [] + }, { "name": "setBlacklist", "mutability": "mutable", @@ -533,7 +485,7 @@ "outputs": [] }, { - "name": "setPeriodsBonds", + "name": "addPeriodsBonds", "mutability": "mutable", "inputs": [ { @@ -620,6 +572,83 @@ "type": "Address" } ] + }, + { + "name": "getAcceptedCallers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ] + }, + { + "name": "getBondPaymentToken", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "TokenIdentifier" + } + ] + }, + { + "name": "getLockPeriods", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "getLockPeriodBondAmount", + "mutability": "readonly", + "inputs": [ + { + "name": "lock_period", + "type": "u64" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "getMinimumPenalty", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getMaximumPenalty", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getWithdrawPenalty", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] } ], "events": [ @@ -1041,6 +1070,43 @@ } ] }, + "ContractConfiguration": { + "type": "struct", + "fields": [ + { + "name": "contract_state", + "type": "State" + }, + { + "name": "bond_payment_token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "lock_periods", + "type": "List" + }, + { + "name": "bond_amounts", + "type": "List" + }, + { + "name": "minimum_penalty", + "type": "u64" + }, + { + "name": "maximum_penalty", + "type": "u64" + }, + { + "name": "withdraw_penalty", + "type": "u64" + }, + { + "name": "accepted_callers", + "type": "List
" + } + ] + }, "EsdtTokenPayment": { "type": "struct", "fields": [ diff --git a/src/bond.ts b/src/bond.ts index f5b0629..5d3f477 100644 --- a/src/bond.ts +++ b/src/bond.ts @@ -9,7 +9,8 @@ import { TokenIdentifierValue, Transaction, TypedValue, - U64Value + U64Value, + VariadicValue } from '@multiversx/sdk-core/out'; import { EnvironmentsEnum, @@ -22,12 +23,19 @@ import BigNumber from 'bignumber.js'; import bondContractAbi from './abis/core-mx-life-bonding-sc.abi.json'; import { parseBond, + parseBondConfiguration, parseCompensation, parseRefund, parseTokenIdentifier } from './common/utils'; import { Contract } from './contract'; -import { Bond, Compensation, PenaltyType, State } from './interfaces'; +import { + Bond, + BondConfiguration, + Compensation, + PenaltyType, + State +} from './interfaces'; export class BondContract extends Contract { /** @@ -71,6 +79,33 @@ export class BondContract extends Contract { } } + /** + * Returns the `bond` contract configuration + */ + async viewContractConfiguration(): Promise { + const interaction = this.contract.methodsExplicit.getContractConfiguration( + [] + ); + const query = interaction.buildQuery(); + const queryResponse = await this.networkProvider.queryContract(query); + const endpointDefinition = interaction.getEndpoint(); + const { firstValue, returnCode } = new ResultsParser().parseQueryResponse( + queryResponse, + endpointDefinition + ); + if (returnCode.isSuccess()) { + const firstValueAsVariadic = firstValue as VariadicValue; + const returnValue = firstValueAsVariadic?.valueOf(); + const bondConfiguration = parseBondConfiguration(returnValue); + return bondConfiguration; + } else { + throw new ErrContractQuery( + 'viewContractConfiguration', + returnCode.toString() + ); + } + } + /** * Returns the contract owner address */ @@ -142,7 +177,7 @@ export class BondContract extends Contract { * Returns the contract lock periods and bond amounts */ async viewLockPeriodsWithBonds(): Promise< - { lockPeriod: string; amount: string }[] + { lockPeriod: number; amount: BigNumber.Value }[] > { const interaction = this.contract.methodsExplicit.getLockPeriodsBonds([]); const query = interaction.buildQuery(); @@ -157,10 +192,10 @@ export class BondContract extends Contract { const bondAmounts: BigNumber[] = firstValue?.valueOf().field1; // Construct array of objects containing lock period and bond amount - const result: { lockPeriod: string; amount: string }[] = []; + const result: { lockPeriod: number; amount: BigNumber.Value }[] = []; for (let i = 0; i < lockPeriods.length; i++) { - const lockPeriod = lockPeriods[i].toString(); - const bondAmount = bondAmounts[i].toString(); + const lockPeriod = lockPeriods[i].toNumber(); + const bondAmount = bondAmounts[i]; result.push({ lockPeriod: lockPeriod, amount: bondAmount }); } @@ -772,26 +807,57 @@ export class BondContract extends Contract { } /** - * Builds a `setPeriodsBonds` transaction to set the periods and bonds + * Builds a `initiateBond` transaction to "whitelist" an address for being able to bond for a specific Data NFT + * @param senderAddress the address of the sender + * @param address the address to be whitelisted + * @param tokenIdentifier the token identifier + * @param nonce the token identifier nonce + */ + initiateBond( + senderAddress: IAddress, + address: IAddress, + tokenIdentifier: string, + nonce: number + ): Transaction { + const tx = new Transaction({ + value: 0, + data: new ContractCallPayloadBuilder() + .setFunction('initiateBond') + .addArg(new AddressValue(address)) + .addArg(new TokenIdentifierValue(tokenIdentifier)) + .addArg(new U64Value(nonce)) + .build(), + receiver: this.contract.getAddress(), + sender: senderAddress, + gasLimit: 20_000_000, + chainID: this.chainID + }); + return tx; + } + + /** + * Builds a `addPeriodsBonds` transaction to set the periods and bonds * @param senderAddress the address of the sender * @param periods an array of periods * @param bonds an array of bond values */ - setPeriodsBonds( + addPeriodsBonds( senderAddress: IAddress, - periods: number[], - bonds: BigNumber.Value[] + lockPeriodsWithBonds: { + lockPeriod: number; + amount: BigNumber.Value; + }[] ) { let combinedArray: TypedValue[] = []; - periods.map((period, index) => { - combinedArray.push(new U64Value(period)); - combinedArray.push(new BigUIntValue(bonds[index])); + lockPeriodsWithBonds.map((lockPeriodWithBond) => { + combinedArray.push(new U64Value(lockPeriodWithBond.lockPeriod)); + combinedArray.push(new BigUIntValue(lockPeriodWithBond.amount)); }); const tx = new Transaction({ value: 0, data: new ContractCallPayloadBuilder() - .setFunction('setPeriodsBonds') + .setFunction('addPeriodsBonds') .setArgs(combinedArray) .build(), receiver: this.contract.getAddress(), @@ -1078,11 +1144,10 @@ export class BondContract extends Contract { .setFunction('withdraw') .addArg(new TokenIdentifierValue(tokenIdentifier)) .addArg(new U64Value(nonce)) - .addArg(new U64Value(nonce)) .build(), receiver: this.contract.getAddress(), sender: senderAddress, - gasLimit: 10_000_000, + gasLimit: 50_000_000, chainID: this.chainID }); return withdrawTx; diff --git a/src/common/utils.ts b/src/common/utils.ts index c6f81db..f9bc199 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -8,11 +8,14 @@ import { } from '../errors'; import { Bond, + BondConfiguration, Compensation, + ContractConfiguration, NftEnumType, NftType, Offer, - Refund + Refund, + State } from '../interfaces'; import { EnvironmentsEnum, dataMarshalUrlOverride } from '../config'; @@ -98,6 +101,30 @@ export function parseBond(value: any): Bond { }; } +export function parseBondConfiguration(value: any): BondConfiguration { + const lockPeriods: BigNumber[] = value.lock_periods; + const bondAmounts: BigNumber[] = value.bond_amounts; + + // Construct array of objects containing lock period and bond amount + const result: { lockPeriod: number; amount: BigNumber.Value }[] = []; + for (let i = 0; i < lockPeriods.length; i++) { + const lockPeriod = lockPeriods[i].toNumber(); + const bondAmount = bondAmounts[i].toFixed(0); + result.push({ lockPeriod: lockPeriod, amount: bondAmount }); + } + return { + contractState: value.contract_state.name as State, + bondPaymentTokenIdentifier: value.bond_payment_token_identifier.toString(), + lockPeriodsWithBonds: result, + minimumPenalty: value.minimum_penalty.toNumber(), + maximumPenalty: value.maximum_penalty.toNumber(), + withdrawPenalty: value.withdraw_penalty.toNumber(), + acceptedCallers: value.accepted_callers.map((address: any) => + address.toString() + ) + }; +} + export function parseCompensation(value: any): Compensation { return { compensationId: value.compensation_id.toNumber(), diff --git a/src/interfaces.ts b/src/interfaces.ts index 91a9e9b..f960a12 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -137,6 +137,16 @@ export interface Bond { remainingAmount: BigNumber.Value; } +export interface BondConfiguration { + contractState: State; + bondPaymentTokenIdentifier: string; + lockPeriodsWithBonds: { lockPeriod: number; amount: BigNumber.Value }[]; + minimumPenalty: number; + maximumPenalty: number; + withdrawPenalty: number; + acceptedCallers: string[]; +} + export interface Refund { compensationId: number; address: string; diff --git a/src/nft-minter.ts b/src/nft-minter.ts index bd502a1..3d5af4f 100644 --- a/src/nft-minter.ts +++ b/src/nft-minter.ts @@ -187,7 +187,6 @@ export class NftMinter extends Minter { // deep validate all mandatory URLs try { - await checkUrlIsUp(dataStreamUrl, [200, 403]); await checkUrlIsUp(dataPreviewUrl, [200]); await checkUrlIsUp(dataMarshalUrl + '/health-check', [200]); } catch (error) { diff --git a/src/sft-minter.ts b/src/sft-minter.ts index cc9641b..d12b9d1 100644 --- a/src/sft-minter.ts +++ b/src/sft-minter.ts @@ -243,8 +243,8 @@ export class SftMinter extends Minter { supply: number, datasetTitle: string, datasetDescription: string, - lockPeriod: number, amountToSend: number, + lockPeriod?: number, options?: { imageUrl?: string; traitsUrl?: string; @@ -292,7 +292,6 @@ export class SftMinter extends Minter { // deep validate all mandatory URLs try { - await checkUrlIsUp(dataStreamUrl, [200, 403]); await checkUrlIsUp(dataPreviewUrl, [200]); await checkUrlIsUp(dataMarshalUrl + '/health-check', [200]); } catch (error) { @@ -363,12 +362,14 @@ export class SftMinter extends Minter { .addArg(new U64Value(royalties)) .addArg(new U64Value(supply)) .addArg(new StringValue(datasetTitle)) - .addArg(new StringValue(datasetDescription)) - .addArg(new U64Value(lockPeriod)) - .build(); + .addArg(new StringValue(datasetDescription)); + + if (lockPeriod) { + data.addArg(new U64Value(lockPeriod)); + } const mintTx = new Transaction({ - data, + data: data.build(), sender: senderAddress, receiver: this.contract.getAddress(), gasLimit: 80_000_000, diff --git a/tests/bond.test.ts b/tests/bond.test.ts index a6984bb..7587f83 100644 --- a/tests/bond.test.ts +++ b/tests/bond.test.ts @@ -1,4 +1,5 @@ import { + BondConfiguration, BondContract, Compensation, State, @@ -16,6 +17,13 @@ describe('Bond test', () => { expect(e.message).toBe('Contract address is not deployed on testnet'); } }); + + test('#view bond configuration', async () => { + const bondContract = new BondContract('devnet'); + const bondConfiguration = await bondContract.viewContractConfiguration(); + + expect(bondConfiguration).toMatchObject; + }); test('#test view methods', async () => { const bondContract = new BondContract('devnet'); diff --git a/tests/datanft.test.ts b/tests/datanft.test.ts index 8872ada..47496f3 100644 --- a/tests/datanft.test.ts +++ b/tests/datanft.test.ts @@ -96,7 +96,7 @@ describe('Data NFT test', () => { }); const owners = await dataNft.getOwners(); - }); + }, 200000); test('#parse token identifier', () => { const tokenIdentifier = 'DATANFTFT3-d0978a'; @@ -112,7 +112,7 @@ describe('Data NFT test', () => { expect(parsed).toBeInstanceOf( Object as unknown as { collection: string; nonce: String } ); - }); + }, 20000); test('#override marhsal url', async () => { DataNft.setNetworkConfig('mainnet'); @@ -124,7 +124,7 @@ describe('Data NFT test', () => { ); expect(dataNft.dataMarshal).toBe(marshalUrls[EnvironmentsEnum.devnet]); expect(dataNft.overrideDataMarshalChainId).toBe('1'); - }); + }, 20000); test('#override marshal url should be empty', async () => { DataNft.setNetworkConfig('mainnet'); @@ -141,5 +141,5 @@ describe('Data NFT test', () => { expect(dataNft.overrideDataMarshal).toBe('overrideUrl'); expect(dataNft.overrideDataMarshalChainId).toBe('D'); - }); + }, 20000); }); diff --git a/tests/nftminter.test.ts b/tests/nftminter.test.ts index a2041fe..c4d3463 100644 --- a/tests/nftminter.test.ts +++ b/tests/nftminter.test.ts @@ -88,7 +88,7 @@ describe('Nft minter test', () => { } ); expect(mintTx).toBeInstanceOf(Transaction); - }, 40000); + }, 200000); test('#mint nft using tax for minting', async () => { const factoryGeneratedContract = new Address(