Skip to content

Commit

Permalink
Merge pull request #2743 from energywebfoundation/feat/registry-extended
Browse files Browse the repository at this point in the history
feat(issuer): Add RegistryExtended contract
  • Loading branch information
josipbagaric authored Aug 13, 2021
2 parents 549add2 + b411d41 commit 4d84b6a
Show file tree
Hide file tree
Showing 37 changed files with 529 additions and 350 deletions.
9 changes: 0 additions & 9 deletions docs/traceability/contracts/Registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@ See {IERC1888-batchIssue}.
`_to` cannot be the zero address.
`_data`, `_values` and `_validityData` must have the same length.

### `batchIssueMultiple(address[] _to, bytes[] _validityData, uint256[] _topics, uint256[] _values, bytes[] _data) → uint256[] ids` (external)

Similar to {IERC1888-batchIssue}, but not a part of the ERC-1888 standard.


Allows batch issuing to an array of _to addresses.
`_to` cannot be the zero addresses.
`_to`, `_data`, `_values`, `_topics` and `_validityData` must have the same length.

### `mint(uint256 _id, address _to, uint256 _quantity)` (external)

Allows the issuer to mint more fungible tokens for existing ERC-188 certificates.
Expand Down
49 changes: 49 additions & 0 deletions docs/traceability/contracts/RegistryExtended.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## `RegistryExtended`






### `constructor(string _uri)` (public)





### `batchIssueMultiple(address[] _to, bytes[] _validityData, uint256[] _topics, uint256[] _values, bytes[] _data) → uint256[] ids` (external)

Similar to {IERC1888-batchIssue}, but not a part of the ERC-1888 standard.


Allows batch issuing to an array of _to addresses.
`_to` cannot be the zero addresses.
`_to`, `_data`, `_values`, `_topics` and `_validityData` must have the same length.

### `safeBatchTransferFromMultiple(address[] _from, address[] _to, uint256[] _ids, uint256[] _values, bytes[] _data)` (external)

Similar to {ERC1155-safeBatchTransferFrom}, but not a part of the ERC-1155 standard.


Allows batch transferring to/from an array of addresses.

### `safeBatchTransferAndClaimFromMultiple(address[] _from, address[] _to, uint256[] _ids, uint256[] _values, bytes[] _data, bytes[] _claimData)` (external)

Similar to {IERC1888-safeBatchTransferAndClaimFrom}, but not a part of the ERC-1888 standard.


Allows batch claiming to/from an array of addresses.


### `TransferBatchMultiple(address operator, address[] from, address[] to, uint256[] ids, uint256[] values)`





