import { EventFilter , utils , providers , BigNumber , ContractTransaction , Contract , constants } from 'ethers' ; import { Certificate , IClaimData , ICertificate , IData } from './Certificate' ; import { getEventsFromContract , IBlockchainEvent } from '../utils/events' ; import { IBlockchainProperties } from './BlockchainProperties' ; export interface IShareInCertificate { [ address ] : string ; } export const encodeClaimData = ( claimData ) : string => { const { beneficiary , location , countryCode , periodStartDate , periodEndDate , purpose } = claimData ; return utils . defaultAbiCoder . encode ( [ 'string' , 'string' , 'string' , 'string' , 'string' , 'string' ] , [ beneficiary , location , countryCode , periodStartDate , periodEndDate , purpose ] ) ; } ; export const decodeClaimData = ( encodedClaimData ) : IClaimData => { const [ beneficiary , location , countryCode , periodStartDate , periodEndDate , purpose ] = utils . defaultAbiCoder . decode ( [ 'string' , 'string' , 'string' , 'string' , 'string' , 'string' ] , encodedClaimData ) ; return { beneficiary , location , countryCode , periodStartDate , periodEndDate , purpose } ; } ; export const encodeData = ( data ) : string => { const { generationStartTime , generationEndTime , deviceId , metadata } = data ; return utils . defaultAbiCoder . encode ( [ 'uint256' , 'uint256' , 'string' , 'string' ] , [ generationStartTime , generationEndTime , deviceId , metadata ?? '' ] ) ; } ; export const decodeData = ( encodedData ) : IData => { const [ generationStartTime , generationEndTime , deviceId , metadata ] = utils . defaultAbiCoder . decode ( [ 'uint256' , 'uint256' , 'string' , 'string' ] , encodedData ) ; return { generationStartTime : generationStartTime . toNumber ( ) , generationEndTime : generationEndTime . toNumber ( ) , deviceId , metadata } ; } ; export async function getAllCertificates ( blockchainProperties ) { const { registry } = blockchainProperties ; const issuanceEvents = await getEventsFromContract ( registry , registry . filters . IssuanceSingle ( null , null , null ) ) ; const certificatePromises = issuanceEvents . map ( ( event ) => new Certificate ( event . _id . toNumber ( ) , blockchainProperties ) . sync ( ) ) ; return Promise . all ( certificatePromises ) ; } export async function getAllOwnedCertificates ( blockchainProperties ) { const { registry , activeUser } = blockchainProperties ; const owner = await activeUser . getAddress ( ) ; const transfers = await getEventsFromContract ( registry , registry . filters . TransferSingle ( null , null , owner , null , null ) ) ; const certificateIds = [ ... new Set < number > ( transfers . map ( ( transfer ) => transfer . id . toNumber ( ) ) ) ] ; const balances = await registry . balanceOfBatch ( Array ( certificateIds . length ) . fill ( owner ) , certificateIds ) ; const available = certificateIds . filter ( ( id , index ) => ! balances [ index ] . isZero ( ) ) ; const certificatePromises = available . map ( ( id ) => new Certificate ( id , blockchainProperties ) . sync ( ) ) ; return Promise . all ( certificatePromises ) ; } export async function decodeEvent ( eventName , event , contract ) { const eventBlock = await contract . provider . getBlock ( event . blockHash ) ; return { name : eventName , transactionHash : event . transactionHash , blockHash : event . blockHash , timestamp : eventBlock . timestamp , ... contract . interface . decodeEventLog ( eventName , event . data , event . topics ) } ; } export const getAllCertificateEvents = async ( certId , blockchainProperties ) : Promise < IBlockchainEvent [ ] > => { const { registry } = blockchainProperties ; const getEvent = async ( filter , eventName ) => { const logs = await registry . provider . getLogs ( { ... filter , fromBlock : 0 , toBlock : 'latest' } ) ; const parsedLogs = await Promise . all ( logs . map ( async ( event ) : Promise < IBlockchainEvent > => decodeEvent ( eventName , event , registry ) ) ) ; return parsedLogs . filter ( ( event ) => event . _id ?. toNumber ( ) === certId || event . id ?. toNumber ( ) === certId ) ; } ; const issuanceSingleEvents = await getEvent ( registry . filters . IssuanceSingle ( null , null , null ) , 'IssuanceSingle' ) ; const issuanceBatchEvents = await getEvent ( registry . filters . IssuanceBatch ( null , null , null ) , 'IssuanceBatch' ) ; const transferSingleEvents = await getEvent ( registry . filters . TransferSingle ( null , null , null , null , null ) , 'TransferSingle' ) ; const transferBatchEvents = await getEvent ( registry . filters . TransferBatch ( null , null , null , null , null ) , 'TransferBatch' ) ; const transferBatchMultipleEvents = await getEvent ( registry . filters . TransferBatchMultiple ( null , null , null , null , null ) , 'TransferBatchMultiple' ) ; const claimSingleEvents = await getEvent ( registry . filters . ClaimSingle ( null , null , null , null , null , null ) , 'ClaimSingle' ) ; const claimBatchEvents = await getEvent ( registry . filters . ClaimBatch ( null , null , null , null , null , null ) , 'ClaimBatch' ) ; const claimBatchMultipleEvents = await getEvent ( registry . filters . ClaimBatchMultiple ( null , null , null , null , null , null ) , 'ClaimBatchMultiple' ) ; return [ ... issuanceSingleEvents , ... issuanceBatchEvents , ... transferSingleEvents , ... transferBatchEvents , ... transferBatchMultipleEvents , ... claimSingleEvents , ... claimBatchEvents , ... claimBatchMultipleEvents ] ; } ; export const calculateOwnership = async ( certificateId , blockchainProperties , creationBlockNumber = 0 ) : Promise < IShareInCertificate > => { const ownedShares = { } ; const { registry } = blockchainProperties ; const transferSingleEvents = ( await getEventsFromContract ( registry , registry . filters . TransferSingle ( null , null , null , null , null ) , creationBlockNumber ) ) . filter ( ( event ) => event . id . eq ( certificateId ) ) ; const transferBatchEvents = ( await getEventsFromContract ( registry , registry . filters . TransferBatch ( null , null , null , null , null ) , creationBlockNumber ) ) . filter ( ( e ) => e . ids . some ( ( id ) => id . eq ( certificateId ) ) ) ; const transferBatchMultipleEvents = ( await getEventsFromContract ( registry , registry . filters . TransferBatchMultiple ( null , null , null , null , null ) , creationBlockNumber ) ) . filter ( ( e ) => e . ids . some ( ( id ) => id . eq ( certificateId ) ) ) ; const allHistoricOwners = [ ... new Set ( [ ... [ ... transferSingleEvents , ... transferBatchEvents ] . map ( ( event ) => event . to ) , ... transferBatchMultipleEvents . map ( ( event ) => event . to ) . reduce ( ( a , b ) => a . concat ( b ) , [ ] ) ] ) ] . filter ( ( address ) => address !== constants . AddressZero ) ; const allHistoricOwnersBalances = await registry . balanceOfBatch ( allHistoricOwners , Array ( allHistoricOwners . length ) . fill ( certificateId . toString ( ) ) ) ; allHistoricOwners . forEach ( ( owner , index ) => { ownedShares [ owner ] = allHistoricOwnersBalances [ index ] . toString ( ) ; } ) ; return ownedShares ; } ; export const calculateClaims = async ( certificateId , blockchainProperties , creationBlockNumber = 0 ) : Promise < IShareInCertificate > => { const claimedShares = { } ; const { registry } = blockchainProperties ; const claimSingleEvents = ( await getEventsFromContract ( registry , registry . filters . ClaimSingle ( null , null , null , null , null , null ) , creationBlockNumber ) ) . filter ( ( event ) => event . _id . eq ( certificateId ) ) ; const claimBatchEvents = ( await getEventsFromContract ( registry , registry . filters . ClaimBatch ( null , null , null , null , null , null ) , creationBlockNumber ) ) . filter ( ( e ) => e . _ids . some ( ( id ) => id . eq ( certificateId ) ) ) ; const claimBatchMultipleEvents = ( await getEventsFromContract ( registry , registry . filters . ClaimBatchMultiple ( null , null , null , null , null , null ) , creationBlockNumber ) ) . filter ( ( e ) => e . _ids . some ( ( id ) => id . eq ( certificateId ) ) ) ; const allHistoricClaimers = [ ... new Set ( [ ... [ ... claimSingleEvents , ... claimBatchEvents ] . map ( ( event ) => event . _claimSubject ) , ... claimBatchMultipleEvents . map ( ( event ) => event . _claimSubject ) . reduce ( ( a , b ) => a . concat ( b ) , [ ] ) ] ) ] . filter ( ( address ) => address !== constants . AddressZero ) ; const allHistoricClaimersBalances = await registry . claimedBalanceOfBatch ( allHistoricClaimers , Array ( allHistoricClaimers . length ) . fill ( certificateId . toString ( ) ) ) ; allHistoricClaimers . forEach ( ( owner , index ) => { claimedShares [ owner ] = allHistoricClaimersBalances [ index ] . toString ( ) ; } ) ; return claimedShares ; } ; export async function approveOperator ( operator , blockchainProperties ) { const { activeUser , registry } = blockchainProperties ; const registryWithSigner = registry . connect ( activeUser ) ; return registryWithSigner . setApprovalForAll ( operator , true ) ; } export async function isApprovedForAll ( operator , blockchainProperties ) { const { activeUser , registry } = blockchainProperties ; const registryWithSigner = registry . connect ( activeUser ) ; const address = await activeUser . getAddress ( ) ; return registryWithSigner . isApprovedForAll ( address , operator ) ; }