Skip to content

Commit

Permalink
Merge branch 'refs/heads/feat/sovereign' into merge-dev-feat-sov-jan30
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/endpoints/tokens/token.controller.ts
#	src/endpoints/transactions/transaction.controller.ts
  • Loading branch information
bogdan-rosianu committed Jan 30, 2025
2 parents d9f1e5f + 19f3047 commit f7cef7c
Show file tree
Hide file tree
Showing 17 changed files with 106 additions and 19 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/unit.tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
name: Unit tests

on:
push:
branches: [main, development]
pull_request:
branches: [main, development]

jobs:
build:
Expand Down
1 change: 1 addition & 0 deletions config/config.devnet-old.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
network: 'devnet-old'
metaChainShardId: 4294967295
crossChainSenderShardId: 4294967293
api:
public: true
private: true
Expand Down
1 change: 1 addition & 0 deletions config/config.devnet.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
network: 'devnet'
metaChainShardId: 4294967295
crossChainSenderShardId: 4294967293
api:
public: true
publicPort: 3001
Expand Down
1 change: 1 addition & 0 deletions config/config.e2e-mocked.mainnet.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
network: 'mainnet'
metaChainShardId: 4294967295
crossChainSenderShardId: 4294967293
api:
public: true
private: true
Expand Down
1 change: 1 addition & 0 deletions config/config.e2e.mainnet.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
network: 'mainnet'
metaChainShardId: 4294967295
crossChainSenderShardId: 4294967293
api:
public: true
private: true
Expand Down
1 change: 1 addition & 0 deletions config/config.mainnet.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
network: 'mainnet'
metaChainShardId: 4294967295
crossChainSenderShardId: 4294967293
api:
public: true
publicPort: 3001
Expand Down
1 change: 1 addition & 0 deletions config/config.placeholder.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
network: 'DAPP_CONFIG'
metaChainShardId: 4294967295
crossChainSenderShardId: 4294967293
api:
public: true
publicPort: 3001
Expand Down
1 change: 1 addition & 0 deletions config/config.testnet.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
network: 'testnet'
metaChainShardId: 4294967295
crossChainSenderShardId: 4294967293
api:
public: true
publicPort: 3001
Expand Down
9 changes: 9 additions & 0 deletions src/common/api-config/api.config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,15 @@ export class ApiConfigService {
return metaChainShardId;
}

getCrossChainSenderShardId(): number {
const crossChainSenderShardId = this.configService.get<number>('crossChainSenderShardId');
if (crossChainSenderShardId === undefined) {
throw new Error('No crossChainSenderShardId present');
}

return crossChainSenderShardId;
}

getRateLimiterSecret(): string | undefined {
return this.configService.get<string>('rateLimiterSecret');
}
Expand Down
17 changes: 14 additions & 3 deletions src/common/indexer/elastic/elastic.indexer.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,16 +535,27 @@ export class ElasticIndexerHelper {
let elasticQuery = ElasticQuery.create();

if (!filter.withRelayedScresults) {
elasticQuery = elasticQuery.withMustMatchCondition('type', 'normal');
if (!filter.withCrossChainTransfers) {
elasticQuery = elasticQuery.withMustMatchCondition('type', 'normal');
} else {
elasticQuery = elasticQuery.withShouldCondition([
QueryType.Match('type', 'normal'),
QueryType.Match('senderShard', this.apiConfigService.getCrossChainSenderShardId()),
]);
}
} else {
elasticQuery = elasticQuery.withShouldCondition([
const shouldConditions = [
QueryType.Match('type', 'normal'),
QueryType.Must([
QueryType.Exists('relayerAddr'),
QueryType.Match('type', 'unsigned'),
new ScriptQuery(`doc['originalTxHash'].size() > 0 && doc['prevTxHash'].size() > 0 && doc['originalTxHash'].value == doc['prevTxHash'].value`),
]),
]);
];
if (filter.withCrossChainTransfers) {
shouldConditions.push(QueryType.Match('senderShard', this.apiConfigService.getCrossChainSenderShardId()));
}
elasticQuery = elasticQuery.withShouldCondition(shouldConditions);
}

elasticQuery = elasticQuery.withMustMatchCondition('senderShard', filter.senderShard)
Expand Down
7 changes: 6 additions & 1 deletion src/endpoints/accounts/account.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,7 @@ export class AccountController {
@ApiQuery({ name: 'isScCall', description: 'Returns sc call transactions details', required: false, type: Boolean })
@ApiQuery({ name: 'withActionTransferValue', description: 'Returns value in USD and EGLD for transferred tokens within the action attribute', required: false })
@ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean })
@ApiQuery({ name: 'withCrossChainTransfers', description: 'If set to true, will include cross chain transfer transactions', required: false, type: Boolean })
async getAccountTransactions(
@Param('address', ParseAddressPipe) address: string,
@Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number,
Expand Down Expand Up @@ -914,6 +915,7 @@ export class AccountController {
@Query('isScCall', ParseBoolPipe) isScCall?: boolean,
@Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean,
@Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean,
@Query('withCrossChainTransfers', ParseBoolPipe) withCrossChainTransfers?: boolean,
) {
const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername, withBlockInfo, withActionTransferValue });