### `ClaimBatchMultiple(address[] _claimIssuer, address[] _claimSubject, uint256[] _topics, uint256[] _ids, uint256[] _values, bytes[] _claimData)`





Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class BlockchainProperties extends ExtendedBaseEntity {

return {
web3,
registry: Contracts.factories.RegistryFactory.connect(this.registry, signer),
registry: Contracts.factories.RegistryExtendedFactory.connect(this.registry, signer),
issuer: Contracts.factories.IssuerFactory.connect(this.issuer, signer),
privateIssuer: this.privateIssuer
? Contracts.factories.PrivateIssuerFactory.connect(this.privateIssuer, signer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ import {
import { AuthGuard } from '@nestjs/passport';
import { CommandBus } from '@nestjs/cqrs';
import { Role } from '@energyweb/origin-backend-core';

import { BigNumber } from 'ethers';
import { ApiBearerAuth, ApiBody, ApiResponse, ApiTags } from '@nestjs/swagger';

import { BatchClaimCertificatesCommand } from './commands/batch-claim-certificates.command';
import { BatchClaimCertificatesDTO } from './commands/batch-claim-certificates.dto';
import { SuccessResponseDTO } from '../../utils/success-response.dto';
import { BatchIssueCertificateDTO } from './commands/batch-issue-certificates.dto';
import { BatchIssueCertificatesCommand } from './commands/batch-issue-certificates.command';
import { BatchTransferCertificatesDTO } from './commands/batch-transfer-certificates.dto';
import { BatchTransferCertificatesCommand } from './commands/batch-transfer-certificates.command';
import { CertificateIdsDTO } from './dto/certificate-ids.dto';
import { BatchCertificateTransferDTO } from './dto/batch-certificate-transfer.dto';
import { BatchCertificateClaimDTO } from './dto/batch-certificate-claim.dto';

@ApiTags('certificates')
@ApiBearerAuth('access-token')
Expand All @@ -56,35 +57,48 @@ export class CertificateBatchController {

@Put('transfer')
@UseGuards(AuthGuard(), ActiveUserGuard, BlockchainAccountGuard)
@ApiBody({ type: BatchTransferCertificatesDTO })
@ApiBody({ type: [BatchCertificateTransferDTO] })
@ApiResponse({
status: HttpStatus.OK,
type: SuccessResponseDTO,
description: 'Returns whether the batch claim succeeded'
})
public async batchTransfer(
@BlockchainAccountDecorator() blockchainAddress: string,
@Body() { certificateAmounts, to }: BatchTransferCertificatesDTO
@Body() transfers: [BatchCertificateTransferDTO]
): Promise<SuccessResponseDTO> {
return this.commandBus.execute(
new BatchTransferCertificatesCommand(certificateAmounts, to, blockchainAddress)
new BatchTransferCertificatesCommand(
transfers.map((transfer) => ({
...transfer,
amount: transfer.amount ? BigNumber.from(transfer.amount) : undefined,
from: transfer.from ?? blockchainAddress
}))
)
);
}

@Put('claim')
@UseGuards(AuthGuard(), ActiveUserGuard, BlockchainAccountGuard)
@ApiBody({ type: BatchClaimCertificatesDTO })
@ApiBody({ type: [BatchCertificateClaimDTO] })
@ApiResponse({
status: HttpStatus.OK,
type: SuccessResponseDTO,
description: 'Returns whether the batch claim succeeded'
})
public async batchClaim(
@BlockchainAccountDecorator() blockchainAddress: string,
@Body() { certificateAmounts, claimData }: BatchClaimCertificatesDTO
@Body() claims: BatchCertificateClaimDTO[]
): Promise<SuccessResponseDTO> {
return this.commandBus.execute(
new BatchClaimCertificatesCommand(certificateAmounts, claimData, blockchainAddress)
new BatchClaimCertificatesCommand(
claims.map((claim) => ({
...claim,
amount: claim.amount ? BigNumber.from(claim.amount) : undefined,
from: claim.from ?? blockchainAddress,
to: claim.to ?? blockchainAddress
}))
)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { IClaimData } from '@energyweb/issuer';
import { CertificateAmount } from '../types';
import { CertificateBatchOperations } from '@energyweb/issuer';

export class BatchClaimCertificatesCommand {
constructor(
public readonly certificateAmounts: CertificateAmount[],
public readonly claimData: IClaimData,
public readonly forAddress: string
) {}
constructor(public readonly claims: CertificateBatchOperations.BatchCertificateClaim[]) {}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { CertificateAmount } from '../types';
import { CertificateBatchOperations } from '@energyweb/issuer';

export class BatchTransferCertificatesCommand {
constructor(
public readonly certificateAmounts: CertificateAmount[],
public readonly to: string,
public readonly forAddress: string
) {}
constructor(public readonly transfers: CertificateBatchOperations.BatchCertificateTransfer[]) {}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
export * from './batch-claim-certificates.command';
export * from './batch-claim-certificates.dto';
export * from './batch-issue-certificates.command';
export * from './batch-issue-certificates.dto';
export * from './batch-transfer-certificates.command';
export * from './batch-transfer-certificates.dto';
export * from './claim-certificate.command';
export * from './claim-certificate.dto';
export * from './issue-certificate.command';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger';
import { IsObject, IsEthereumAddress } from 'class-validator';

import { CertificateBatchOperations, IClaimData } from '@energyweb/issuer';
import { BatchCertificateTransferDTO } from './batch-certificate-transfer.dto';

export class BatchCertificateClaimDTO
extends OmitType(BatchCertificateTransferDTO, ['to'] as const)
implements Omit<CertificateBatchOperations.BatchCertificateClaim, 'amount'>
{
@ApiProperty({ type: Object })
@IsObject()
claimData: IClaimData;

@ApiPropertyOptional({ type: String })
@IsEthereumAddress()
to?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger';
import { Validate, IsOptional, IsEthereumAddress } from 'class-validator';

import { IntUnitsOfEnergy } from '@energyweb/origin-backend-utils';
import { CertificateBatchOperations } from '@energyweb/issuer';

import { CertificateDTO } from './certificate.dto';

export class BatchCertificateTransferDTO
extends PickType(CertificateDTO, ['id'] as const)
implements Omit<CertificateBatchOperations.BatchCertificateTransfer, 'amount'>
{
@ApiProperty({ type: String })
@IsEthereumAddress()
to: string;

@ApiPropertyOptional({ type: String })
@IsOptional()
@IsEthereumAddress()
from?: string;

@ApiPropertyOptional({ type: String })
@IsOptional()
@Validate(IntUnitsOfEnergy)
amount?: string;
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ export * from './certificate.dto';
export * from './claim.dto';
export * from './energy.dto';
export * from './certificate-ids.dto';
export * from './certificate-amount.dto';
export * from './batch-certificate-transfer.dto';
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { HttpStatus } from '@nestjs/common';
import { BatchClaimCertificatesCommand } from '../commands/batch-claim-certificates.command';
import { Certificate } from '../certificate.entity';
import { BlockchainPropertiesService } from '../../blockchain/blockchain-properties.service';
import { TOTAL_AMOUNT } from '../types';

@CommandHandler(BatchClaimCertificatesCommand)
export class BatchClaimCertificatesHandler
Expand All @@ -20,59 +19,43 @@ export class BatchClaimCertificatesHandler
private readonly blockchainPropertiesService: BlockchainPropertiesService
) {}

async execute({
certificateAmounts,
claimData,
forAddress
}: BatchClaimCertificatesCommand): Promise<ISuccessResponse> {
async execute({ claims }: BatchClaimCertificatesCommand): Promise<ISuccessResponse> {
const blockchainProperties = await this.blockchainPropertiesService.get();

const certificatesToClaim = await this.repository.findByIds(
certificateAmounts.map((cert) => cert.id),
{ relations: ['blockchain'] }
);
for (const { id, amount, from } of claims) {
if (!amount) {
continue;
}

const notOwnedCertificates = certificatesToClaim
.filter(
(cert) =>
!cert.owners[forAddress] ||
BigNumber.from(cert.owners[forAddress] ?? 0).isZero()
)
.map((cert) => cert.id);
const cert = await this.repository.findOne(id);

if (notOwnedCertificates.length > 0) {
return ResponseFailure(
`Requested claiming of certificates, but you do not own certificates with IDs: ${notOwnedCertificates.join(
', '
)}`,
HttpStatus.FORBIDDEN
);
if (
!cert.owners[from] ||
BigNumber.from(cert.owners[from] ?? 0).isZero() ||
BigNumber.from(cert.owners[from] ?? 0).lt(amount)
) {
return ResponseFailure(
`Requested claiming of certificate ${id} with amount ${amount.toString()}, but you only own ${
cert.owners[from] ?? 0
}`,
HttpStatus.FORBIDDEN
);
}
}

const amounts = certificateAmounts.map((cert) =>
BigNumber.from(
cert.amount === TOTAL_AMOUNT
? certificatesToClaim.find((c) => c.id === cert.id).owners[forAddress]
: cert.amount
)
);

try {
const batchClaimTx = await CertificateBatchOperations.claimCertificates(
certificateAmounts.map((cert) => cert.id),
claimData,
blockchainProperties.wrap(),
forAddress,
amounts
claims,
blockchainProperties.wrap()
);

const receipt = await batchClaimTx.wait();

if (receipt.status === 0) {
throw new Error(
`ClaimBatch tx ${
receipt.transactionHash
} on certificate with id ${certificatesToClaim.map((cert) => cert.id)} failed.`
`ClaimBatch tx ${receipt.transactionHash} on certificate with id ${claims.map(
(claim) => claim.id
)} failed.`
);
}
} catch (error) {
Expand Down
Loading

0 comments on commit 4d84b6a

Please sign in to comment.