Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
"@alien-worlds/aw-antelope": "^0.0.45",
"@alien-worlds/aw-contract-alien-worlds": "^0.0.28",
"@alien-worlds/aw-contract-dao-worlds": "^0.0.39",
"@alien-worlds/aw-contract-index-worlds": "^0.0.33",
"@alien-worlds/aw-contract-stkvt-worlds": "^0.0.27",
"@alien-worlds/aw-contract-token-worlds": "^0.0.36",
"@alien-worlds/aw-contract-index-worlds": "^0.0.33",
"@alien-worlds/aw-contract-msig-worlds": "^0.0.6",
"@fastify/swagger": "6.1.1",
"@shelf/winston-datadog-logs-transport": "^1.0.7",
"ajv": "^8.12.0",
Expand Down
18 changes: 14 additions & 4 deletions src/endpoints/api.ioc.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { setupAlienWorldsContractService } from '@alien-worlds/aw-contract-alien
import { setupIndexWorldsContractService } from '@alien-worlds/aw-contract-index-worlds';
import { setupStkvtWorldsDeltaRepository } from '@alien-worlds/aw-contract-stkvt-worlds';
import { setupTokenWorldsContractService } from '@alien-worlds/aw-contract-token-worlds';
import { setupMsigWorldsContractService } from '@alien-worlds/aw-contract-msig-worlds';
import { AntelopeRpcSourceImpl } from '@alien-worlds/aw-antelope';
import ApiConfig from '@src/config/api-config';
import { MongoSource } from '@alien-worlds/aw-storage-mongodb';
Expand All @@ -19,6 +20,7 @@ import { CustodiansDependencyInjector } from './custodians/custodians.ioc';
import { HealthDependencyInjector } from './health/health.ioc';
import { VotingHistoryDependencyInjector } from './voting-history/voting-history.ioc';
import { PingDependencyInjector } from './ping/ping.ioc';
import { MSIGSDependencyInjector } from './msigs/msigs.ioc';