Expand All @@ -935,6 +937,7 @@ export class AccountController {
isScCall,
round,
withRelayedScresults,
withCrossChainTransfers,
});
TransactionFilter.validate(transactionFilter, size);
return await this.transactionService.getTransactions(transactionFilter, new QueryPagination({ from, size }), options, address, fields);
Expand All @@ -959,6 +962,7 @@ export class AccountController {
@ApiQuery({ name: 'isRelayed', description: 'Returns isRelayed transactions details', required: false, type: Boolean })
@ApiQuery({ name: 'isScCall', description: 'Returns sc call transactions details', required: false, type: Boolean })
@ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean })
@ApiQuery({ name: 'withCrossChainTransfers', description: 'If set to true, will include cross chain transfer transactions', required: false, type: Boolean })
async getAccountTransactionsCount(
@Param('address', ParseAddressPipe) address: string,
@Query('sender', ParseAddressPipe) sender?: string,
Expand All @@ -977,7 +981,7 @@ export class AccountController {
@Query('isRelayed', ParseBoolPipe) isRelayed?: boolean,
@Query('isScCall', ParseBoolPipe) isScCall?: boolean,
@Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean,

@Query('withCrossChainTransfers', ParseBoolPipe) withCrossChainTransfers?: boolean,
): Promise<number> {

return await this.transactionService.getTransactionCount(new TransactionFilter({
Expand All @@ -997,6 +1001,7 @@ export class AccountController {
isScCall,
round,
withRelayedScresults,
withCrossChainTransfers,
}), address);
}

Expand Down
6 changes: 6 additions & 0 deletions src/endpoints/tokens/token.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export class TokenController {
@ApiQuery({ name: 'withBlockInfo', description: 'Returns sender / receiver block details', required: false, type: Boolean })
@ApiQuery({ name: 'withActionTransferValue', description: 'Returns value in USD and EGLD for transferred tokens within the action attribute', required: false })
@ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean })
@ApiQuery({ name: 'withCrossChainTransfers', description: 'If set to true, will include cross chain transfer transactions', required: false, type: Boolean })
async getTokenTransactions(
@Param('identifier', ParseTokenPipe) identifier: string,
@Query('from', new DefaultValuePipe(0), ParseIntPipe) from: number,
Expand All @@ -232,6 +233,7 @@ export class TokenController {
@Query('withBlockInfo', ParseBoolPipe) withBlockInfo?: boolean,
@Query('withActionTransferValue', ParseBoolPipe) withActionTransferValue?: boolean,
@Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean,
@Query('withCrossChainTransfers', ParseBoolPipe) withCrossChainTransfers?: boolean,
) {
const options = TransactionQueryOptions.applyDefaultOptions(size, { withScResults, withOperations, withLogs, withScamInfo, withUsername, withBlockInfo, withActionTransferValue });

Expand All @@ -256,6 +258,7 @@ export class TokenController {
round,
isScCall,
withRelayedScresults,
withCrossChainTransfers,
});
TransactionFilter.validate(transactionFilter, size);

Expand Down Expand Up @@ -284,6 +287,7 @@ export class TokenController {
@ApiQuery({ name: 'round', description: 'Filter by round number', required: false })
@ApiQuery({ name: 'isScCall', description: 'Returns sc call transactions details', required: false, type: Boolean })
@ApiQuery({ name: 'withRelayedScresults', description: 'If set to true, will include smart contract results that resemble relayed transactions', required: false, type: Boolean })
@ApiQuery({ name: 'withCrossChainTransfers', description: 'If set to true, will include cross chain transfer transactions', required: false, type: Boolean })
async getTokenTransactionsCount(
@Param('identifier', ParseTokenPipe) identifier: string,
@Query('sender', ParseAddressPipe) sender?: string,
Expand All @@ -298,6 +302,7 @@ export class TokenController {
@Query('round', ParseIntPipe) round?: number,
@Query('withRelayedScresults', ParseBoolPipe) withRelayedScresults?: boolean,
@Query('isScCall', ParseBoolPipe) isScCall?: boolean,
@Query('withCrossChainTransfers', ParseBoolPipe) withCrossChainTransfers?: boolean,
) {
return await this.transactionService.getTransactionCount(new TransactionFilter({
sender,
Expand All @@ -313,6 +318,7 @@ export class TokenController {
round,
withRelayedScresults,
isScCall,
withCrossChainTransfers,
}));
}

Expand Down
1 change: 1 addition & 0 deletions src/endpoints/transactions/entities/transaction.filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class TransactionFilter {
withRefunds?: boolean;
withRelayedScresults?: boolean;
withTxsRelayedByAddress?: boolean;
withCrossChainTransfers?: boolean;

constructor(init?: Partial<TransactionFilter>) {
Object.assign(this, init);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class TransactionMetadata {
receiver: string = '';
value: BigInt = BigInt(0);
functionName?: string;
senderShard?: number = -1;
functionArgs: string[] = [];
timestamp: number = 0;
transfers?: TransactionMetadataTransfer[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import { TransactionActionEsdtNftRecognizerService } from "./recognizers/esdt/tr
import { TokenTransferService } from "src/endpoints/tokens/token.transfer.service";
import { TransactionType } from "src/endpoints/transactions/entities/transaction.type";
import { MetabondingActionRecognizerService } from "./recognizers/mex/mex.metabonding.action.recognizer.service";
import { AddressUtils, BinaryUtils, StringUtils } from "@multiversx/sdk-nestjs-common";
import { OriginLogger } from "@multiversx/sdk-nestjs-common";
import { AddressUtils, BinaryUtils, OriginLogger, StringUtils } from "@multiversx/sdk-nestjs-common";
import { TokenTransferProperties } from "../../tokens/entities/token.transfer.properties";
import { ApiConfigService } from "../../../common/api-config/api.config.service";

@Injectable()
export class TransactionActionService {
Expand All @@ -26,6 +27,7 @@ export class TransactionActionService {
@Inject(forwardRef(() => TokenTransferService))
private readonly tokenTransferService: TokenTransferService,
private readonly metabondingRecognizer: MetabondingActionRecognizerService,
private readonly apiConfigService: ApiConfigService,
) { }

private async getRecognizers() {
Expand Down Expand Up @@ -86,6 +88,9 @@ export class TransactionActionService {
metadata.receiver = transaction.receiver;
metadata.timestamp = transaction.timestamp;
metadata.value = BigInt(transaction.value);
if (transaction.senderShard !== undefined) {
metadata.senderShard = transaction.senderShard;
}

if (transaction.data) {
const decodedData = BinaryUtils.base64Decode(transaction.data);
Expand Down Expand Up @@ -130,7 +135,8 @@ export class TransactionActionService {
try {
if (transaction.type === TransactionType.SmartContractResult) {
if (metadata.functionName === 'MultiESDTNFTTransfer' &&
metadata.functionArgs.length > 0
metadata.functionArgs.length > 0 &&
metadata.senderShard !== this.apiConfigService.getCrossChainSenderShardId()
) {
// if the first argument has up to 4 hex chars (meaning it will contain up to 65536 transfers)
// then we insert the address as the first parameter. otherwise we assume that the address
Expand Down Expand Up @@ -170,8 +176,15 @@ export class TransactionActionService {
}

private async getMultiTransferMetadata(metadata: TransactionMetadata, applyValue: boolean = false): Promise<TransactionMetadata | undefined> {
/*
sovereign cross chain transfer example: MultiESDTNFTTransfer@02@4147452d626532353731@@01314fb37062980000@42474431362d633437663436@@5d894a4a3a220000
regular chain example: MultiESDTNFTTransfer@0000000000000000050000b4c094947e427d79931a8bad81316b797d238cdb3f@02@4c524f4e452d633133303234@@036f5933a0d19ae387@524f4e452d626232653639@@04493d2ce61b650000@6164644c6971756964697479@01@01
*/
const isSovereignCrossChainTransfer = metadata.senderShard === this.apiConfigService.getCrossChainSenderShardId();
if (metadata.sender !== metadata.receiver) {
return undefined;
if (!isSovereignCrossChainTransfer) {
return undefined;
}
}

if (metadata.functionName !== 'MultiESDTNFTTransfer') {
Expand All @@ -183,27 +196,42 @@ export class TransactionActionService {
return undefined;
}

if (!AddressUtils.isValidHexAddress(args[0])) {
if (!AddressUtils.isValidHexAddress(args[0]) && !isSovereignCrossChainTransfer) {
return undefined;
}

const receiver = AddressUtils.bech32Encode(args[0]);
const transferCount = BinaryUtils.hexToNumber(args[1]);
let receiver: string;
if (!isSovereignCrossChainTransfer) {
receiver = AddressUtils.bech32Encode(args[0]);
} else {
receiver = metadata.receiver;
}

let transferCountIndex = 1;
if (isSovereignCrossChainTransfer) {
transferCountIndex = 0;
}
const transferCount = BinaryUtils.hexToNumber(args[transferCountIndex]);

const result = new TransactionMetadata();
if (!result.transfers) {
result.transfers = [];
}

let index = 2;
if (isSovereignCrossChainTransfer) {
index = 1;
}
for (let i = 0; i < transferCount; i++) {
const identifier = BinaryUtils.hexToString(args[index++]);
const nonce = args[index++];
const value = this.parseValueFromMultiTransferValueArg(args[index++]);

let validProperties = false;
if (nonce && nonce !== "00") {
const properties = await this.tokenTransferService.getTokenTransferProperties({ identifier, nonce });
if (properties) {
validProperties = true;
result.transfers.push({
value,
properties,
Expand All @@ -212,12 +240,26 @@ export class TransactionActionService {
} else {
const properties = await this.tokenTransferService.getTokenTransferProperties({ identifier, timestamp: metadata.timestamp, value: value.toString(), applyValue });
if (properties) {
validProperties = true;
result.transfers.push({
value,
properties,
});
}
}

// TODO: might remove this after token details are indexed inside sovereign es
if (!validProperties) { // missing token details (decimals for example. extract transfer info - best effort)
result.transfers.push({
value,
properties: new TokenTransferProperties({
decimals: 18,
identifier,
ticker: identifier,
token: identifier,
}),
});
}
}

result.sender = metadata.sender;
Expand Down
Loading

0 comments on commit f7cef7c

Please sign in to comment.