diff --git a/src/ddo/Contract.ts b/src/ddo/Contract.ts new file mode 100644 index 0000000..0396a1a --- /dev/null +++ b/src/ddo/Contract.ts @@ -0,0 +1,7 @@ +import Event from "./Event" + +export default class Contract { + public contractName: string + public fulfillmentOperator: number + public events: Event[] +} diff --git a/src/ddo/Service.ts b/src/ddo/Service.ts index 7e910b5..4891d39 100644 --- a/src/ddo/Service.ts +++ b/src/ddo/Service.ts @@ -1,4 +1,5 @@ import Condition from "./Condition" +import Contract from "./Contract" import MetaData from "./MetaData" export default class Service { @@ -9,5 +10,6 @@ export default class Service { public purchaseEndpoint?: string public description?: string = "My public social inbox" public metadata?: MetaData = {} as MetaData + public serviceAgreementContract?: Contract public conditions?: Condition[] = [] } diff --git a/src/examples/ExecuteAgreement.ts b/src/examples/ExecuteAgreement.ts index f9d68c8..dbbc508 100644 --- a/src/examples/ExecuteAgreement.ts +++ b/src/examples/ExecuteAgreement.ts @@ -54,6 +54,8 @@ import {Account, Logger, Ocean, ServiceAgreement} from "../squid" const accessService = ddo.findServiceByType("Access") + await consumer.requestTokens(metaData.base.price) + const serviceAgreementSignatureResult: any = await ocean.signServiceAgreement(ddo.id, accessService.serviceDefinitionId, consumer) Logger.log("ServiceAgreement Id:", serviceAgreementSignatureResult.serviceAgreementId) diff --git a/src/keeper/contracts/ServiceAgreement.ts b/src/keeper/contracts/ServiceAgreement.ts index db09194..ca2d4c7 100644 --- a/src/keeper/contracts/ServiceAgreement.ts +++ b/src/keeper/contracts/ServiceAgreement.ts @@ -47,7 +47,7 @@ export default class ServiceAgreement extends ContractBase { return this.send("executeAgreement", publisherAddress, [ serviceAgreementTemplateId, serviceAgreementSignatureHash, consumerAddress, valueHashes, - timeoutValues, "0x" + serviceAgreementId, "0x" + did.replace("did:op:", ""), + timeoutValues, serviceAgreementId, "0x" + did.replace("did:op:", ""), ]) } } diff --git a/src/ocean/Ocean.ts b/src/ocean/Ocean.ts index 2cb7e98..9df0849 100644 --- a/src/ocean/Ocean.ts +++ b/src/ocean/Ocean.ts @@ -4,9 +4,13 @@ import BrizoProvider from "../brizo/BrizoProvider" import ConfigProvider from "../ConfigProvider" import Authentication from "../ddo/Authentication" import Condition from "../ddo/Condition" +import Contract from "../ddo/Contract" import DDO from "../ddo/DDO" +import Event from "../ddo/Event" +import EventHandlers from "../ddo/EventHandlers" import MetaData from "../ddo/MetaData" import Service from "../ddo/Service" +import ContractEvent from "../keeper/Event" import Keeper from "../keeper/Keeper" import Web3Provider from "../keeper/Web3Provider" import Config from "../models/Config" @@ -19,6 +23,8 @@ import ServiceAgreement from "./ServiceAgreements/ServiceAgreement" import ServiceAgreementTemplate from "./ServiceAgreements/ServiceAgreementTemplate" import Access from "./ServiceAgreements/Templates/Access" +import EventListener from "../keeper/EventListener" + export default class Ocean { public static async getInstance(config: Config) { @@ -105,6 +111,21 @@ export default class Ocean { serviceDefinitionId: accessServiceDefinitionId, // the id of the service agreement template templateId: serviceAgreementTemplate.getId(), + serviceAgreementContract: { + contractName: "ServiceAgreement", + fulfillmentOperator: template.fulfillmentOperator, + events: [ + { + name: "ExecuteAgreement", + actorType: ["consumer"], + handlers: { + moduleName: "payment", + functionName: "lockPayment", + version: "0.1", + } as EventHandlers, + } as Event, + ], + } as Contract, conditions, } as Service, { @@ -142,11 +163,35 @@ export default class Ocean { const ddo = await AquariusProvider.getAquarius().retrieveDDO(did) const id = did.replace("did:op:", "") - const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generatePrefixedId() try { const serviceAgreementSignature: string = await ServiceAgreement.signServiceAgreement(id, ddo, serviceDefinitionId, serviceAgreementId, consumer) + + const accessService: Service = ddo.findServiceByType("Access") + const metadataService: Service = ddo.findServiceByType("Metadata") + + const event: ContractEvent = EventListener.subscribe(accessService.serviceAgreementContract.contractName, + accessService.serviceAgreementContract.events[0].name, { + serviceAgreementId, + }) + + const price = metadataService.metadata.base.price + const balance = await consumer.getOceanBalance() + if (balance < price) { + throw new Error(`Not enough ocean tokens! Should have ${price} but has ${balance}`) + } + + event.listenOnce((data) => { + + new ServiceAgreement(serviceAgreementId) + .buyAsset(id, + metadataService.metadata.base.price, + consumer, + ) + }) + return { serviceAgreementId, serviceAgreementSignature, diff --git a/src/ocean/ServiceAgreements/ServiceAgreement.ts b/src/ocean/ServiceAgreements/ServiceAgreement.ts index dba4bc6..6c8faa5 100644 --- a/src/ocean/ServiceAgreements/ServiceAgreement.ts +++ b/src/ocean/ServiceAgreements/ServiceAgreement.ts @@ -117,11 +117,6 @@ export default class ServiceAgreement extends OceanBase { return new ServiceAgreement( executeAgreementReceipt.events.ExecuteAgreement.returnValues.serviceAgreementId, - ddo, - publisher, - new Account(consumerAddress), - executeAgreementReceipt.events.ExecuteAgreement.returnValues.state, - executeAgreementReceipt.events.ExecuteAgreement.returnValues.status, ) } @@ -186,7 +181,7 @@ export default class ServiceAgreement extends OceanBase { contionValues.push({ type: parameter.type, - value: parameter.name === "serviceId" ? "0x" + serviceAgreementId : parameter.value, + value: parameter.name === "serviceId" ? serviceAgreementId : parameter.value, } as ValuePair) }) @@ -196,12 +191,7 @@ export default class ServiceAgreement extends OceanBase { return values } - private constructor(serviceAgreementId: string, - ddo: DDO, - private publisher: Account, - consumer: Account, - state: boolean, - status: boolean) { + constructor(serviceAgreementId: string) { super(serviceAgreementId) } @@ -218,12 +208,11 @@ export default class ServiceAgreement extends OceanBase { return lockPaymentReceipt.status } - public async grantAccess(assetId: string, documentId: string): Promise { + public async grantAccess(assetId: string, documentId: string, publisher: Account): Promise { const {accessConditions} = await Keeper.getInstance() const grantAccessReceipt = - await accessConditions.grantAccess(this.getId(), assetId, documentId, - this.publisher.getId()) + await accessConditions.grantAccess(this.getId(), assetId, documentId, publisher.getId()) return !!grantAccessReceipt.events.AccessGranted } diff --git a/test/ocean/ServiceAgreement.test.ts b/test/ocean/ServiceAgreement.test.ts index e4e5fb9..1472dba 100644 --- a/test/ocean/ServiceAgreement.test.ts +++ b/test/ocean/ServiceAgreement.test.ts @@ -60,7 +60,7 @@ describe("ServiceAgreement", () => { const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) - const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generatePrefixedId() // @ts-ignore WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) @@ -79,7 +79,7 @@ describe("ServiceAgreement", () => { const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) - const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generatePrefixedId() // @ts-ignore WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) @@ -103,7 +103,7 @@ describe("ServiceAgreement", () => { const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) - const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generatePrefixedId() // @ts-ignore WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) @@ -127,7 +127,7 @@ describe("ServiceAgreement", () => { const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService, metaDataService]}) - const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generatePrefixedId() // @ts-ignore WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) @@ -156,7 +156,7 @@ describe("ServiceAgreement", () => { const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) - const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generatePrefixedId() // @ts-ignore WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) @@ -178,7 +178,7 @@ describe("ServiceAgreement", () => { assert(paid) // todo: use document id - const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, assetId) + const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, assetId, publisherAccount) assert(accessGranted) }) @@ -186,7 +186,7 @@ describe("ServiceAgreement", () => { const did: string = `did:op:${assetId}` const ddo = new DDO({id: did, service: [accessService]}) - const serviceAgreementId: string = IdGenerator.generateId() + const serviceAgreementId: string = IdGenerator.generatePrefixedId() // @ts-ignore WebServiceConnectorProvider.setConnector(new WebServiceConnectorMock(ddo)) @@ -201,7 +201,7 @@ describe("ServiceAgreement", () => { assert(serviceAgreement) // todo: use document id - const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, assetId) + const accessGranted: boolean = await serviceAgreement.grantAccess(assetId, assetId, publisherAccount) assert(!accessGranted) }) })