Skip to content

Commit

Permalink
feat:add klayr blockchain support
Browse files Browse the repository at this point in the history
  • Loading branch information
reyraa committed Dec 6, 2024
1 parent 31f7723 commit 0f1ab8a
Show file tree
Hide file tree
Showing 17 changed files with 1,589 additions and 1,334 deletions.
4 changes: 2 additions & 2 deletions config/middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export default [
export default ({ env }) => [
'strapi::logger',
'strapi::errors',
'strapi::session',
{
name: 'strapi::cors',
config: {
origin: ['http://localhost:3000'], // @todo use env vars
origin: env.array('CORS_ORIGINS', ['http://localhost:3000']),
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
headers: ['Content-Type', 'Authorization'],
credentials: true,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
"lint": "eslint"
},
"dependencies": {
"@coral-xyz/anchor": "^0.30.1",
"@solana/web3.js": "^1.95.3",
"@liskhq/lisk-db": "^0.3.10",
"@strapi/plugin-cloud": "4.25.1",
"@strapi/plugin-i18n": "4.25.1",
"@strapi/plugin-users-permissions": "^4.25.6",
"@strapi/strapi": "4.25.1",
"better-sqlite3": "8.6.0",
"crypto": "^1.0.1",
"klayr-sdk": "^6.1.3",
"pg": "^8.12.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
Expand Down
13 changes: 12 additions & 1 deletion src/api/contribution-tier/controllers/contribution-tier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default factories.createCoreController(
// POST
async create(ctx) {
const { user } = ctx.state;
const { project, amount } = ctx.request.body;
let entityId: string = '';

try {
Expand All @@ -33,7 +34,17 @@ export default factories.createCoreController(
);

if (wallet.length === 1) {
// @todo Inform the blockchain app
const txResult = await strapi.service('api::contribution-tier.contribution-tier').registerOnChain({
account: wallet[0],
tierId: entityId,
campaignId: project.id,
amount,
});

// Check funding progress and update the project status
if (!txResult.success) {
throw new Error('Could not register contribution on network');
}

return result;
} else {
Expand Down
20 changes: 13 additions & 7 deletions src/api/contribution-tier/services/contribution-tier.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
/**
* contribution-tier service
*/

import { factories } from '@strapi/strapi';
import { createTransaction, Commands } from '../../../utils/network';

export default factories.createCoreService('api::contribution-tier.contribution-tier', () => ({
async registerOnChain({ account, tierId, campaignId, amount }) {
const params = {
campaignId,
tierId,
amount,
};

export default factories.createCoreService(
'api::contribution-tier.contribution-tier',
);
const txResult = await createTransaction(Commands.Create, params, account);
return txResult;
},
}));
9 changes: 8 additions & 1 deletion src/api/contribution/controllers/contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,16 @@ export default factories.createCoreController(
);

if (wallet.length === 1) {
// @todo Inform the blockchain app
const txResult = await strapi.service('api::contribution.contribution').registerOnChain({
account: wallet[0],
tierId: tier.id,
campaignId: project.id,
});

// Check funding progress and update the project status
if (!txResult.success) {
throw new Error('Could not register contribution on network');
}
} else {
throw new Error('Could not find associated wallet');
}
Expand Down
17 changes: 12 additions & 5 deletions src/api/contribution/services/contribution.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/**
* contribution service
*/

import { factories } from '@strapi/strapi';
import { createTransaction, Commands } from '../../../utils/network';

export default factories.createCoreService('api::contribution.contribution', () => ({
async registerOnChain({ account, tierId, campaignId }) {
const params = {
campaignId,
tierId,
};

export default factories.createCoreService('api::contribution.contribution');
const txResult = await createTransaction(Commands.Create, params, account);
return txResult;
},
}));
20 changes: 10 additions & 10 deletions src/api/profile/services/profile.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { factories } from '@strapi/strapi';
import { Keypair } from '@solana/web3.js';
import { passphrase, cryptography } from 'klayr-sdk';
import crypto from 'crypto';

import { encryptPrivateKey, decryptPrivateKey } from '../../../utils/crypto'; // Import updated

import { encryptPrivateKey } from '../../../utils/crypto';
import { SupportedBlockchains } from '../../../utils/types';
import { DERIVATION_PATH } from '../../../utils/network';

export default factories.createCoreService('api::profile.profile', {
addProfileAndWallet: async (userId: string) => {
Expand All @@ -24,15 +23,17 @@ export default factories.createCoreService('api::profile.profile', {
});

// Create a wallet for the new user
const wallet = Keypair.generate();
const phrase = passphrase.Mnemonic.generateMnemonic();
const privateKey = await cryptography.ed.getPrivateKeyFromPhraseAndPath(phrase, DERIVATION_PATH)
const publicKey = cryptography.ed.getPublicKeyFromPrivateKey(privateKey);
const address = cryptography.address.getKlayr32AddressFromPublicKey(publicKey);
const iv = crypto.randomBytes(16);
const privateKey = wallet.secretKey;
await strapi.entityService.create('api::wallet.wallet', {
data: {
public_key: wallet.publicKey.toString(),
public_key: publicKey.toString('hex'),
encrypted_private_key: encryptPrivateKey(privateKey, iv),
address: wallet.publicKey.toBase58(),
blockchain: SupportedBlockchains.Solana,
address: address,
blockchain: SupportedBlockchains.Klayr,
encryption_metadata: {},
users_permissions_user: userId,
createdAt: now,
Expand All @@ -43,7 +44,6 @@ export default factories.createCoreService('api::profile.profile', {
strapi.log.info(`Profile and wallet created for user ${userId}`);
} catch (error) {
strapi.log.error('Error during profile or wallet creation:', error);
// @todo we should delete the user and inform the client about the error
}
},
});
49 changes: 30 additions & 19 deletions src/api/project/controllers/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,21 @@ export default factories.createCoreController(
);

if (wallet.length === 1) {
// @todo Inform the blockchain app

let txResult;
if (ctx.request.body.data.status === ProjectStatus.Published) {
// @todo Sign and send
txResult = await strapi.service('api::project.project').publish({
account: wallet[0],
campaignId: id,
});
// Check funding progress and update the project status
} else if (ctx.request.body.data.status === ProjectStatus.Withdrawn) {
// @todo Sign and send
txResult = await strapi.service('api::project.project').payout({
account: wallet[0],
campaignId: id,
});
}
if (!txResult.success) {
throw new Error('Could not register contribution on network');
}
} else {
// @todo ridi
Expand All @@ -343,6 +352,7 @@ export default factories.createCoreController(
// POST
async create(ctx) {
const { user } = ctx.state;
let entityId;

try {
const { data } = ctx.is('multipart')
Expand All @@ -363,6 +373,7 @@ export default factories.createCoreController(

// Proceed with creating the the project
const result = await super.create(ctx);
entityId = result.data.id;
// find user's sk to sign and send TX to solana program
const wallet = await strapi.entityService.findMany(
'api::wallet.wallet',
Expand All @@ -373,28 +384,28 @@ export default factories.createCoreController(
},
);


if (wallet.length === 1) {
try {
// @todo Inform the blockchain app
console.log(
'Project campaign successfully created on the blockchain',
);
} catch (blockchainError) {
// If the smart contract interaction was unsuccessful, delete the recently created project in Strapi
await strapi.entityService.delete(
'api::project.project',
result.data.id,
);
throw new Error(
`Blockchain transaction failed. Error: ${blockchainError.message}`,
);
const txResult = await strapi.service('api::project.project').createCampaign({
account: wallet[0],
softGoal: data.soft_goal,
hardGoal: data.hard_goal,
deadline: data.deadline,
apiId: entityId,
});

// Check funding progress and update the project status
if (!txResult.transactionId) {
throw new Error('Could not register contribution on network');
}
} else {
throw new Error('Wallet not found');
}

return result;
} catch (err) {
// const id = err.message.split('Error transaction:')[1]
// If the smart contract interaction was unsuccessful, delete the recently created project in Strapi
await strapi.entityService.delete('api::project.project', entityId);
ctx.throw(500, err);
}
},
Expand Down
115 changes: 33 additions & 82 deletions src/api/project/services/project.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,34 @@
/**
* project service
*/

import { factories } from '@strapi/strapi';

export default factories.createCoreService('api::project.project');

// import { factories } from '@strapi/strapi';
// import { Program, AnchorProvider, web3, utils, BN, setProvider } from "@coral-xyz/anchor"
// import { PublicKey, TransactionInstruction } from '@solana/web3.js';
// import { Buffer } from 'buffer';

// // Make sure to load your program IDL and program key
// import idl from './crowdfunding.json';
// import { Crowdfunding } from './crowdfunding';
// import { publicKey } from '@coral-xyz/anchor/dist/cjs/utils';

// const idl_string= JSON.stringify(idl);
// const idl_object = JSON.parse(idl_string);
// const programId = new PublicKey(idl.address);
// const { SystemProgram } = web3;

// export default factories.createCoreService('api::project.project', ({ strapi }) => ({
// async createProject(projectIdInput: number, softCapInput: number, hardCapInput: number, deadlineInput: number) {
// try {

// // Setup provider and program
// const provider = AnchorProvider.env();
// const program = new Program(idl, new PublicKey('6BsMtttdteCnV3b6XmxTiLS9VQfb57yu7cRH8SKfP4u3'), provider);

// const owner = provider.wallet.publicKey;
// const muzikieAddress = new PublicKey("3fh3nfHi22i93zq971bJFEC5o1NCaQYND4g33yMQS2ko");

// // Convert inputs to anchor types (BN, etc.)
// const projectId = new BN(projectIdInput);
// const softCap = new BN(softCapInput);
// const hardCap = new BN(hardCapInput);
// const deadline = new BN(deadlineInput);

// // Find the program derived address (PDA) for the project
// const [projectPDA] = await PublicKey.findProgramAddress(
// [Buffer.from(projectId.toArrayLike(Buffer, 'le', 8))],
// program.programId
// );

// // Build the transaction instruction to create the project
// const tx = new web3.Transaction();
// tx.add(
// program.instruction.initProject(
// projectId,
// softCap,
// hardCap,
// deadline,
// owner,
// muzikieAddress,
// {
// accounts: {
// owner: owner,
// projectState: projectPDA,
// systemProgram: SystemProgram.programId,
// },
// }
// )
// );

// // Send the transaction
// const signature = await provider.sendAndConfirm(tx);

// // Log the transaction signature and return the project PDA
// console.log('Transaction signature', signature);

// return {
// message: 'Project created successfully',
// projectPDA: projectPDA.toString(),
// signature: signature,
// };
// } catch (error) {
// strapi.log.error('Error creating project on Solana blockchain:', error);
// throw new Error('Failed to create project on blockchain.');
// }
// }
// }));
import { createTransaction, Commands } from '../../../utils/network';

export default factories.createCoreService('api::project.project', () => ({
async createCampaign({ account, softGoal, hardGoal, deadline, apiId }) {
const params = {
apiId,
softGoal,
hardGoal,
deadline,
};

const txResult = await createTransaction(Commands.Create, params, account);
return txResult;
},

async publish({ account, campaignId }) {
const params = {
campaignId,
};

const txResult = await createTransaction(Commands.Publish, params, account);
return txResult;
},

async payout({ account, campaignId }) {
const params = {
campaignId,
};

const txResult = await createTransaction(Commands.Payout, params, account);
return txResult;
},
}));
6 changes: 6 additions & 0 deletions src/utils/network/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const chainID = Buffer.from(process.env.CHAIN_ID as string, 'hex');
export enum endpoints {
getAuthAccount ='auth_getAuthAccount',
postTransaction ='txpool_postTransaction',
};
export const DERIVATION_PATH = "m/44'/134'/0";
Loading

0 comments on commit 0f1ab8a

Please sign in to comment.