diff --git a/db/migrations/9_add_confidential_assets_pallet.sql b/db/migrations/9_add_confidential_assets_pallet.sql new file mode 100644 index 00000000..2d1c75cf --- /dev/null +++ b/db/migrations/9_add_confidential_assets_pallet.sql @@ -0,0 +1,65 @@ +DO $$ +BEGIN + IF NOT EXISTS (select 1 from pg_type where typname = '04c7376a01') then + create type "04c7376a01" AS ENUM ( + 'SenderAffirmed', + 'SenderReverted', + 'SenderCounterUpdated', + 'MediatorAffirmed', + 'MediatorRejected', + 'ReceiverAffirmed', + 'ReceiverClaimed' + ); + END IF; +END +$$; + +alter type "7a0b4cc03e" add value if not exists 'confidentialassets' after 'validators'; + +alter type "8f5a39c8ee" add value if not exists 'AccountAssetRegistered' after 'StorageDepositTransferredAndReleased'; +alter type "8f5a39c8ee" add value if not exists 'AccountCurveTreeRootUpdated' after 'AccountAssetRegistered'; +alter type "8f5a39c8ee" add value if not exists 'AccountRegistered' after 'AccountCurveTreeRootUpdated'; +alter type "8f5a39c8ee" add value if not exists 'AccountStateLeafInserted' after 'AccountRegistered'; +alter type "8f5a39c8ee" add value if not exists 'AssetCreated' after 'AccountStateLeafInserted'; +alter type "8f5a39c8ee" add value if not exists 'AssetCurveTreeRootUpdated' after 'AssetCreated'; +alter type "8f5a39c8ee" add value if not exists 'AssetMinted' after 'AssetCurveTreeRootUpdated'; +alter type "8f5a39c8ee" add value if not exists 'AssetStateLeafUpdated' after 'AssetMinted'; +alter type "8f5a39c8ee" add value if not exists 'AssetUpdated' after 'AssetStateLeafUpdated'; +alter type "8f5a39c8ee" add value if not exists 'EncryptionKeyRegistered' after 'AssetUpdated'; +alter type "8f5a39c8ee" add value if not exists 'FeeAccountCurveTreeRootUpdated' after 'EncryptionKeyRegistered'; +alter type "8f5a39c8ee" add value if not exists 'FeeAccountDeposited' after 'FeeAccountCurveTreeRootUpdated'; +alter type "8f5a39c8ee" add value if not exists 'FeeAccountStateLeafInserted' after 'FeeAccountDeposited'; +alter type "8f5a39c8ee" add value if not exists 'FeeAccountUpdated' after 'FeeAccountStateLeafInserted'; +alter type "8f5a39c8ee" add value if not exists 'FeeAccountWithdrawn' after 'FeeAccountUpdated'; +alter type "8f5a39c8ee" add value if not exists 'MediatorAffirmed' after 'FeeAccountWithdrawn'; +alter type "8f5a39c8ee" add value if not exists 'MediatorRejected' after 'MediatorAffirmed'; +alter type "8f5a39c8ee" add value if not exists 'ReceiverAffirmed' after 'MediatorRejected'; +alter type "8f5a39c8ee" add value if not exists 'ReceiverClaimed' after 'ReceiverAffirmed'; +alter type "8f5a39c8ee" add value if not exists 'RelayerBatchedProofs' after 'ReceiverClaimed'; +alter type "8f5a39c8ee" add value if not exists 'SenderAffirmed' after 'RelayerBatchedProofs'; +alter type "8f5a39c8ee" add value if not exists 'SenderCounterUpdated' after 'SenderAffirmed'; +alter type "8f5a39c8ee" add value if not exists 'SenderReverted' after 'SenderCounterUpdated'; +alter type "8f5a39c8ee" add value if not exists 'SettlementCreated' after 'SenderReverted'; +alter type "8f5a39c8ee" add value if not exists 'SettlementStatusUpdated' after 'SettlementCreated'; + +alter type "0bf3c7d4ef" add value if not exists 'batched_settlement' after 'ensure_updated'; +alter type "0bf3c7d4ef" add value if not exists 'create_asset' after 'batched_settlement'; +alter type "0bf3c7d4ef" add value if not exists 'create_settlement' after 'create_asset'; +alter type "0bf3c7d4ef" add value if not exists 'execute_instant_settlement' after 'create_settlement'; +alter type "0bf3c7d4ef" add value if not exists 'instant_receiver_affirmation' after 'execute_instant_settlement'; +alter type "0bf3c7d4ef" add value if not exists 'instant_sender_affirmation' after 'instant_receiver_affirmation'; +alter type "0bf3c7d4ef" add value if not exists 'mediator_affirmation' after 'instant_sender_affirmation'; +alter type "0bf3c7d4ef" add value if not exists 'mint_asset' after 'mediator_affirmation'; +alter type "0bf3c7d4ef" add value if not exists 'receiver_affirmation' after 'mint_asset'; +alter type "0bf3c7d4ef" add value if not exists 'receiver_claim' after 'receiver_affirmation'; +alter type "0bf3c7d4ef" add value if not exists 'register_account_assets' after 'receiver_claim'; +alter type "0bf3c7d4ef" add value if not exists 'register_accounts' after 'register_account_assets'; +alter type "0bf3c7d4ef" add value if not exists 'register_encryption_keys' after 'register_accounts'; +alter type "0bf3c7d4ef" add value if not exists 'register_fee_accounts' after 'register_encryption_keys'; +alter type "0bf3c7d4ef" add value if not exists 'relayer_submit_batched_proofs' after 'register_fee_accounts'; +alter type "0bf3c7d4ef" add value if not exists 'sender_affirmation' after 'relayer_submit_batched_proofs'; +alter type "0bf3c7d4ef" add value if not exists 'sender_revert' after 'sender_affirmation'; +alter type "0bf3c7d4ef" add value if not exists 'sender_update_counter' after 'sender_revert'; +alter type "0bf3c7d4ef" add value if not exists 'submit_batched_proofs' after 'sender_update_counter'; +alter type "0bf3c7d4ef" add value if not exists 'topup_fee_accounts' after 'submit_batched_proofs'; + diff --git a/project.ts b/project.ts index 84a0598c..c3199c3a 100644 --- a/project.ts +++ b/project.ts @@ -142,6 +142,33 @@ const filters = { VenuesBlocked: ['handleVenuesBlocked'], FundsMoved: ['handleConfidentialAssetMoveFunds'], }, + confidentialAssets: { + AccountAssetRegistered: ['handleDartAccountAssetRegistered'], + AccountCurveTreeRootUpdated: ['handleDartAccountCurveTreeRootUpdated'], + AccountRegistered: ['handleDartAccountRegistered'], + AccountStateLeafInserted: ['handleDartAccountStateLeafInserted'], + AssetCreated: ['handleDartAssetCreated'], + AssetCurveTreeRootUpdated: ['handleDartAssetCurveTreeRootUpdated'], + AssetMinted: ['handleDartAssetMinted'], + AssetStateLeafUpdated: ['handleDartAssetStateLeafUpdated'], + AssetUpdated: ['handleDartAssetUpdated'], + EncryptionKeyRegistered: ['handleDartEncryptionKeyRegistered'], + FeeAccountCurveTreeRootUpdated: ['handleDartFeeAccountCurveTreeRootUpdated'], + FeeAccountDeposited: ['handleDartFeeAccountDeposited'], + FeeAccountStateLeafInserted: ['handleDartFeeAccountStateLeafInserted'], + FeeAccountUpdated: ['handleDartFeeAccountUpdated'], + FeeAccountWithdrawn: ['handleDartFeeAccountWithdrawn'], + MediatorAffirmed: ['handleDartMediatorAffirmed'], + MediatorRejected: ['handleDartMediatorRejected'], + ReceiverAffirmed: ['handleDartReceiverAffirmed'], + ReceiverClaimed: ['handleDartReceiverClaimed'], + RelayerBatchedProofs: ['handleDartRelayerBatchedProofs'], + SenderAffirmed: ['handleDartSenderAffirmed'], + SenderCounterUpdated: ['handleDartSenderCounterUpdated'], + SenderReverted: ['handleDartSenderReverted'], + SettlementCreated: ['handleDartSettlementCreated'], + SettlementStatusUpdated: ['handleDartSettlementStatusUpdated'], + }, corporateAction: { CAInitiated: [], CALinkedToDoc: [], diff --git a/schema.graphql b/schema.graphql index c7010ee7..6384ba08 100644 --- a/schema.graphql +++ b/schema.graphql @@ -3033,6 +3033,284 @@ type ConfidentialAssetMovement @entity { createdEvent: Event! } +## DART confidential assets ## + +enum DartLegActionEnum { + SenderAffirmed + SenderReverted + SenderCounterUpdated + MediatorAffirmed + MediatorRejected + ReceiverAffirmed + ReceiverClaimed +} + +""" +Represents an encryption key registered on the DART confidential assets pallet +""" +type DartConfidentialEncryptionKey @entity { + id: ID! + caller: Identity! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Represents a confidential account registered on the DART confidential assets pallet +""" +type DartConfidentialAccount @entity { + id: ID! + caller: Identity! + encryptionKey: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! + assets: [DartConfidentialAccountAsset!]! @derivedFrom(field: "account") +} + +""" +Links a confidential account to an asset registration +""" +type DartConfidentialAccountAsset @entity { + id: ID! + account: DartConfidentialAccount! + assetId: Int! + caller: Identity! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Stores account curve tree root updates +""" +type DartConfidentialAccountCurveRoot @entity { + id: ID! + root: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Stores account state leaf insertions +""" +type DartConfidentialAccountStateLeaf @entity { + id: ID! + leafIndex: BigInt! + commitment: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Represents a confidential asset created on the DART confidential assets pallet +""" +type DartConfidentialAsset @entity { + id: ID! + assetId: Int! + caller: Identity! + mediators: [String!] + auditors: [String!] + name: String! + symbol: String! + decimals: Int! + data: String + totalSupply: BigInt + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! + mints: [DartConfidentialAssetMint!]! @derivedFrom(field: "asset") +} + +""" +Captures updates to mediators/auditors for a confidential asset +""" +type DartConfidentialAssetUpdate @entity { + id: ID! + asset: DartConfidentialAsset! + mediators: [String!] + auditors: [String!] + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Stores asset curve tree root updates +""" +type DartConfidentialAssetCurveRoot @entity { + id: ID! + root: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Stores asset state leaf updates +""" +type DartConfidentialAssetStateLeaf @entity { + id: ID! + leafIndex: BigInt! + leaf: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Represents a mint of a DART confidential asset +""" +type DartConfidentialAssetMint @entity { + id: ID! + asset: DartConfidentialAsset! @index(unique: false) + account: DartConfidentialAccount + caller: Identity! + amount: BigInt! + totalSupply: BigInt! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Represents a fee account registration or top-up event +""" +type DartConfidentialFeeAccount @entity { + id: ID! + account: String! + caller: Identity! + isRegistration: Boolean! + amount: BigInt! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Stores fee account curve tree root updates +""" +type DartConfidentialFeeAccountCurveRoot @entity { + id: ID! + root: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Stores fee account state leaf insertions +""" +type DartConfidentialFeeAccountStateLeaf @entity { + id: ID! + leafIndex: BigInt! + commitment: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Captures deposits into the fee account +""" +type DartConfidentialFeeAccountDeposit @entity { + id: ID! + sender: String! + amount: BigInt! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Captures withdrawals from the fee account +""" +type DartConfidentialFeeAccountWithdraw @entity { + id: ID! + receiver: String! + amount: BigInt! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Captures actions on a settlement leg +""" +type DartConfidentialLegAction @entity { + id: ID! + legRef: String! + action: DartLegActionEnum! + keyIndex: Int + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Captures relayer batch submissions +""" +type DartConfidentialRelayerBatch @entity { + id: ID! + relayer: String! + amount: BigInt! + batchHash: String! + batchResult: String + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + +""" +Represents a confidential settlement on DART +""" +type DartConfidentialSettlement @entity { + id: ID! + settlementRef: String! + memo: String + assetRootBlock: Int! + legs: [String!]! + status: String + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! + statuses: [DartConfidentialSettlementStatus!]! @derivedFrom(field: "settlement") +} + +""" +Tracks settlement status changes +""" +type DartConfidentialSettlementStatus @entity { + id: ID! + settlement: DartConfidentialSettlement! + status: String! + eventIdx: Int! + createdBlock: Block! + updatedBlock: Block! + createdEvent: Event! +} + """ Represent asset pre-approvals from an Identity. These entries will not require an affirmation when the identity is receiving them """ diff --git a/src/mappings/entities/confidentialAssets/mapDartConfidentialAssets.ts b/src/mappings/entities/confidentialAssets/mapDartConfidentialAssets.ts new file mode 100644 index 00000000..38c43afb --- /dev/null +++ b/src/mappings/entities/confidentialAssets/mapDartConfidentialAssets.ts @@ -0,0 +1,792 @@ +import { Codec } from '@polkadot/types/types'; +import { SubstrateEvent } from '@subql/types'; +import { + DartConfidentialAccount, + DartConfidentialAccountAsset, + DartConfidentialAccountCurveRoot, + DartConfidentialAccountStateLeaf, + DartConfidentialAsset, + DartConfidentialAssetCurveRoot, + DartConfidentialAssetMint, + DartConfidentialAssetStateLeaf, + DartConfidentialAssetUpdate, + DartConfidentialEncryptionKey, + DartConfidentialFeeAccount, + DartConfidentialFeeAccountCurveRoot, + DartConfidentialFeeAccountDeposit, + DartConfidentialFeeAccountStateLeaf, + DartConfidentialFeeAccountWithdraw, + DartConfidentialLegAction, + DartConfidentialRelayerBatch, + DartConfidentialSettlement, + DartConfidentialSettlementStatus, + DartLegActionEnum, +} from '../../../types'; +import { + bytesToString, + getBigIntValue, + getErrorDetails, + getNumberValue, + getStringArrayValue, + getTextValue, +} from '../../../utils/common'; +import { extractArgs } from '../common'; + +/** + * Safely parse a Codec representing a JSON array of strings. + * Falls back to an empty array if parsing fails. + */ +const parseStringArray = (item: Codec): string[] => { + try { + return getStringArrayValue(item) || []; + } catch { + return []; + } +}; + +/** + * Parse a Codec that could be an array of bytes/strings and normalize to string[]. + * Attempts to decode bytes to string; otherwise stringifies JSON values. + * Falls back to a single-element array with string content when parsing fails. + */ +const parseBytesArray = (item: Codec): string[] => { + const json = item.toJSON(); + if (Array.isArray(json)) { + return json.map(val => { + if (typeof val === 'string') { + try { + return bytesToString(api.registry.createType('Bytes', val) as unknown as Codec) || val; + } catch { + return val; + } + } + return JSON.stringify(val); + }); + } + + try { + return parseStringArray(item); + } catch { + const text = getTextValue(item); + return text ? [text] : []; + } +}; + +/** + * Normalize a Result-like Codec into a string. + * Returns 'Ok' when Ok present; stringifies Err; otherwise returns text value. + */ +const normalizeBatchResult = (item: Codec): string => { + const json = item.toJSON(); + if (json && typeof json === 'object') { + if ('Ok' in json || 'ok' in json) { + return 'Ok'; + } + if ('Err' in json || 'err' in json) { + return JSON.stringify(json.Err ?? json.err); + } + } + return getTextValue(item); +}; + +const ensureEncryptionKey = async ( + encryptionKey: string, + callerId: string, + eventIdx: number, + blockId: string, + blockEventId: string +): Promise => { + const existing = await DartConfidentialEncryptionKey.get(encryptionKey); + if (existing) { + existing.callerId = callerId; + existing.eventIdx = eventIdx; + existing.updatedBlockId = blockId; + await existing.save(); + return; + } + + await DartConfidentialEncryptionKey.create({ + id: encryptionKey, + callerId, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +const ensureAccount = async ( + accountId: string, + callerId: string, + encryptionKey: string, + eventIdx: number, + blockId: string, + blockEventId: string +): Promise => { + const existing = await DartConfidentialAccount.get(accountId); + if (existing) { + existing.callerId = callerId; + existing.encryptionKey = encryptionKey; + existing.eventIdx = eventIdx; + existing.updatedBlockId = blockId; + await existing.save(); + return existing; + } + + const created = DartConfidentialAccount.create({ + id: accountId, + callerId, + encryptionKey, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }); + + await created.save(); + return created; +}; + +type AssetArgs = { + assetId: string; + callerId: string; + mediators: string[]; + auditors: string[]; + name: string; + symbol: string; + decimals: number; + data?: string; + eventIdx: number; + blockId: string; + blockEventId: string; +}; + +const ensureAsset = async ({ + assetId, + callerId, + mediators, + auditors, + name, + symbol, + decimals, + data, + eventIdx, + blockId, + blockEventId, +}: AssetArgs): Promise => { + const existing = await DartConfidentialAsset.get(assetId); + if (existing) { + existing.callerId = callerId; + existing.mediators = mediators; + existing.auditors = auditors; + existing.name = name; + existing.symbol = symbol; + existing.decimals = decimals; + existing.data = data; + existing.eventIdx = eventIdx; + existing.updatedBlockId = blockId; + await existing.save(); + return existing; + } + + const created = DartConfidentialAsset.create({ + id: assetId, + assetId: Number(assetId), + callerId, + mediators, + auditors, + name, + symbol, + decimals, + data, + totalSupply: BigInt(0), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }); + + await created.save(); + return created; +}; + +export const handleDartEncryptionKeyRegistered = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [rawCallerDid, rawEncryptionKey] = params; + + const callerId = getTextValue(rawCallerDid); + const encryptionKey = getTextValue(rawEncryptionKey); + + if (!encryptionKey || !callerId) { + return; + } + + await ensureEncryptionKey(encryptionKey, callerId, eventIdx, blockId, blockEventId); +}; + +export const handleDartAccountRegistered = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [rawCallerDid, rawAccount, rawEncryptionKey] = params; + + const callerId = getTextValue(rawCallerDid); + const accountId = getTextValue(rawAccount); + const encryptionKey = getTextValue(rawEncryptionKey); + + if (!callerId || !accountId || !encryptionKey) { + return; + } + + await ensureEncryptionKey(encryptionKey, callerId, eventIdx, blockId, blockEventId); + await ensureAccount(accountId, callerId, encryptionKey, eventIdx, blockId, blockEventId); +}; + +export const handleDartAccountAssetRegistered = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [rawCallerDid, rawAccount, rawAssetId] = params; + + const callerId = getTextValue(rawCallerDid); + const accountId = getTextValue(rawAccount); + const assetId = getNumberValue(rawAssetId); + + if (!callerId || !accountId) { + return; + } + + const account = await DartConfidentialAccount.get(accountId); + if (!account) { + return; + } + + const id = `${accountId}/${assetId}`; + const existing = await DartConfidentialAccountAsset.get(id); + + if (existing) { + existing.callerId = callerId; + existing.eventIdx = eventIdx; + existing.updatedBlockId = blockId; + await existing.save(); + return; + } + + await DartConfidentialAccountAsset.create({ + id, + accountId, + assetId, + callerId, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartAccountCurveTreeRootUpdated = async ( + event: SubstrateEvent +): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [root] = params; + + await DartConfidentialAccountCurveRoot.create({ + id: blockEventId, + root: getTextValue(root), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartAccountStateLeafInserted = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [leafIndex, commitment] = params; + + await DartConfidentialAccountStateLeaf.create({ + id: blockEventId, + leafIndex: getBigIntValue(leafIndex), + commitment: getTextValue(commitment), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartAssetCreated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [ + rawCallerDid, + rawAssetId, + rawMediators, + rawAuditors, + rawName, + rawSymbol, + rawDecimals, + rawData, + ] = params; + + const callerId = getTextValue(rawCallerDid); + const assetIdNumber = getNumberValue(rawAssetId); + const assetId = assetIdNumber?.toString(); + if (!callerId || assetId === undefined) { + return; + } + + const mediators = parseStringArray(rawMediators); + const auditors = parseStringArray(rawAuditors); + const name = getTextValue(rawName) || ''; + const symbol = getTextValue(rawSymbol) || ''; + const decimals = getNumberValue(rawDecimals) || 0; + const data = bytesToString(rawData); + + await ensureAsset({ + assetId, + callerId, + mediators, + auditors, + name, + symbol, + decimals, + data, + eventIdx, + blockId, + blockEventId, + }); +}; + +export const handleDartAssetUpdated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [rawCallerDid, rawAssetId, rawMediators, rawAuditors] = params; + + const callerId = getTextValue(rawCallerDid); + const assetIdNumber = getNumberValue(rawAssetId); + const assetId = assetIdNumber?.toString(); + + if (!callerId || assetId === undefined) { + return; + } + + const mediators = parseStringArray(rawMediators); + const auditors = parseStringArray(rawAuditors); + + const asset = await DartConfidentialAsset.get(assetId); + if (asset) { + asset.mediators = mediators; + asset.auditors = auditors; + asset.callerId = callerId; + asset.eventIdx = eventIdx; + asset.updatedBlockId = blockId; + await asset.save(); + } else { + await ensureAsset({ + assetId, + callerId, + mediators, + auditors, + name: '', + symbol: '', + decimals: 0, + data: undefined, + eventIdx, + blockId, + blockEventId, + }); + } + + await DartConfidentialAssetUpdate.create({ + id: blockEventId, + assetId, + mediators, + auditors, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartAssetCurveTreeRootUpdated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [root] = params; + + await DartConfidentialAssetCurveRoot.create({ + id: blockEventId, + root: getTextValue(root), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartAssetStateLeafUpdated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [leafIndex, leaf] = params; + + await DartConfidentialAssetStateLeaf.create({ + id: blockEventId, + leafIndex: getBigIntValue(leafIndex), + leaf: getTextValue(leaf), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartAssetMinted = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [rawCallerDid, rawAssetId, rawAmount, rawTotalSupply, rawAccount] = params; + + const callerId = getTextValue(rawCallerDid); + const assetIdNumber = getNumberValue(rawAssetId); + const assetId = assetIdNumber?.toString(); + + if (!callerId || assetId === undefined) { + return; + } + + const amount = getBigIntValue(rawAmount); + const totalSupply = getBigIntValue(rawTotalSupply); + const accountId = getTextValue(rawAccount); + + const asset = + (await DartConfidentialAsset.get(assetId)) || + (await ensureAsset({ + assetId, + callerId, + mediators: [], + auditors: [], + name: '', + symbol: '', + decimals: 0, + data: undefined, + eventIdx, + blockId, + blockEventId, + })); + + asset.totalSupply = totalSupply; + asset.eventIdx = eventIdx; + asset.updatedBlockId = blockId; + await asset.save(); + + await DartConfidentialAssetMint.create({ + id: blockEventId, + assetId, + accountId, + callerId, + amount, + totalSupply, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartFeeAccountCurveTreeRootUpdated = async ( + event: SubstrateEvent +): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [root] = params; + + await DartConfidentialFeeAccountCurveRoot.create({ + id: blockEventId, + root: getTextValue(root), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartFeeAccountStateLeafInserted = async ( + event: SubstrateEvent +): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [leafIndex, commitment] = params; + + await DartConfidentialFeeAccountStateLeaf.create({ + id: blockEventId, + leafIndex: getBigIntValue(leafIndex), + commitment: getTextValue(commitment), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartFeeAccountDeposited = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [sender, amount] = params; + + await DartConfidentialFeeAccountDeposit.create({ + id: blockEventId, + sender: getTextValue(sender), + amount: getBigIntValue(amount), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartFeeAccountWithdrawn = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [receiver, amount] = params; + + await DartConfidentialFeeAccountWithdraw.create({ + id: blockEventId, + receiver: getTextValue(receiver), + amount: getBigIntValue(amount), + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartFeeAccountUpdated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [rawCallerDid, rawAccount, rawIsRegistration, rawAmount] = params; + + const callerId = getTextValue(rawCallerDid); + const accountId = getTextValue(rawAccount); + + if (!callerId || !accountId) { + return; + } + + const isRegistration = JSON.parse(getTextValue(rawIsRegistration) || 'false'); + const amount = getBigIntValue(rawAmount); + + const existing = await DartConfidentialFeeAccount.get(accountId); + if (existing) { + existing.callerId = callerId; + existing.isRegistration = isRegistration; + existing.amount = amount; + existing.eventIdx = eventIdx; + existing.updatedBlockId = blockId; + await existing.save(); + return; + } + + await DartConfidentialFeeAccount.create({ + id: accountId, + account: accountId, + callerId, + isRegistration, + amount, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +const saveLegAction = async ( + legRef: string, + action: DartLegActionEnum, + eventIdx: number, + blockId: string, + blockEventId: string, + keyIndex?: number +): Promise => { + if (!legRef) { + return; + } + + await DartConfidentialLegAction.create({ + id: blockEventId, + legRef, + action, + keyIndex, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartMediatorAffirmed = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [legRef, keyIndex] = params; + await saveLegAction( + getTextValue(legRef), + DartLegActionEnum.MediatorAffirmed, + eventIdx, + blockId, + blockEventId, + getNumberValue(keyIndex) + ); +}; + +export const handleDartMediatorRejected = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [legRef, keyIndex] = params; + await saveLegAction( + getTextValue(legRef), + DartLegActionEnum.MediatorRejected, + eventIdx, + blockId, + blockEventId, + getNumberValue(keyIndex) + ); +}; + +export const handleDartReceiverAffirmed = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [legRef] = params; + await saveLegAction( + getTextValue(legRef), + DartLegActionEnum.ReceiverAffirmed, + eventIdx, + blockId, + blockEventId + ); +}; + +export const handleDartReceiverClaimed = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [legRef] = params; + await saveLegAction( + getTextValue(legRef), + DartLegActionEnum.ReceiverClaimed, + eventIdx, + blockId, + blockEventId + ); +}; + +export const handleDartSenderAffirmed = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [legRef] = params; + await saveLegAction( + getTextValue(legRef), + DartLegActionEnum.SenderAffirmed, + eventIdx, + blockId, + blockEventId + ); +}; + +export const handleDartSenderCounterUpdated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [legRef] = params; + await saveLegAction( + getTextValue(legRef), + DartLegActionEnum.SenderCounterUpdated, + eventIdx, + blockId, + blockEventId + ); +}; + +export const handleDartSenderReverted = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [legRef] = params; + await saveLegAction( + getTextValue(legRef), + DartLegActionEnum.SenderReverted, + eventIdx, + blockId, + blockEventId + ); +}; + +export const handleDartRelayerBatchedProofs = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [relayer, amount, batchHash, batchResult] = params; + + let resultText = normalizeBatchResult(batchResult); + if (!resultText) { + try { + resultText = JSON.stringify(getErrorDetails(batchResult)); + } catch { + resultText = undefined; + } + } + + await DartConfidentialRelayerBatch.create({ + id: blockEventId, + relayer: getTextValue(relayer), + amount: getBigIntValue(amount), + batchHash: getTextValue(batchHash), + batchResult: resultText, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }).save(); +}; + +export const handleDartSettlementCreated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [settlementRef, memo, assetRootBlock, legsCodec] = params; + + const id = getTextValue(settlementRef); + if (!id) { + return; + } + + const legs = parseBytesArray(legsCodec); + + const settlement = DartConfidentialSettlement.create({ + id, + settlementRef: id, + memo: bytesToString(memo), + assetRootBlock: getNumberValue(assetRootBlock), + legs, + status: undefined, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }); + + await settlement.save(); +}; + +export const handleDartSettlementStatusUpdated = async (event: SubstrateEvent): Promise => { + const { params, eventIdx, blockId, blockEventId } = extractArgs(event); + const [settlementRef, statusCodec] = params; + + const id = getTextValue(settlementRef); + if (!id) { + return; + } + + const status = getTextValue(statusCodec); + + const settlement = + (await DartConfidentialSettlement.get(id)) || + DartConfidentialSettlement.create({ + id, + settlementRef: id, + memo: undefined, + assetRootBlock: 0, + legs: [], + status, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }); + + settlement.status = status; + settlement.eventIdx = eventIdx; + settlement.updatedBlockId = blockId; + + const statusHistory = DartConfidentialSettlementStatus.create({ + id: blockEventId, + settlementId: id, + status, + eventIdx, + createdBlockId: blockId, + updatedBlockId: blockId, + createdEventId: blockEventId, + }); + + await Promise.all([settlement.save(), statusHistory.save()]); +}; diff --git a/src/mappings/entities/index.ts b/src/mappings/entities/index.ts index 51fc488c..d529b38d 100644 --- a/src/mappings/entities/index.ts +++ b/src/mappings/entities/index.ts @@ -9,6 +9,7 @@ export * from './confidentialAssets/mapConfidentialAccountCreated'; export * from './confidentialAssets/mapConfidentialAsset'; export * from './confidentialAssets/mapConfidentialAssetTransaction'; export * from './confidentialAssets/mapConfidentialTransaction'; +export * from './confidentialAssets/mapDartConfidentialAssets'; export * from './assets/mapCorporateActions'; export * from './events/mapEvent'; export * from './externalAgents/mapExternalAgentAction';