Skip to content

Done#27

Merged
toaru005 merged 1 commit intoEditfrom
Feature-toaru/be-6-sendtoken
Feb 17, 2026
Merged

Done#27
toaru005 merged 1 commit intoEditfrom
Feature-toaru/be-6-sendtoken

Conversation

@toaru005
Copy link
Contributor

まだ変更予定あり

まだ変更予定あり
Copilot AI review requested due to automatic review settings February 17, 2026 11:56
@toaru005 toaru005 merged commit 65754fb into Edit Feb 17, 2026
5 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds cryptocurrency token transfer functionality to the application, including tools for creating Symbol blockchain transactions, AES encryption/decryption for private keys, and API routes for sending tokens and checking token balances. However, the PR description indicates "まだ変更予定あり" (still planning changes), and the title is simply "Done", which suggests this may be a work-in-progress.

Changes:

  • Added blockchain transaction creation tools (CreateTransferTx, SignAndAnnounce reference)
  • Added AES-256-GCM encryption/decryption utilities for secure private key storage
  • Added API routes for token transfers and balance queries
  • Added utility to fetch remaining token balances from Symbol blockchain

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 15 comments.

File Description
Backend/Workspace/Tools/LeftToken.js Fetches account token balances from Symbol blockchain node
Backend/Workspace/Tools/CreateTransferTx.js Creates Symbol blockchain transfer transactions with mosaics
Backend/Workspace/Tools/AESControl.js Provides AES-256-GCM encryption/decryption for sensitive data
Backend/Workspace/Routes/SendToken.js API routes for sending tokens and querying balances

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const DBPerf = require('../Tools/DBPref');
const VCM = require('../Tools/VerifyCookieMiddleware');
const LeftToken = require('../Tools/LeftToken');
const decrypt = require('../Tools/AESControl');
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The require statement only imports the 'decrypt' function from AESControl.js, but the module exports both 'encrypt' and 'decrypt' as named exports in an object. The correct import should be: const { decrypt } = require('../Tools/AESControl'); or const AESControl = require('../Tools/AESControl'); followed by AESControl.decrypt()

Suggested change
const decrypt = require('../Tools/AESControl');
const { decrypt } = require('../Tools/AESControl');

Copilot uses AI. Check for mistakes.
// パスワードを照合して認証
const decryptedPrivateKey = decrypt(password , privateKey);

