Skip to content

Commit

Permalink
Adds Angor UI (#13)
Browse files Browse the repository at this point in the history
* feat(mempool-ui): adds initial component files

* fix: interface importing

* chore: local dev setup

* adds angor ui components and interfaces

* feat(ui): adds blockchain view

* feature(angor-search): adds angor to search bar

* feat(ui): updates angor module structure

* feat(ui): adds pagination - part 1

* feat(ui): adds pagination to the list

* feat(ui): adds no-data skeleton

* fix: updates the project component

* fix: return empty array if no investments

* chore: restores local dev settings

* chore: removes redundant description

* fix: adds missed mock (#14)

* mempool#144 - Extract NostrEventId instead Npub (#16)

* fix: updates signet challenge

* feat(eventId): initial commit

* feat(event-id): removes npub from query

* feat(eventId): updates unit tests

* fix: reverse order of projects

* reverts local dev changes
  • Loading branch information
nostrdev-com authored Dec 19, 2024
1 parent d5174d6 commit e5ff277
Show file tree
Hide file tree
Showing 23 changed files with 1,078 additions and 65 deletions.
38 changes: 21 additions & 17 deletions backend/src/angor/AngorTransactionDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,19 @@ export class AngorTransactionDecoder {
const founderKeyHashInt = this.hashToInt(founderKeyHash);
const projectIdDerivation = this.getProjectIdDerivation(founderKeyHashInt);
const projectId = this.getProjectId(projectIdDerivation);
const nostrPubKey = this.getNostrPubKey();
const nostrEventId = this.getNostrEventId();
const addressOnFeeOutput = this.getAddressOnFeeOutput();
const txid = this.transaction.getId();

// Store Angor project in the DB.
await this.storeProjectInfo(
projectId,
nostrPubKey,
addressOnFeeOutput,
transactionStatus,
founderKeyHex,
txid,
createdOnBlock
createdOnBlock,
nostrEventId
);

// If transaction is confirmed (in the block), update statuses
Expand Down Expand Up @@ -224,23 +224,27 @@ export class AngorTransactionDecoder {
const chunks = bitcoinJS.script.toASM(decompiled).split(' ');

// Throw an error if the chunks amount is incorrect.
if (chunks.length !== 3) {
if (chunks.length !== 4) {
throw new Error(`${errorBase} Wrong chunk amount.`);
}

// Throw an error if the first chunk is not OP_RETURN.
if (chunks[0] !== 'OP_RETURN') {
throw new Error(`${errorBase} Wrong first chunk.`);
throw new Error(`${errorBase} Wrong OP_RETURN chunk.`);
}

// Throw an error if the byte length of the second chunk is not 33.
if (Buffer.from(chunks[1], 'hex').byteLength !== 33) {
throw new Error(`${errorBase} Wrong second chunk.`);
throw new Error(`${errorBase} Wrong founder pubkey chunk.`);
}

// Throw an error if the byte length of the third chunk is not 32.
if (Buffer.from(chunks[2], 'hex').byteLength !== 32) {
throw new Error(`${errorBase} Wrong third chunk.`);
if (Buffer.from(chunks[2], 'hex').byteLength !== 2) {
throw new Error(`${errorBase} Wrong key type chunk.`);
}

if (Buffer.from(chunks[3], 'hex').byteLength !== 32) {
throw new Error(`${errorBase} Wrong nostr event ID chunk.`);
}

// Remove the first chunk (OP_RETURN) as it is not useful anymore.
Expand Down Expand Up @@ -405,13 +409,13 @@ export class AngorTransactionDecoder {
}

/**
* Sets Nostr public key.
* @returns - string representing Nostr public key of Angor project.
* Sets Nostr event id.
* @return - string representing the Nostr event ID associated with the current Angor project.
* @private
*/
private getNostrPubKey(): string {
private getNostrEventId(): string {
const chunks = this.decompileProjectCreationOpReturnScript();

return chunks[1];
return chunks[2];
}

/**
Expand All @@ -434,21 +438,21 @@ export class AngorTransactionDecoder {
*/
private async storeProjectInfo(
projectId: string,
nostrPubKey: string,
addressOnFeeOutput: string,
transactionStatus: AngorTransactionStatus,
founderKey: string,
txid: string,
createdOnBlock?: number
createdOnBlock?: number,
nostrEventId?: string
): Promise<void> {
await AngorProjectRepository.$setProject(
projectId,
nostrPubKey,
addressOnFeeOutput,
transactionStatus,
founderKey,
txid,
createdOnBlock
createdOnBlock,
nostrEventId
);
}

Expand Down
41 changes: 23 additions & 18 deletions backend/src/angor/tests/AngorTransactionDecoder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ describe('AngorTransactionDecoder', () => {
describe('Decoding transaction for Angor project creation', () => {
const data = {
transactionHex:
'0100000000010138de40ff6a3d27c33d5b84edf8f35911d819c9c547689cc4da6c5603bc3b26990000000000ffffffff0310270000000000001600144282ccfe323dbba535ccdfc8b66aeeb0bd7dd95b0000000000000000446a210352eb18befb145fef4b5c24513608183d7c3d8004d5fcad9e0e6dc2e89a0af6ae20c749d81fc42037e5b9f7b32ae266cbce75019ce6771be6877d3c509e97e39c14669b052a0100000016001431e5c2bdb361b68eb243ec7dbd46bd7cf71a1d86024730440220075dd33e3809a58423257bf23f5632e305387c283f48dfdce7f355787be33d8002201072fa636fb6bad35ac9d7f8e35fb41eea47de8b38346dc4f123260e9c74c3b401210385143acd30d6d05a5bf35ef1028ce4c50eadffa14670716976007fec5ed3e58500000000',
'01000000000101899c7a384fe0e17dfd3d56fcf6bfff796b5d0f40ecb5bc513a92e891bb2018af0200000000ffffffff031127000000000000160014e6285b56cb7cd9a51af2f28cb02762b5298c98db0000000000000000476a2102070d174561688500aac733116dbe70c5ab7480559d25e1c040f480491870c8ba020100200f2d8db8568bd3e12bdab1faa217fffc80459053967eff8bde0a65f14e2b7079d542052a01000000160014ae63769a0b0a5f69b3be5d1e6bb4b00d15eff7d0024830450221008e797faa2ef8c3e91ff03f4a47e76740cbadf4b5061d0508ffd89ab869891cb2022050c624530f5c6afbe6ec0dcba4c81287431595f89370225f7d12d3866cc0499f01210396d79f9c4a836defed971668ea51ed50495d5e2d205da2590e7f6600af03f8c800000000',
founderKeyHex:
'0352eb18befb145fef4b5c24513608183d7c3d8004d5fcad9e0e6dc2e89a0af6ae',
'02070d174561688500aac733116dbe70c5ab7480559d25e1c040f480491870c8ba',
founderKeyHashHex:
'cacedcee9bc28a37b36718ea210fcf7caac182cdb66cc17fafb6027478a221c8',
founderKeyHashInt: 2023891400,
projectIdDeriviation: 1011945700,
projectId: 'angor1qg2pvel3j8ka62dwvmlytv6hwkz7hmk2mms7qll',
nPub: 'c749d81fc42037e5b9f7b32ae266cbce75019ce6771be6877d3c509e97e39c14',
addressOnFeeOutput: 'tb1qg2pvel3j8ka62dwvmlytv6hwkz7hmk2m0ncfee',
txid: '00b78119bb6eff9f64b2d29948ddd830f405b18dfdb802a3ec2df4eacfcd1f40',
'68828edc1c6312c915c8967475be57f42d45764105af8216f2da7170d033240a',
founderKeyHashInt: 3493012490,
projectIdDeriviation: 1746506245,
projectId: 'angor1qzkfpckm2vnhdvfcwr7vdhwt7ns3rd95gr0age0',
nostrEventId: '0f2d8db8568bd3e12bdab1faa217fffc80459053967eff8bde0a65f14e2b7079',
addressOnFeeOutput: 'tb1quc59k4kt0nv62xhj72xtqfmzk55cexxmae8lyc',
txid: '0d28976a42bf7618ad9470cf0202e2eb06d6072e75e139eab012a160b7b480aa',
blockHeight: 40000,
};

Expand Down Expand Up @@ -154,11 +154,11 @@ describe('AngorTransactionDecoder', () => {

const {
projectId,
nPub,
addressOnFeeOutput,
founderKeyHex,
txid,
blockHeight,
nostrEventId
} = data;

await angorDecoder.decodeAndStoreProjectCreationTransaction(
Expand All @@ -168,12 +168,17 @@ describe('AngorTransactionDecoder', () => {

expect(setProjectSpy).toHaveBeenCalledWith(
projectId,
nPub,
addressOnFeeOutput,
transactionStatus,
founderKeyHex,
txid,
blockHeight
blockHeight,
nostrEventId
);

expect(updateInvestmentStatusSpy).toHaveBeenCalledWith(
addressOnFeeOutput,
transactionStatus
);

expect(updateInvestmentStatusSpy).toHaveBeenCalledWith(
Expand All @@ -188,10 +193,10 @@ describe('AngorTransactionDecoder', () => {
jest.restoreAllMocks();
});

it('should return 2 chunks', () => {
it('should return 3 chunks', () => {
const chunks = angorDecoder['decompileProjectCreationOpReturnScript']();

expect(chunks.length).toEqual(2);
expect(chunks.length).toEqual(3);

chunks.forEach((chunk) => expect(typeof chunk).toEqual('string'));
});
Expand Down Expand Up @@ -290,9 +295,9 @@ describe('AngorTransactionDecoder', () => {
});
});

describe('getNostrPubKey', () => {
it('should return Nostr public key', () => {
expect(angorDecoder['getNostrPubKey']()).toEqual(data.nPub);
describe('getNostrEventId', () => {
it('should return nostrEventId', () => {
expect(angorDecoder['getNostrEventId']()).toEqual(data.nostrEventId);
});
});
});
Expand Down Expand Up @@ -339,10 +344,10 @@ describe('AngorTransactionDecoder', () => {
.mockImplementation(() =>
Promise.resolve({
founder_key: '',
npub: '',
id: '',
created_on_block: 1,
txid: '',
nostr_event_id: ''
})
);

Expand Down
18 changes: 9 additions & 9 deletions backend/src/api/angor/angor.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { fetchAngorVouts, computeAdvancedStats, computeStatsTally } from './ango

interface ProjectsPayloadItem {
founderKey: string;
nostrPubKey: string;
projectIdentifier: string;
createdOnBlock: number;
trxId: string;
nostrEventId: string;
}
interface ProjectPayloadItem {
founderKey: string;
nostrPubKey: string;
projectIdentifier: string;
createdOnBlock: number;
trxId: string;
nostrEventId: string;
totalInvestmentsCount: number;
}

Expand Down Expand Up @@ -150,14 +150,14 @@ class AngorRoutes {
const payload: ProjectsPayloadItem[] = projects
.map((project) => ({
founderKey: project.founder_key,
nostrPubKey: project.npub,
projectIdentifier: project.id,
createdOnBlock: project.created_on_block,
trxId: project.txid,
nostrEventId: project.nostr_event_id
}))
.sort(
(p1: ProjectsPayloadItem, p2: ProjectsPayloadItem) =>
p1.createdOnBlock - p2.createdOnBlock
p2.createdOnBlock - p1.createdOnBlock
);

// Amount of confirmed Angor projects.
Expand Down Expand Up @@ -197,10 +197,10 @@ class AngorRoutes {
// Adjust DB data to confirm ProjectsPayloadItem interface.
const payload: ProjectPayloadItem = {
founderKey: project.founder_key,
nostrPubKey: project.npub,
projectIdentifier: project.id,
createdOnBlock: project.created_on_block,
trxId: project.txid,
nostrEventId: project.nostr_event_id,
totalInvestmentsCount: project.investments_count,
};

Expand Down Expand Up @@ -235,7 +235,6 @@ class AngorRoutes {
const spentVouts: AngorVout[][] = await Promise.all(
investments.map(async (investment) => {
//fetch transaction for each investment, with full info about vouts
console.log('investment: ', investment);
const fullTr = await transactionUtils.$getTransactionExtended(
investment.transaction_id,
true,
Expand Down Expand Up @@ -351,7 +350,6 @@ class AngorRoutes {
return;
}
}

// Angor project investments.
const projectInvestments =
await AngorProjectRepository.$getProjectInvestments(
Expand All @@ -361,15 +359,17 @@ class AngorRoutes {
);

// Adjust DB data to confirm ProjectInvestmentPayloadItem interface.
const payload: ProjectInvestmentPayloadItem[] = projectInvestments
const payload: ProjectInvestmentPayloadItem[] = projectInvestments.length > 0
? projectInvestments
.map((investment) => ({
investorPublicKey: investment.investor_npub,
totalAmount: investment.amount_sats,
transactionId: investment.transaction_id,
hashOfSecret: investment.secret_hash,
isSeeder: investment.is_seeder,
}))
.sort();
.sort()
: [];

// Amount of confirmed Angor project investments.
const investmentsCount =
Expand Down
3 changes: 1 addition & 2 deletions backend/src/api/database-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class DatabaseMigration {
*/
public async $initializeOrMigrateDatabase(): Promise<void> {
logger.debug('MIGRATIONS: Running migrations');

await this.$printDatabaseVersion();

// First of all, if the `state` database does not exist, create it so we can track migration version
Expand Down Expand Up @@ -1285,11 +1284,11 @@ class DatabaseMigration {
private getCreateAngorProjectsTableQuery(): string {
return `CREATE TABLE IF NOT EXISTS angor_projects (
id CHAR(45) NOT NULL,
npub CHAR(64) NOT NULL,
address_on_fee_output CHAR(51) NOT NULL,
creation_transaction_status VARCHAR(10) NOT NULL,
created_on_block INT(10),
txid VARCHAR(64) NOT NULL,
nostr_event_id VARCHAR(64),
founder_key VARCHAR(66) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;`;
Expand Down
Loading

0 comments on commit e5ff277

Please sign in to comment.