Skip to content

Commit

Permalink
Merge pull request #610 from nevermined-io/feat/subs_attr
Browse files Browse the repository at this point in the history
Adding support to flexible credit attributes
  • Loading branch information
aaitor authored Nov 9, 2023
2 parents 067ae6a + 31aec9c commit 0283db5
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 49 deletions.
8 changes: 4 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1355,7 +1355,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

> 2 May 2022
- add aave service agreement to ddo when creating nft721 asset [`#248`](https://github.com/nevermined-io/sdk-js/pull/248)
- add aave service agreement to ddo when creating nft721 asset [`#248`](https://github.com/nevermined-io/sdk-js/pull/248)
- Adding v0.19.21 Changelog updates [`c93cdc5`](https://github.com/nevermined-io/sdk-js/commit/c93cdc55f139a43db4130ccb0f80924d2645a931)

#### [v0.19.21](https://github.com/nevermined-io/sdk-js/compare/v0.19.20...v0.19.21)
Expand Down Expand Up @@ -1618,7 +1618,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

> 18 January 2022
- Removing not used parameter [`#186`](https://github.com/nevermined-io/sdk-js/pull/186)
- Removing not used parameter [`#186`](https://github.com/nevermined-io/sdk-js/pull/186)
- [wip] Create agreement+pay in one transaction [`#183`](https://github.com/nevermined-io/sdk-js/pull/183)
- Adapting to contracts `v1.3.3` [`#177`](https://github.com/nevermined-io/sdk-js/pull/177)
- Adding v0.17.2 Changelog updates [`5eddda4`](https://github.com/nevermined-io/sdk-js/commit/5eddda43954e013e6e6f7344c9a877d801aacb5c)
Expand Down Expand Up @@ -2124,7 +2124,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Feature/sync develop [`#268`](https://github.com/nevermined-io/sdk-js/pull/268)
- Quick fix for non-eip1559 networks [`#266`](https://github.com/nevermined-io/sdk-js/pull/266)
- integrate Permissions and refactor search query [`#264`](https://github.com/nevermined-io/sdk-js/pull/264)
- add aave service agreement to ddo when creating nft721 asset [`#248`](https://github.com/nevermined-io/sdk-js/pull/248)
- add aave service agreement to ddo when creating nft721 asset [`#248`](https://github.com/nevermined-io/sdk-js/pull/248)
- Get the keeper version from the artifacts instead of package version [`#244`](https://github.com/nevermined-io/sdk-js/pull/244)
- replace `metadata-api` url by `marketplace-api` and sort type [`#243`](https://github.com/nevermined-io/sdk-js/pull/243)
- fixing issues with BigNumbers [`#246`](https://github.com/nevermined-io/sdk-js/pull/246)
Expand Down Expand Up @@ -2163,7 +2163,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Feature/190 add did to sec markets [`#191`](https://github.com/nevermined-io/sdk-js/pull/191)
- Adding utility methods for getting nft token uri [`#189`](https://github.com/nevermined-io/sdk-js/pull/189)
- Lint ... [`#187`](https://github.com/nevermined-io/sdk-js/pull/187)
- Removing not used parameter [`#186`](https://github.com/nevermined-io/sdk-js/pull/186)
- Removing not used parameter [`#186`](https://github.com/nevermined-io/sdk-js/pull/186)
- [wip] Create agreement+pay in one transaction [`#183`](https://github.com/nevermined-io/sdk-js/pull/183)
- Adapting to contracts `v1.3.3` [`#177`](https://github.com/nevermined-io/sdk-js/pull/177)
- Correct typo in route [`#184`](https://github.com/nevermined-io/sdk-js/pull/184)
Expand Down
4 changes: 4 additions & 0 deletions integration/external/Services_NFT1155.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,8 @@ describe('Gate-keeping of Web Services using NFT ERC-1155 End-to-End', () => {
tokenId: subscriptionDDO.shortId(),
duration: 0, // Doesnt expire
amount: costServiceInCredits, // The cost of accessing the service
maxCreditsToCharge: 2n,
minCreditsToCharge: 1n,
nftTransfer,
},
},
Expand Down Expand Up @@ -537,6 +539,7 @@ describe('Gate-keeping of Web Services using NFT ERC-1155 End-to-End', () => {
it('should be able to retrieve services associated with a subscription filtering by endpoints', async () => {
const result = await nevermined.search.servicesBySubscription(
subscriptionDDO.id,
NeverminedNFT1155Type.nft1155Credit,
endpointsFilter,
)
assert.equal(result.totalResults.value, 1)
Expand All @@ -553,6 +556,7 @@ describe('Gate-keeping of Web Services using NFT ERC-1155 End-to-End', () => {
it('should not be able to retrieve any services associated with a subscription filtering by endpoints which do not exist', async () => {
const result = await nevermined.search.servicesBySubscription(
subscriptionDDO.id,
NeverminedNFT1155Type.nft1155Credit,
endpointsFilter2,
)
assert.equal(result.totalResults.value, 0)
Expand Down
1 change: 1 addition & 0 deletions integration/nevermined/DDO.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ describe('DDO Tests', () => {
'signatureValue',
'userId',
'templateId',
'nftAttributes',
] as any,
)
})
Expand Down
28 changes: 14 additions & 14 deletions integration/nevermined/NFT1155.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import chai, { assert } from 'chai'
import { decodeJwt, JWTPayload } from 'jose'
import chaiAsPromised from 'chai-as-promised'
import {
Account,
DDO,
Nevermined,
AssetPrice,
AssetAttributes,
NFTAttributes,
ContractHandler,
} from '../../src'
import { Account, DDO, Nevermined, AssetPrice, NFTAttributes, ContractHandler } from '../../src'
import { config } from '../config'
import { getMetadata } from '../utils'
import { getRoyaltyAttributes, RoyaltyKind } from '../../src/nevermined'
Expand Down Expand Up @@ -105,7 +97,7 @@ describe('NFT1155 End-to-End', () => {
it('Should be able to publish a mintable DID attached to the new NFT1155 contract', async () => {
royaltyAttributes = getRoyaltyAttributes(nevermined, RoyaltyKind.Standard, royalties)

const assetAttributes = AssetAttributes.getInstance({
const nftAttributes = NFTAttributes.getNFT1155Instance({
metadata,
services: [
{
Expand All @@ -115,12 +107,9 @@ describe('NFT1155 End-to-End', () => {
},
{
serviceType: 'nft-access',
nft: { amount: numberNFTs },
nft: { amount: numberNFTs, maxCreditsToCharge: 100n, minCreditsToCharge: 1n },
},
],
})
const nftAttributes = NFTAttributes.getNFT1155Instance({
...assetAttributes,
nftContractAddress: nftUpgradeable.address,
cap: cappedAmount,
royaltyAttributes,
Expand All @@ -131,6 +120,17 @@ describe('NFT1155 End-to-End', () => {
assert.isDefined(ddo.shortId())
})

it('NFT Attributes should be part of the DDO', async () => {
const accessService = ddo.getServicesByType('nft-access')[0]
const ddoAttributes = accessService.attributes.main.nftAttributes

console.log(JSON.stringify(ddoAttributes))

assert.equal(ddoAttributes.amount, numberNFTs)
assert.equal(ddoAttributes.maxCreditsToCharge, 100n)
assert.equal(ddoAttributes.minCreditsToCharge, 1n)
})

it('Should be able to approve permissions', async () => {
await nftUpgradeable.setApprovalForAll(someone.getId(), true, deployer)

Expand Down
10 changes: 5 additions & 5 deletions integration/nevermined/NVMAppFlows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { decodeJwt, JWTPayload } from 'jose'
import { Account, DDO, MetaData, Nevermined, AssetPrice, NFTAttributes } from '../../src'
import { Token, TransferNFTCondition } from '../../src/keeper'
import { config } from '../config'
import { getMetadata } from '../utils'
import { generateSubscriptionMetadata, getMetadata } from '../utils'
import TestContractHandler from '../../test/keeper/TestContractHandler'
import {
getRoyaltyAttributes,
Expand Down Expand Up @@ -88,9 +88,8 @@ describe('NVM App main flows using Credit NFTs (ERC-1155)', () => {
payload = decodeJwt(config.marketplaceAuthToken)

datasetMetadata = getMetadata()
timeSubscriptionMetadata = getMetadata(undefined, 'NVM App Time only Subscription')
subscriptionMetadata = getMetadata(undefined, 'NVM App Credits Subscription')
subscriptionMetadata.main.type = 'subscription'
timeSubscriptionMetadata = generateSubscriptionMetadata('NVM App Time only Subscription')
subscriptionMetadata = generateSubscriptionMetadata('NVM App Credits Subscription')

datasetMetadata.userId = payload.sub
neverminedNodeAddress = await nevermined.services.node.getProviderAddress()
Expand Down Expand Up @@ -394,10 +393,11 @@ describe('NVM App main flows using Credit NFTs (ERC-1155)', () => {
serviceType: 'nft-access',
nft: {
tokenId: creditSubscriptionDDO.shortId(),
// TODO: Review
duration: subscriptionSilverDuration,
amount: accessCostInCreditsDataset,
nftTransfer,
maxCreditsToCharge: 100n,
minCreditsToCharge: 1n,
},
},
],
Expand Down
48 changes: 47 additions & 1 deletion integration/utils/ddo-metadata-generator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { MetaData, AssetPrice, ResourceAuthentication } from '../../src'
import {
MetaData,
AssetPrice,
ResourceAuthentication,
PaymentAttributes,
DefaultPaymentAttributes,
} from '../../src'

const metadata: Partial<MetaData> = {
main: {
Expand Down Expand Up @@ -84,6 +90,25 @@ const webServiceMetadata: Partial<MetaData> = {
},
}

const subscriptionMetadata: Partial<MetaData> = {
main: {
name: undefined,
type: 'subscription',
dateCreated: '2022-10-10T17:00:00Z',
datePublished: '2022-10-10T17:00:00Z',
author: 'Nevermined',
license: 'CC-BY',
files: [],
paymentAttributes: [
{
paymentType: 'serviceAgreements',
paymentEnabled: true,
},
],
},
additionalInformation: {},
}

export const generateMetadata = (
name: string,
nonce: string | number = Math.random(),
Expand Down Expand Up @@ -149,6 +174,27 @@ export const generateWebServiceMetadata = (
return serviceMetadata
}

export const generateSubscriptionMetadata = (
name: string,
paymentAttributes: PaymentAttributes[] = DefaultPaymentAttributes,
nonce: string | number = Math.random(),
): MetaData => {
const _metadata = {
...subscriptionMetadata,
main: {
...subscriptionMetadata.main,
name,
paymentAttributes,
...({ nonce } as any),
},
additionalInformation: {
...subscriptionMetadata.additionalInformation,
},
}

return _metadata
}

export const getMetadata = (nonce: string | number = Math.random(), name = 'TestAsset'): MetaData =>
generateMetadata(name, nonce) as MetaData

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nevermined-io/sdk",
"version": "2.0.2",
"version": "2.0.3",
"description": "Javascript SDK for connecting with Nevermined Data Platform ",
"main": "./dist/node/sdk.js",
"typings": "./dist/node/sdk.d.ts",
Expand Down
19 changes: 19 additions & 0 deletions src/ddo/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ export interface Workflow {
stages: Stage[]
}

export interface PaymentAttributes {
paymentType: 'serviceAgreements' | 'stripe'
paymentEnabled: boolean
additionalAttributes?: {
[key: string]: string
}
}

export const DefaultPaymentAttributes: PaymentAttributes[] = [
{
paymentType: 'serviceAgreements',
paymentEnabled: true,
},
]

export interface Algorithm {
language: string
format?: string
Expand Down Expand Up @@ -283,6 +298,8 @@ export interface MetaDataMain {
nftType?: NeverminedNFTType

isDTP?: boolean

paymentAttributes?: PaymentAttributes[]
}

/**
Expand Down Expand Up @@ -682,6 +699,7 @@ export interface ServiceNFTAccess extends ServiceCommon {
name: string
datePublished: string
timeout: number
nftAttributes?: NFTServiceAttributes
}
serviceAgreementTemplate?: ServiceAgreementTemplate
additionalInformation: {
Expand All @@ -702,6 +720,7 @@ export interface ServiceNFTSales extends ServiceCommon, PricedMetadataInformatio
datePublished: string
price: string
timeout: number
nftAttributes?: NFTServiceAttributes
}
serviceAgreementTemplate?: ServiceAgreementTemplate
additionalInformation: {
Expand Down
12 changes: 12 additions & 0 deletions src/errors/NFTError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ export class NFTError extends Error {
super(`NFT error: ${message}`)
}
}

export class DynamicCreditsOverLimit extends NFTError {
constructor(message: string) {
super(`DynamicCreditsOverLimit error: ${message}`)
}
}

export class DynamicCreditsUnderLimit extends NFTError {
constructor(message: string) {
super(`DynamicCreditsUnderLimit error: ${message}`)
}
}
1 change: 1 addition & 0 deletions src/keeper/contracts/templates/BaseTemplate.abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export abstract class BaseTemplate<Params, S extends Service>
creator: publisher.getId(),
datePublished: metadata.main.datePublished,
name: this.name(),
...(serviceAttributes.nft && { nftAttributes: serviceAttributes.nft }),
...(priceData && priceData.attributes.main),
},
additionalInformation: {
Expand Down
29 changes: 21 additions & 8 deletions src/keeper/contracts/templates/NFTAccessTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@ import {
ValidationParams,
} from '../../../ddo'
import { InstantiableConfig } from '../../../Instantiable.abstract'
import { Account, DDO, NeverminedNFT1155Type, TxParameters } from '../../../sdk'
import {
Account,
DDO,
NFTServiceAttributes,
NeverminedNFT1155Type,
TxParameters,
} from '../../../sdk'
import { AgreementInstance, AgreementTemplate } from './AgreementTemplate.abstract'
import { BaseTemplate } from './BaseTemplate.abstract'
import { nftAccessTemplateServiceAgreementTemplate } from './NFTAccessTemplate.serviceAgreementTemplate'
import { NFTAccessCondition, NFTHolderCondition } from '../conditions'
import { DynamicCreditsUnderLimit } from '../../../errors/NFTError'

export interface NFTAccessTemplateParams {
holderAddress: string
Expand Down Expand Up @@ -156,24 +163,30 @@ export class NFTAccessTemplate extends BaseTemplate<NFTAccessTemplateParams, Ser
return false
}

const nftAccessService =
const nftAccessService = (
params.service_index && params.service_index > 0
? ddo.findServiceByIndex(params.service_index)
: ddo.findServiceByType(this.service())
) as ServiceNFTAccess

const amount = DDO.getNftAmountFromService(nftAccessService)
if (amount <= 0n) return true

const contractAddress = DDO.getNftContractAddressFromService(
nftAccessService as ServiceNFTAccess,
const amount = NFTServiceAttributes.getCreditsToCharge(
nftAccessService.attributes.main.nftAttributes,
)

const contractAddress = DDO.getNftContractAddressFromService(nftAccessService)

const tokenId = DDO.getTokenIdFromService(nftAccessService) || ddo.id

const nftContract = await this.nevermined.contracts.loadNft1155(contractAddress)

await nftContract.burnFromHolder(params.consumer_address, tokenId, amount, from, txparams)
const balance = await nftContract.balance(tokenId, params.consumer_address)
if (balance < amount) {
throw new DynamicCreditsUnderLimit(
`Balance is under the number of required credits to be burned: ${balance} < ${amount}`,
)
}

await nftContract.burnFromHolder(params.consumer_address, tokenId, amount, from, txparams)
return true
}
}
Loading

0 comments on commit 0283db5

Please sign in to comment.