Skip to content

Commit

Permalink
feat: create ipa when create chapter
Browse files Browse the repository at this point in the history
  • Loading branch information
harisato committed Oct 17, 2024
1 parent d90b725 commit 4f47240
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table "public"."story_manga" alter column "story_ip_asset_id" set not null;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table "public"."story_manga" alter column "story_ip_asset_id" drop not null;
29 changes: 29 additions & 0 deletions src/modules/chapter/chapter.graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,35 @@ export class ChapterGraphql {
}
}

async queryUserAddressById(userId: string): Promise<string> {
const headers = {
'x-hasura-admin-secret': this.configService.get<string>(
'graphql.adminSecret'
),
};

const result = await this.graphqlSvc.query(
this.configService.get<string>('graphql.endpoint'),
'',
`query authorizer_users($id: bpchar!) {
authorizer_users(limit: 1, where: {id: {_eq: $id}}) {
active_wallet_address: active_evm_address
}
}`,
'authorizer_users',
{
id: userId,
},
headers
);

if (result.data.authorizer_users[0]?.active_wallet_address) {
return result.data.authorizer_users[0]?.active_wallet_address;
} else {
throw new NotFoundException('wallet address not found');
}
}

async adminCreateChapter(variables: any) {
const headers = {
'x-hasura-admin-secret': this.configService.get<string>(
Expand Down
82 changes: 66 additions & 16 deletions src/modules/chapter/chapter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import { CreatorService } from '../creator/creator.service';
import { StoryEventGraphql } from '../story-event/story-event.graphql';
import { IPFSService } from '../files/ipfs.service';
import { FilesService } from '../files/files.service';
import { StoryEventService } from '../story-event/story-event.service';
import { SubmissionType } from '../story-event/story-event.enum';
import { getBytes32FromIpfsHash } from '../story-event/utils';

@Injectable()
export class ChapterService {
Expand All @@ -43,7 +46,8 @@ export class ChapterService {
private uploadChapterService: UploadChapterService,
private configSvc: ConfigService,
private creatorService: CreatorService,
private storyEventService: StoryEventGraphql,
private storyEventGrapqh: StoryEventGraphql,
private storyEventService: StoryEventService,
private ipfsService: IPFSService,
private fileService: FilesService
) {}
Expand Down Expand Up @@ -134,7 +138,7 @@ export class ChapterService {
chapter_type,
pushlish_date,
status,
submission_id,
story_submission_id: submission_id,
thumbnail_url: newThumbnailUrl,
});

Expand All @@ -155,21 +159,24 @@ export class ChapterService {
});

if (submission_id) {
// query submission
const submission = await this.storyEventService.getSubmissionDetail({
id: submission_id,
});

// upload chapter images to ipfs
const ipfsDisplayUrl = this.configSvc.get<string>('network.ipfsQuery');

const ipfsImageFolder = `/punkga-manga-${manga_id}-chapter-${chapterId}/images`;
const { cid: chapterImagesCid } =
await this.ipfsService.uploadLocalFolderToIpfs(
storageFolder,
ipfsImageFolder
);
const ipfsFolderUrl = `${ipfsDisplayUrl}/${chapterImagesCid}`;
let viChapterImagesIpfsUrl;
let enChapterImagesIpfsUrl;
for (const chapter_language of chapter_images.chapter_languages) {
const ipfsImageFolder = `/punkga-manga-${manga_id}-chapter-${chapterId}/${chapter_language.language_id}/images`;
const { cid: chapterImagesCid } =
await this.ipfsService.uploadLocalFolderToIpfs(
`${storageFolder}/unzip/1`,
ipfsImageFolder
);
const ipfsFolderUrl = `${ipfsDisplayUrl}/${chapterImagesCid}`;
if (chapter_language.language_id === 1)
enChapterImagesIpfsUrl = ipfsFolderUrl;
if (chapter_language.language_id === 2)
viChapterImagesIpfsUrl = ipfsFolderUrl;
}

// upload nft image to ipfs
const thumbnail = files.filter((f) => f.fieldname === 'thumbnail')[0];
Expand All @@ -189,7 +196,10 @@ export class ChapterService {
description: `Punkga Story Event Manga - ${mangaMainTitle}`,
attributes: [
{
chapter_images: ipfsFolderUrl,
chapter_images: {
vi: viChapterImagesIpfsUrl,
en: enChapterImagesIpfsUrl,
},
},
],
image: thumbnailIpfs,
Expand All @@ -200,7 +210,47 @@ export class ChapterService {
`/metadata-${new Date().getTime()}`
);

const insertStoryMangaResult =
await this.storyEventGrapqh.insertStoryManga({
object: {
manga_id: manga_id,
ipfs_url: metadataCID,
},
});
if (insertStoryMangaResult.errors) return insertStoryMangaResult;
const storyMangaId =
insertStoryMangaResult.data.insert_story_manga_one.id;

// create job to mint and register ip_asset
// --- query submission
const submission = await this.storyEventGrapqh.getSubmissionDetail({
id: submission_id,
});
const character_ids = submission.data.manga_characters;
const queryStoryCharactersResult =
await this.storyEventGrapqh.queryStoryCharacters({
story_character_ids: character_ids.map(
(character) => character.story_character_id
),
});
const ipAssetIds = queryStoryCharactersResult.data.story_character.map(
(character) => character.story_ip_asset.ip_asset_id
);

const userWalletAddress =
await this.chapterGraphql.queryUserAddressById(userId);
const jobData = {
name: mangaMainTitle,
user_id: userId,
metadata_ipfs: `${ipfsDisplayUrl}/${metadataCID}`,
// story_artwork_id: storyArtworkId,
submission_id: submission_id,
user_wallet_address: userWalletAddress,
ip_asset_ids: ipAssetIds,
metadata_hash: getBytes32FromIpfsHash(metadataCID),
};

await this.storyEventService.addEventJob(SubmissionType.Manga, jobData);
}

// remove files
Expand Down Expand Up @@ -432,7 +482,7 @@ export class ChapterService {
chapter_type,
pushlish_date,
status,
submission_id,
story_submission_id: submission_id,
thumbnail_url:
newThumbnailUrl !== '' ? newThumbnailUrl : thumbnail_url,
},
Expand Down
154 changes: 114 additions & 40 deletions src/modules/story-event/story-event.consumer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,51 +190,46 @@ export class StoryEventConsumer implements OnModuleInit {
}
}

async createStoryMangaIpAsset(data: any) {}

async createStoryArtworkIpAsset(data: any) {
async createStoryMangaIpAsset(data: any) {
try {
// mint nft & create ipa
const { ipAssetId, nftId, hash } =
await this.mintAndRegisterIpAndMakeDerivative(data);

const args = [
[
data.ip_asset_ids,
'0x8bb1ade72e21090fc891e1d4b88ac5e57b27cb31',
defaultPILTerms.licenseTermsIds,
defaultPILTerms.royaltyContext,
],
[
data.metadata_ipfs,
data.metadata_hash,
data.metadata_ipfs,
data.metadata_hash,
],
data.user_wallet_address,
];

const address =
`${this.storyChain.contracts.story_event_derivative_contract}` as any;

const hash = await this.walletClient.writeContract({
abi: storyEventDerivativeAbi,
address,
functionName: 'mintAndRegisterIpAndMakeDerivative',
args,
chain: iliad,
account: this.account,
});
const txReceipt = (await this.publicClient.waitForTransactionReceipt({
hash,
})) as any;

const targetLogs = parseTxIpRegisteredEvent(txReceipt);
// // --- update story artwork set story_ip_id
// const updateStoryArtworkResult =
// await this.storyEventGraphql.updateStoryArtwork({
// id: data.story_artwork_id,
// story_ip_asset_id:
// insertStoryIPAResult.data.insert_story_ip_asset_one.id,
// });
// if (updateStoryArtworkResult.errors) {
// this.logger.error(
// `Update story artwork error: ${JSON.stringify(
// updateStoryArtworkResult
// )}`
// );
// throw new InternalServerErrorException('Update story artwork failed ');
// }

let nftId = 0;
let ipAssetId = targetLogs[0].ipId;
this.logger.log(
`Create Artwork IP Asset Done: ipid ${ipAssetId} hash ${hash}`
);
} catch (error) {
this.logger.error(error.message);
return {
errors: {
message: error.message,
},
};
}
}

if (txReceipt.logs[0].topics[3]) {
nftId = parseInt(txReceipt.logs[0].topics[3], 16);
}
async createStoryArtworkIpAsset(data: any) {
try {
// mint nft & create ipa
const { ipAssetId, nftId, hash } =
await this.mintAndRegisterIpAndMakeDerivative(data);

// update offchain data
// --- insert story ip asset
Expand Down Expand Up @@ -294,4 +289,83 @@ export class StoryEventConsumer implements OnModuleInit {
};
}
}

async mintAndRegisterIpAndMakeDerivative(data: any) {
const args = [
[
data.ip_asset_ids,
'0x8bb1ade72e21090fc891e1d4b88ac5e57b27cb31',
defaultPILTerms.licenseTermsIds,
defaultPILTerms.royaltyContext,
],
[
data.metadata_ipfs,
data.metadata_hash,
data.metadata_ipfs,
data.metadata_hash,
],
data.user_wallet_address,
];

const address =
`${this.storyChain.contracts.story_event_derivative_contract}` as any;

const hash = await this.walletClient.writeContract({
abi: storyEventDerivativeAbi,
address,
functionName: 'mintAndRegisterIpAndMakeDerivative',
args,
chain: iliad,
account: this.account,
});
const txReceipt = (await this.publicClient.waitForTransactionReceipt({
hash,
})) as any;

const targetLogs = parseTxIpRegisteredEvent(txReceipt);

let nftId = 0;
let ipAssetId = targetLogs[0].ipId;

if (txReceipt.logs[0].topics[3]) {
nftId = parseInt(txReceipt.logs[0].topics[3], 16);
}

// update offchain data
// --- insert story ip asset
const insertStoryIPAResult = await this.storyEventGraphql.insertStoryIPA({
object: {
ip_asset_id: ipAssetId,
nft_contract_address: this.storyChain.contracts.story_event_contract,
nft_token_id: nftId.toString(),
tx_hash: hash,
user_id: data.user_id,
},
});
if (insertStoryIPAResult.errors) {
this.logger.error(
`Insert story IP Asset error: ${JSON.stringify(insertStoryIPAResult)}`
);
throw new InternalServerErrorException('Insert story IP Asset failed ');
}

// --- update submission set status = done
const updateSubmissionResult =
await this.storyEventGraphql.updateSubmission({
id: data.submission_id,
status: SubmissionStatus.Approved,
});
if (updateSubmissionResult.errors) {
this.logger.error(
`Update submission error: ${JSON.stringify(updateSubmissionResult)}`
);
throw new InternalServerErrorException('Update submission failed ');
}

return {
ipAssetId,
nftId,
hash,
};
}
}
21 changes: 21 additions & 0 deletions src/modules/story-event/story-event.graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,27 @@ export class StoryEventGraphql {
);
}

async insertStoryManga(variables: any) {
const headers = {
'x-hasura-admin-secret': this.configSvc.get<string>(
'graphql.adminSecret'
),
};

return this.graphqlSvc.query(
this.configSvc.get<string>('graphql.endpoint'),
'',
`mutation insert_story_manga($object: story_manga_insert_input = {}) {
insert_story_manga_one(object: $object) {
id
}
}`,
'insert_story_manga',
variables,
headers
);
}

async queryStoryCharacters(variables: any) {
const headers = {
'x-hasura-admin-secret': this.configSvc.get<string>(
Expand Down
Loading

0 comments on commit 4f47240

Please sign in to comment.