const {tx, keyPair, facade} = CreateTransferTx({
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destructured variable 'tx' does not match the exported property name from CreateTransferTx. According to the CreateTransferTx.js file (line 74-78), the function returns an object with properties 'createTransferTx', 'keyPair', and 'facade'. This should be: const {createTransferTx, keyPair, facade} = CreateTransferTx({...})

Copilot uses AI. Check for mistakes.
const NODE_URL = 'https://sym-test-01.opening-line.jp:3001';
// 実際に署名とアナウンスを行う
await SignAndAnnounce(tx, keyPair, facade, NODE_URL);
// Shutdown Log
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable 'logOwner' is undefined. It is used in this log statement but was never declared in the function scope. This will cause a ReferenceError at runtime.

Suggested change
// Shutdown Log
// Shutdown Log
const logOwner = 'SendToken';

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +70
console.log(`[${logOwner}] Output => createTransferTx: \n${{
type: createTransferTx.type,
recipientAddress: createTransferTx.recipientAddress,
mosaics: createTransferTx.mosaics,
message: createTransferTx.message,
deadline: createTransferTx.deadline,
}}`);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The console.log statement attempts to log an object literal instead of using JSON.stringify. This will output '[object Object]' instead of the actual transaction details. Based on the comment on line 63, this should use JSON.stringify: console.log([${logOwner}] Output => createTransferTx: \n${JSON.stringify({...}, null, 2)})

Suggested change
console.log(`[${logOwner}] Output => createTransferTx: \n${{
type: createTransferTx.type,
recipientAddress: createTransferTx.recipientAddress,
mosaics: createTransferTx.mosaics,
message: createTransferTx.message,
deadline: createTransferTx.deadline,
}}`);
console.log(`[${logOwner}] Output => createTransferTx: \n${JSON.stringify({
type: createTransferTx.type,
recipientAddress: createTransferTx.recipientAddress,
mosaics: createTransferTx.mosaics,
message: createTransferTx.message,
deadline: createTransferTx.deadline,
}, null, 2)}`);

Copilot uses AI. Check for mistakes.
const MosaicID = mosaicID[0].MosaicID;

// パスワードを照合して認証
const decryptedPrivateKey = decrypt(password , privateKey);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential security issue: The decrypt function is called with the password and privateKey, but there's no verification that the decryption was successful before using the decryptedPrivateKey. If the password is incorrect or the privateKey is corrupted, the decrypt function might return invalid data that could cause issues downstream. Consider validating the decrypted result before use.

Suggested change
const decryptedPrivateKey = decrypt(password , privateKey);
let decryptedPrivateKey;
try {
decryptedPrivateKey = decrypt(password, privateKey);
} catch (error) {
console.error("秘密鍵の復号に失敗しました:", error);
return res.status(500).send({ message: 'Failed to decrypt private key' });
}
if (!decryptedPrivateKey || typeof decryptedPrivateKey !== 'string') {
console.error("秘密鍵の復号結果が無効です");
return res.status(400).send({ message: 'Invalid decrypted private key' });
}

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +73
const decryptedPrivateKey = decrypt(password , privateKey);

const {tx, keyPair, facade} = CreateTransferTx({
networkType: 'testnet',
senderPrivateKey: decryptedPrivateKey,
recipientRawAddress: SendToAddress,
message: `Send ${Amount} tokens to ${sendtoUserID}`,
mosaics: [
{
mosaicId: MosaicID,
amount: BigInt(Amount) * 1_000_000n }
],
deadlineHours: 2,
})

// 署名とアナウンス
// NODEの定義
const NODE_URL = 'https://sym-test-01.opening-line.jp:3001';
// 実際に署名とアナウンスを行う
await SignAndAnnounce(tx, keyPair, facade, NODE_URL);
// Shutdown Log
console.log(`[${logOwner}] Shutdown!`);

return res.status(200).json({ message: "OK: Send Successful"});
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no error handling around the CreateTransferTx function call and the SignAndAnnounce call. If these functions throw errors (e.g., due to invalid parameters or network issues), the route handler will crash without providing a helpful error message to the client. This makes debugging difficult and provides a poor user experience. Consider wrapping these operations in a try-catch block.

Suggested change
const decryptedPrivateKey = decrypt(password , privateKey);
const {tx, keyPair, facade} = CreateTransferTx({
networkType: 'testnet',
senderPrivateKey: decryptedPrivateKey,
recipientRawAddress: SendToAddress,
message: `Send ${Amount} tokens to ${sendtoUserID}`,
mosaics: [
{
mosaicId: MosaicID,
amount: BigInt(Amount) * 1_000_000n }
],
deadlineHours: 2,
})
// 署名とアナウンス
// NODEの定義
const NODE_URL = 'https://sym-test-01.opening-line.jp:3001';
// 実際に署名とアナウンスを行う
await SignAndAnnounce(tx, keyPair, facade, NODE_URL);
// Shutdown Log
console.log(`[${logOwner}] Shutdown!`);
return res.status(200).json({ message: "OK: Send Successful"});
try {
const decryptedPrivateKey = decrypt(password , privateKey);
const {tx, keyPair, facade} = CreateTransferTx({
networkType: 'testnet',
senderPrivateKey: decryptedPrivateKey,
recipientRawAddress: SendToAddress,
message: `Send ${Amount} tokens to ${sendtoUserID}`,
mosaics: [
{
mosaicId: MosaicID,
amount: BigInt(Amount) * 1_000_000n }
],
deadlineHours: 2,
});
// 署名とアナウンス
// NODEの定義
const NODE_URL = 'https://sym-test-01.opening-line.jp:3001';
// 実際に署名とアナウンスを行う
await SignAndAnnounce(tx, keyPair, facade, NODE_URL);
// Shutdown Log
console.log(`[${logOwner}] Shutdown!`);
return res.status(200).json({ message: "OK: Send Successful"});
} catch (err) {
console.error("Error in SendToken:", err);
return res.status(500).json({ message: "Error sending token" });
}

Copilot uses AI. Check for mistakes.
networkType: 'testnet',
senderPrivateKey: decryptedPrivateKey,
recipientRawAddress: SendToAddress,
message: `Send ${Amount} tokens to ${sendtoUserID}`,
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message parameter in CreateTransferTx is incorrectly named. According to the function signature in CreateTransferTx.js (line 32), the parameter is named 'messageText', but here it's being passed as 'message'. This will result in the messageText defaulting to an empty string instead of using the intended message.

Suggested change
message: `Send ${Amount} tokens to ${sendtoUserID}`,
messageText: `Send ${Amount} tokens to ${sendtoUserID}`,

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +46
const roomName = await DBPerf("送金するルームの名前の抽出", "SELECT RoomName FROM Rooms WHERE userID = ?", [fromUserID]);
const mosaicName = await DBPerf("MosaicIDの抽出", "SELECT MosaicName FROM RoomDetails WHERE RoomName = ?", [roomName[0].RoomName]);
const mosaicID = await DBPerf("MosaicIDの抽出", "SELECT MosaicID FROM Mosaics WHERE MosaicName = ?", [mosaicName[0].MosaicName]);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for database query results. If the database query returns an empty result on line 44 or lines 45-46, accessing roomName[0].RoomName or mosaicName[0].MosaicName will throw a TypeError. Add validation to check if the query results are not empty before accessing array elements.

Suggested change
const roomName = await DBPerf("送金するルームの名前の抽出", "SELECT RoomName FROM Rooms WHERE userID = ?", [fromUserID]);
const mosaicName = await DBPerf("MosaicIDの抽出", "SELECT MosaicName FROM RoomDetails WHERE RoomName = ?", [roomName[0].RoomName]);
const mosaicID = await DBPerf("MosaicIDの抽出", "SELECT MosaicID FROM Mosaics WHERE MosaicName = ?", [mosaicName[0].MosaicName]);
const roomName = await DBPerf("送金するルームの名前の抽出", "SELECT RoomName FROM Rooms WHERE userID = ?", [fromUserID]);
if (!roomName || roomName.length === 0) {
console.log("送金に使用するルームが見つかりません!");
return res.status(400).send({ message: 'Room information not found for user' });
}
const mosaicName = await DBPerf("MosaicIDの抽出", "SELECT MosaicName FROM RoomDetails WHERE RoomName = ?", [roomName[0].RoomName]);
if (!mosaicName || mosaicName.length === 0) {
console.log("送金に使用するMosaicNameが見つかりません!");
return res.status(400).send({ message: 'Mosaic name not found for room' });
}
const mosaicID = await DBPerf("MosaicIDの抽出", "SELECT MosaicID FROM Mosaics WHERE MosaicName = ?", [mosaicName[0].MosaicName]);
if (!mosaicID || mosaicID.length === 0) {
console.log("送金に使用するMosaicIDが見つかりません!");
return res.status(400).send({ message: 'Mosaic ID not found for mosaic name' });
}

Copilot uses AI. Check for mistakes.
// NODEの定義
const NODE_URL = 'https://sym-test-01.opening-line.jp:3001';
// 実際に署名とアナウンスを行う
await SignAndAnnounce(tx, keyPair, facade, NODE_URL);
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable 'tx' used here is incorrectly destructured on line 52. It should be 'createTransferTx' to match the return value from the CreateTransferTx function.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +10
const SignAndAnnounce = require('../Tools/SignAndAnnounce');

Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module '../Tools/SignAndAnnounce' is imported but does not exist in the codebase. This will cause a module not found error at runtime. The SignAndAnnounce.js file needs to be created or the import path needs to be corrected.

Suggested change
const SignAndAnnounce = require('../Tools/SignAndAnnounce');
const https = require('https');
/**
* 署名とアナウンスを行うヘルパー関数
* @param {Object} tx - 作成済みトランザクション
* @param {Object} keyPair - 送信者のキーペア
* @param {Object} facade - ネットワーク用ファサード
* @param {string} nodeUrl - ノードのベースURL (: https://sym-test-01.opening-line.jp:3001)
*/
async function SignAndAnnounce(tx, keyPair, facade, nodeUrl) {
// トランザクションに署名
const signedTx = facade.signTransaction(keyPair, tx);
// 署名済みトランザクションオブジェクトからペイロードを抽出
const payload =
(signedTx && (signedTx.payload || signedTx.serializedTransaction || signedTx.serialized)) ||
signedTx;
const body = JSON.stringify({ payload });
// /transactions エンドポイントへアナウンス
const url = new URL('/transactions', nodeUrl);
await new Promise((resolve, reject) => {
const req = https.request(
url,
{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
},
},
(res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve();
} else {
reject(
new Error(
`Failed to announce transaction: ${res.statusCode} ${data}`
)
);
}
});
}
);
req.on('error', (err) => {
reject(err);
});
req.write(body);
req.end();
});
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants