Skip to content

Commit

Permalink
Wallet address field in payment events (#749)
Browse files Browse the repository at this point in the history
feat(events): added provider info (including wallet address) to the event details

BREAKING CHANGE: Replacing the providerId and providerName event fields
with an object of the ProviderInfo type (with walletAddress)
In Invoice/Debit Note Received events, the amount field has been changed
from string to number
  • Loading branch information
mgordel authored Jan 8, 2024
1 parent 9137662 commit d71a588
Show file tree
Hide file tree
Showing 23 changed files with 380 additions and 214 deletions.
17 changes: 6 additions & 11 deletions src/agreement/agreement.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { Logger } from "../utils";
import { Logger, YagnaApi } from "../utils";
import { Agreement as AgreementModel } from "ya-ts-client/dist/ya-market/src/models";
import { YagnaOptions } from "../executor";
import { AgreementFactory } from "./factory";
import { AgreementConfig } from "./config";
import { Events } from "../events";
import { YagnaApi } from "../utils/yagna/yagna";
import { GolemError } from "../error/golem-error";

/**
* @hidden
*/
export interface ProviderInfo {
name: string;
id: string;
walletAddress: string;
}

/**
Expand Down Expand Up @@ -69,7 +66,7 @@ export class Agreement {

/**
* Create agreement for given proposal ID
* @param proposalId - proposal ID
* @param proposal
* @param yagnaApi
* @param agreementOptions - {@link AgreementOptions}
* @return Agreement
Expand Down Expand Up @@ -110,13 +107,11 @@ export class Agreement {
await this.yagnaApi.market.confirmAgreement(this.id, appSessionId);
await this.yagnaApi.market.waitForApproval(this.id, this.options.agreementWaitingForApprovalTimeout);
this.logger?.debug(`Agreement ${this.id} approved`);
this.options.eventTarget?.dispatchEvent(
new Events.AgreementConfirmed({ id: this.id, providerId: this.provider.id }),
);
this.options.eventTarget?.dispatchEvent(new Events.AgreementConfirmed({ id: this.id, provider: this.provider }));
} catch (error) {
this.logger?.debug(`Unable to confirm agreement with provider ${this.provider.name}. ${error}`);
this.options.eventTarget?.dispatchEvent(
new Events.AgreementRejected({ id: this.id, providerId: this.provider.id, reason: error.toString() }),
new Events.AgreementRejected({ id: this.id, provider: this.provider, reason: error.toString() }),
);
throw error;
}
Expand Down Expand Up @@ -146,7 +141,7 @@ export class Agreement {
timeout: this.options.agreementRequestTimeout,
});
this.options.eventTarget?.dispatchEvent(
new Events.AgreementTerminated({ id: this.id, providerId: this.provider.id, reason: reason.message }),
new Events.AgreementTerminated({ id: this.id, provider: this.provider, reason: reason.message }),
);
this.logger?.debug(`Agreement ${this.id} terminated`);
} catch (error) {
Expand Down
13 changes: 8 additions & 5 deletions src/agreement/factory.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Agreement, AgreementOptions } from "./agreement";
import { Logger } from "../utils";
import { Logger, YagnaApi } from "../utils";
import { AgreementConfig } from "./config";
import { Events } from "../events";
import { YagnaApi } from "../utils/yagna/yagna";
import { GolemError } from "../error/golem-error";
import { ProposalProperties } from "../market/proposal";

Expand Down Expand Up @@ -43,17 +42,21 @@ export class AgreementFactory {
timeout: this.options.agreementRequestTimeout,
});
const { data } = await this.yagnaApi.market.getAgreement(agreementId);
const offerProperties: ProposalProperties = data.offer.properties as ProposalProperties;
const demandProperties: ProposalProperties = data.demand.properties as ProposalProperties;
const chosenPaymentPlatform = demandProperties["golem.com.payment.chosen-platform"];
console.log({ chosenPaymentPlatform });
const provider = {
name: (data.offer.properties as ProposalProperties)["golem.node.id.name"],
name: offerProperties["golem.node.id.name"],
id: data.offer.providerId,
walletAddress: offerProperties[`golem.com.payment.platform.${chosenPaymentPlatform}.address`] as string,
};
if (!provider.id || !provider.name) throw new GolemError("Unable to get provider info");
const agreement = new Agreement(agreementId, provider, this.yagnaApi, this.options);
this.options.eventTarget?.dispatchEvent(
new Events.AgreementCreated({
id: agreementId,
providerId: provider.id,
providerName: provider.name,
provider,
validTo: data?.validTo,
proposalId,
}),
Expand Down
47 changes: 23 additions & 24 deletions src/events/events.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ProposalDetails } from "../market/proposal";
import { ProposalDetails } from "../market";
import { PackageDetails } from "../package/package";
import { DemandDetails } from "../market/demand";

import { RequireAtLeastOne } from "../utils/types";
import { ProviderInfo } from "../agreement";
/**
* Global Event Type with which all API events will be emitted. It should be used on all listeners that would like to handle events.
*/
Expand Down Expand Up @@ -45,8 +46,7 @@ export class TaskStarted extends BaseEvent<{
id: string;
agreementId: string;
activityId: string;
providerId: string;
providerName: string;
provider: ProviderInfo;
}> {}

/**
Expand All @@ -55,8 +55,7 @@ export class TaskStarted extends BaseEvent<{
export class TaskRedone extends BaseEvent<{
id: string;
agreementId: string;
providerId: string;
providerName: string;
provider: ProviderInfo;
retriesCount: number;
/**
* The activity that was involved
Expand All @@ -74,8 +73,8 @@ export class TaskRedone extends BaseEvent<{
export class TaskRejected extends BaseEvent<{
id: string;
agreementId: string;
providerId: string;
providerName: string;

provider: ProviderInfo;
/**
* The activity that was involved when the rejection took place
*
Expand All @@ -92,28 +91,28 @@ export class DemandUnsubscribed extends BaseEvent<{ id: string }> {}
export class CollectFailed extends BaseEvent<{ id: string; reason?: string }> {}
export class ProposalReceived extends BaseEvent<{
id: string;
providerId: string;
provider: ProviderInfo;
parentId: string | null;
details: ProposalDetails;
}> {}
export class ProposalRejected extends BaseEvent<{
id: string;
providerId?: string;
provider: ProviderInfo;
reason?: string;
parentId: string | null;
}> {}
export class ProposalResponded extends BaseEvent<{
id: string;
providerId: string;
provider: ProviderInfo;
counteringProposalId: string;
}> {}
export class ProposalFailed extends BaseEvent<{
id: string;
providerId: string;
provider: ProviderInfo;
parentId: string | null;
reason?: string;
}> {}
export class ProposalConfirmed extends BaseEvent<{ id: string; providerId: string }> {}
export class ProposalConfirmed extends BaseEvent<{ id: string; provider: ProviderInfo }> {}
export class PackageCreated extends BaseEvent<{
packageReference: RequireAtLeastOne<{
imageHash: string;
Expand All @@ -124,37 +123,37 @@ export class PackageCreated extends BaseEvent<{
}> {}
export class AgreementCreated extends BaseEvent<{
id: string;
providerId: string;
providerName: string;
provider: ProviderInfo;
proposalId: string;
validTo?: string;
}> {}
export class AgreementConfirmed extends BaseEvent<{ id: string; providerId: string }> {}
export class AgreementRejected extends BaseEvent<{ id: string; providerId: string; reason?: string }> {}
export class AgreementTerminated extends BaseEvent<{ id: string; providerId: string; reason?: string }> {}
export class AgreementConfirmed extends BaseEvent<{ id: string; provider: ProviderInfo }> {}
export class AgreementRejected extends BaseEvent<{ id: string; provider: ProviderInfo; reason?: string }> {}
export class AgreementTerminated extends BaseEvent<{ id: string; provider: ProviderInfo; reason?: string }> {}
export class InvoiceReceived extends BaseEvent<{
id: string;
providerId: string;
provider: ProviderInfo;
agreementId: string;
amount: string; // It is coming as a string
amount: number;
}> {}
export class DebitNoteReceived extends BaseEvent<{
id: string;
agreementId: string;
activityId: string;
amount: string; // It is coming as a string
amount: number;
provider: ProviderInfo;
}> {}
export class PaymentAccepted extends BaseEvent<{
id: string;
providerId: string;
agreementId: string;
amount: string; // It is coming as a string
amount: number;
provider: ProviderInfo;
}> {}
export class DebitNoteAccepted extends BaseEvent<{
id: string;
providerId: string;
agreementId: string;
amount: string; // It is coming as a string
amount: number;
provider: ProviderInfo;
}> {}
export class PaymentFailed extends BaseEvent<{ id: string; agreementId: string; reason?: string }> {}
export class ActivityCreated extends BaseEvent<{ id: string; agreementId: string }> {}
Expand Down
15 changes: 13 additions & 2 deletions src/market/demand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Package } from "../package";
import { Allocation } from "../payment";
import { YagnaOptions } from "../executor";
import { DemandFactory } from "./factory";
import { Proposal } from "./proposal";
import { Proposal, ProposalProperties } from "./proposal";
import { Logger, sleep, YagnaApi } from "../utils";
import { DemandConfig } from "./config";
import { Events } from "../events";
Expand Down Expand Up @@ -135,13 +135,15 @@ export class Demand extends EventTarget {
/**
* @param id - demand ID
* @param demandRequest - {@link DemandOfferBase}
* @param allocation - {@link Allocation}
* @param yagnaApi - {@link YagnaApi}
* @param options - {@link DemandConfig}
* @hidden
*/
constructor(
public readonly id: string,
private demandRequest: DemandOfferBase,
private allocation: Allocation,
private yagnaApi: YagnaApi,
private options: DemandConfig,
) {
Expand Down Expand Up @@ -188,11 +190,19 @@ export class Demand extends EventTarget {
for (const event of events as Array<ProposalEvent & ProposalRejectedEvent>) {
if (event.eventType === "ProposalRejectedEvent") {
this.logger?.debug(`Proposal rejected. Reason: ${event.reason?.message}`);
const proposalProperties = event.proposal.properties as ProposalProperties;
this.options.eventTarget?.dispatchEvent(
new Events.ProposalRejected({
id: event.proposalId,
parentId: this.findParentProposal(event.proposalId),
reason: event.reason?.message,
provider: {
id: event.proposal.issuerId,
name: proposalProperties["golem.node.id.name"],
walletAddress: proposalProperties[
`golem.com.payment.platform.${this.allocation.paymentPlatform}.address`
] as string,
},
}),
);
continue;
Expand All @@ -204,14 +214,15 @@ export class Demand extends EventTarget {
this.yagnaApi.market,
event.proposal,
this.demandRequest,
this.allocation.paymentPlatform,
this.options.eventTarget,
);
this.dispatchEvent(new DemandEvent(DEMAND_EVENT_TYPE, proposal));
this.options.eventTarget?.dispatchEvent(
new Events.ProposalReceived({
id: proposal.id,
parentId: this.findParentProposal(event.proposal.prevProposalId),
providerId: proposal.issuerId,
provider: proposal.provider,
details: proposal.details,
}),
);
Expand Down
2 changes: 1 addition & 1 deletion src/market/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class DemandFactory {
}),
);
this.options.logger?.info(`Demand published on the market`);
return new Demand(id, demandRequest, this.yagnaApi, this.options);
return new Demand(id, demandRequest, this.allocation, this.yagnaApi, this.options);
}

private async getDecorations(): Promise<MarketDecoration[]> {
Expand Down
1 change: 1 addition & 0 deletions src/market/proposal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const buildTestProposal = (props: Partial<ProposalProperties>): Proposal => {
mockApi,
model,
mockDemand,
"testPaymentPlatform",
);

return proposal;
Expand Down
23 changes: 16 additions & 7 deletions src/market/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { RequestorApi } from "ya-ts-client/dist/ya-market/api";
import { DemandOfferBase } from "ya-ts-client/dist/ya-market";
import { Events } from "../events";
import { GolemError } from "../error/golem-error";
import { ProviderInfo } from "../agreement";

export type PricingInfo = {
cpuSec: number;
Expand Down Expand Up @@ -58,7 +59,6 @@ export interface ProposalDetails {
cpuThreads: number;
memory: number;
storage: number;
providerName: string;
publicNet: boolean;
runtimeCapabilities: string[];
runtimeName: string;
Expand All @@ -71,7 +71,7 @@ export interface ProposalDetails {
export class Proposal {
id: string;
readonly issuerId: string;
readonly provider: { id: string; name: string };
readonly provider: ProviderInfo;
readonly properties: ProposalProperties;
readonly constraints: string;
readonly timestamp: string;
Expand All @@ -88,6 +88,7 @@ export class Proposal {
* @param api - {@link RequestorApi}
* @param model - {@link ProposalModel}
* @param demandRequest - {@link DemandOfferBase}
* @param paymentPlatform
* @param eventTarget - {@link EventTarget}
*/
constructor(
Expand All @@ -97,6 +98,7 @@ export class Proposal {
private readonly api: RequestorApi,
model: ProposalModel,
private readonly demandRequest: DemandOfferBase,
private readonly paymentPlatform: string,
private eventTarget?: EventTarget,
) {
this.id = model.proposalId;
Expand All @@ -107,7 +109,7 @@ export class Proposal {
this.prevProposalId = model.prevProposalId;
this.timestamp = model.timestamp;
this.counteringProposalId = null;
this.provider = { id: this.issuerId, name: this.details.providerName };
this.provider = this.getProviderInfo();

// Run validation to ensure that the Proposal is in a complete and correct state
this.validate();
Expand All @@ -122,7 +124,6 @@ export class Proposal {
cpuThreads: this.properties["golem.inf.cpu.threads"],
memory: this.properties["golem.inf.mem.gib"],
storage: this.properties["golem.inf.storage.gib"],
providerName: this.properties["golem.node.id.name"],
publicNet: this.properties["golem.node.net.is-public"],
runtimeCapabilities: this.properties["golem.runtime.capabilities"],
runtimeName: this.properties["golem.runtime.name"],
Expand Down Expand Up @@ -204,7 +205,7 @@ export class Proposal {
this.eventTarget?.dispatchEvent(
new Events.ProposalRejected({
id: this.id,
providerId: this.issuerId,
provider: this.provider,
parentId: this.id,
reason,
}),
Expand All @@ -220,7 +221,7 @@ export class Proposal {
this.eventTarget?.dispatchEvent(
new Events.ProposalFailed({
id: this.id,
providerId: this.issuerId,
provider: this.provider,
parentId: this.id,
reason,
}),
Expand All @@ -234,7 +235,7 @@ export class Proposal {
this.eventTarget?.dispatchEvent(
new Events.ProposalResponded({
id: this.id,
providerId: this.issuerId,
provider: this.provider,
counteringProposalId: counteringProposalId,
}),
);
Expand All @@ -252,4 +253,12 @@ export class Proposal {
.map((prop) => prop.split(".")[4]) || []
);
}

private getProviderInfo(): ProviderInfo {
return {
id: this.issuerId,
name: this.properties["golem.node.id.name"],
walletAddress: this.properties[`golem.com.payment.platform.${this.paymentPlatform}.address`] as string,
};
}
}
Loading

0 comments on commit d71a588

Please sign in to comment.