export class ApiDependencyInjector extends DependencyInjector {
public async setup(config: ApiConfig): Promise<void> {
Expand All @@ -37,6 +39,7 @@ export class ApiDependencyInjector extends DependencyInjector {
const votingHistoryDI = new VotingHistoryDependencyInjector(container);
const candidatesDI = new CandidatesDependencyInjector(container);
const custodiansDI = new CustodiansDependencyInjector(container);
const msigsDI = new MSIGSDependencyInjector(container);

healthDI.setup(config);
pingDI.setup();
Expand All @@ -46,30 +49,37 @@ export class ApiDependencyInjector extends DependencyInjector {
votingHistoryDI.setup();
candidatesDI.setup();
custodiansDI.setup();
msigsDI.setup();

/**
* SMART CONTRACT SERVICES
*/

await setupIndexWorldsContractService(
setupIndexWorldsContractService(
antelopeRpcSource,
config.antelope.hyperionUrl,
container
);

await setupAlienWorldsContractService(
setupAlienWorldsContractService(
antelopeRpcSource,
config.antelope.hyperionUrl,
container
);

await setupDaoWorldsContractService(
setupDaoWorldsContractService(
antelopeRpcSource,
config.antelope.hyperionUrl,
container
);

await setupTokenWorldsContractService(
setupTokenWorldsContractService(
antelopeRpcSource,
config.antelope.hyperionUrl,
container
);

setupMsigWorldsContractService(
antelopeRpcSource,
config.antelope.hyperionUrl,
container
Expand Down
4 changes: 4 additions & 0 deletions src/endpoints/msigs/data/dtos/msigs.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type GetMSIGSRequestQueryParams = {
dacId: string;
limit: number;
};
23 changes: 23 additions & 0 deletions src/endpoints/msigs/data/mappers/msig-state.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {
MSIGStateIndex,
MSIGStateKey,
} from '@endpoints/msigs/domain/msig.enums';

/**
* The `MSIGStateMapper` class is responsible for converting a numeric state into a string state.
*/
export class MSIGStateMapper {
private msigKeyMapping = new Map<MSIGStateIndex, MSIGStateKey>([
[MSIGStateIndex.PENDING, MSIGStateKey.PENDING],
[MSIGStateIndex.EXECUTED, MSIGStateKey.EXECUTED],
[MSIGStateIndex.CANCELLED, MSIGStateKey.CANCELLED],
]);

/**
* Converts a list of key-value pairs into a `DacAccounts` object.
* @returns {MSIGStateKey} - The DacAccounts object representing the DAC accounts with their balances.
*/
public toLabel(state: number): MSIGStateKey {
return this.msigKeyMapping.get(state) || MSIGStateKey.UNKNOWN;
}
}
42 changes: 42 additions & 0 deletions src/endpoints/msigs/data/mappers/proposals.mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as MSIGWorldsCommon from '@alien-worlds/aw-contract-msig-worlds';

import { Proposal } from '@endpoints/msigs/domain/entities/proposals';
import { MSIGStateMapper } from './msig-state.mapper';
/**
* The `ProposalsMapper` class is responsible for converting an object representing a MSIG Proposal from the MSIG Worlds contract
* into a `Proposal` object, which is a domain entity that represents a MSIG Proposal.
*/
export class ProposalsMapper {
private mapper = new MSIGStateMapper();

/**
* @param {MSIGWorldsCommon.Deltas.Entities.Proposals} msigWorldsProposal - The object representing the Proposal from the Msig Worlds contract.
* @returns {Proposal} - The `Proposal` object representing the MSIG Proposal.
*/
public toProposal(
msigWorldsProposal: MSIGWorldsCommon.Deltas.Entities.Proposals
): Proposal {
const {
proposer,
proposalName,
packedTransaction,
metadata,
modifiedDate,
state,
earliestExecTime,
id,
...rest
} = msigWorldsProposal;
return Proposal.create(
proposer,
proposalName,
packedTransaction.raw,
metadata,
modifiedDate,
this.mapper.toLabel(state),
earliestExecTime,
id,
rest
);
}
}
248 changes: 248 additions & 0 deletions src/endpoints/msigs/domain/__tests__/get-dacs.controller.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/*
import { ExtendedSymbolRawModel, Pair } from '@alien-worlds/aw-antelope';
import * as AlienWorldsCommon from '@alien-worlds/aw-contract-alien-worlds';
import * as DaoWorldsCommon from '@alien-worlds/aw-contract-dao-worlds';
import * as IndexWorldsCommon from '@alien-worlds/aw-contract-index-worlds';
import * as TokenWorldsCommon from '@alien-worlds/aw-contract-token-worlds';
import { Container, Failure, Result } from '@alien-worlds/aw-core';
import { DacMapper } from '@endpoints/dacs/data/mappers/dacs.mapper';

import { MSIGSController } from '../msigs.controller';
import { GetMSIGSInput } from '../models/msigs.input';
import { CreateAggregatedMSIGRecords } from '../use-cases/create-aggregated-msig-records.use-case';
import { GetAllMSIGSUseCase } from '../use-cases/get-all-msigs.use-case';
import { GetApprovalsUseCase } from '../use-cases/get-approvals.use-case';
// import { GetDacTokensUseCase } from '../use-cases/_get-dac-tokens.use-case';
// import { GetDacTreasuryUseCase } from '../use-cases/_get-dac-treasury.use-case';

const getAllDacsUseCase = {
execute: jest.fn(() =>
Result.withContent([
new DacMapper().toDac(
new IndexWorldsCommon.Deltas.Mappers.DacsRawMapper().toEntity(<
IndexWorldsCommon.Deltas.Types.DacsRawModel
>{
owner: 'eyeke.dac',
dac_id: 'eyeke',
title: 'Eyeke',
symbol: <ExtendedSymbolRawModel>{
sym: '4,EYE',
contract: 'token.worlds',
},
dac_state: 0,
refs: [
{
key: '1',
value: 'QmW1SeninpNQUMLstTPFTv7tMkRdBZMHBJTgf17Znr1uKK',
},
{
key: '2',
value:
'Also known as Second Earth or Terra Alterna as it is the nearest, and closest, of all Alien Worlds to Earth. Humans found Eyeke inhabited by a Monastic order of Greys who believe that Eyeke is a spiritual place. Despite initial fears, Trilium mining seems to be tolerated by the Monks at this time.',
},
{
key: '12',
value: 'QmUXjmrQ6j2ukCdPjacdQ48MmYo853u4Y5y3kb5b4HBuuF',
},
] as Pair<string, string>[],
accounts: [{ key: '0', value: 'eyeke.world' }] as Pair<
string,
string
>[],
})
),
])
),
};

const getDacTreasuryUseCase = {
execute: jest.fn(() =>
Result.withContent([
new AlienWorldsCommon.Deltas.Mappers.AccountsRawMapper().toEntity(<
AlienWorldsCommon.Deltas.Types.AccountsRawModel
>{
balance: 'string',
}),
])
),
};
const getDacInfoUseCase = {
execute: jest.fn(() =>
Result.withContent([
new DaoWorldsCommon.Deltas.Mappers.DacglobalsRawMapper().toEntity({
data: [
{ key: 'auth_threshold_high', value: ['uint8', 3] },
{ key: 'auth_threshold_low', value: ['uint8', 2] },
{ key: 'auth_threshold_mid', value: ['uint8', 3] },
{ key: 'budget_percentage', value: ['uint32', 200] },
{ key: 'initial_vote_quorum_percent', value: ['uint32', 2] },
{
key: 'lastclaimbudgettime',
value: ['time_point_sec', '2023-07-12T06:59:34'],
},
{
key: 'lastperiodtime',
value: ['time_point_sec', '2023-07-12T02:13:16'],
},
{ key: 'lockup_release_time_delay', value: ['uint32', 1] },
{
key: 'lockupasset',
value: [
'extended_asset',
{ quantity: '5000.0000 EYE', contract: 'token.worlds' },
],
},
{ key: 'maxvotes', value: ['uint8', 2] },
{ key: 'met_initial_votes_threshold', value: ['bool', 1] },
{ key: 'number_active_candidates', value: ['uint32', 16] },
{ key: 'numelected', value: ['uint8', 5] },
{ key: 'periodlength', value: ['uint32', 604800] },
{
key: 'requested_pay_max',
value: [
'extended_asset',
{ quantity: '0.0000 TLM', contract: 'alien.worlds' },
],
},
{ key: 'should_pay_via_service_provider', value: ['bool', 0] },
{ key: 'token_supply_theshold', value: ['uint64', 100000000] },
{
key: 'total_votes_on_candidates',
value: ['int64', '419531173046'],
},
{
key: 'total_weight_of_votes',
value: ['int64', '84271574980'],
},
{ key: 'vote_quorum_percent', value: ['uint32', 1] },
] as any,
}),
])
),
};

const getDacTokensUseCase = {
execute: jest.fn(() =>
Result.withContent([
new TokenWorldsCommon.Deltas.Mappers.StatRawMapper().toEntity({
supply: '1660485.1217 EYE',
max_supply: '10000000000.0000 EYE',
issuer: 'federation',
transfer_locked: false,
}),
])
),
};

const input: GetDacsInput = {
dacId: 'string',
limit: 1,
toJSON: () => ({
dacId: 'string',
limit: 1,
}),
};

let container: Container;
let controller: MSIGSController;

describe('GetDacs Controller Unit tests', () => {
beforeAll(() => {
container = new Container();

container
.bind<GetAllMSIGSUseCase>(GetAllMSIGSUseCase.Token)
.toConstantValue(getAllDacsUseCase as any);
container
.bind<GetDacTreasuryUseCase>(GetDacTreasuryUseCase.Token)
.toConstantValue(getDacTreasuryUseCase as any);
container
.bind<GetApprovalsUseCase>(GetApprovalsUseCase.Token)
.toConstantValue(getDacInfoUseCase as any);
container
.bind<GetDacTokensUseCase>(GetDacTokensUseCase.Token)
.toConstantValue(getDacTokensUseCase as any);
container
.bind<CreateAggregatedMSIGRecords>(CreateAggregatedMSIGRecords.Token)
.to(CreateAggregatedMSIGRecords);
container.bind<MSIGSController>(MSIGSController.Token).to(MSIGSController);
});

beforeEach(() => {
controller = container.get<MSIGSController>(MSIGSController.Token);
});

afterAll(() => {
jest.clearAllMocks();
container = null;
});

it('"Token" should be set', () => {
expect(MSIGSController.Token).not.toBeNull();
});

it('Should execute GetAllDacsUseCase', async () => {
await controller.getDacs(input);

expect(getAllDacsUseCase.execute).toBeCalled();
});

it('Should execute GetDacTreasuryUseCase', async () => {
await controller.getDacs(input);

expect(getDacTreasuryUseCase.execute).toBeCalled();
});

it('Should execute GetDacInfoUseCase', async () => {
await controller.getDacs(input);

expect(getDacInfoUseCase.execute).toBeCalled();
});

it('Should execute GetDacTokensUseCase', async () => {
await controller.getDacs(input);

expect(getDacTokensUseCase.execute).toBeCalled();
});

it('Should return failure when GetAllDacsUseCase fails', async () => {
getAllDacsUseCase.execute.mockImplementationOnce(
jest.fn(() => Result.withFailure(Failure.fromError(null)))
);

const output = await controller.getDacs(input);

expect(output.result.isFailure).toBeTruthy();
});

it('Should return failure when GetDacTreasuryUseCase fails', async () => {
getDacTreasuryUseCase.execute.mockImplementationOnce(
jest.fn(() => Result.withFailure(Failure.fromError(null)))
);

const output = await controller.getDacs(input);

expect(output.result.isFailure).toBeTruthy();
});

it('Should return failure when GetDacInfoUseCase fails', async () => {
getDacInfoUseCase.execute.mockImplementationOnce(
jest.fn(() => Result.withFailure(Failure.fromError(null)))
);

const output = await controller.getDacs(input);

expect(output.result.isFailure).toBeTruthy();
});

it('Should return failure when GetDacTokensUseCase fails', async () => {
getDacTokensUseCase.execute.mockImplementationOnce(
jest.fn(() => Result.withFailure(Failure.fromError(null)))
);

const output = await controller.getDacs(input);

expect(output.result.isFailure).toBeTruthy();
});
});
*/
Loading