Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add benfen&ton&sui&sol token transfer #266

Merged
merged 1 commit into from
Dec 16, 2024
Merged
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
109 changes: 105 additions & 4 deletions packages/example/components/chains/benfen/example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { WalletWithRequiredFeatures } from '@benfen/bfc.js/dist/cjs/wallet-stand
function Example() {
const client = useBenfenClient();
const { setProvider } = useWallet();
const [customToAddress, setCustomToAddress] = useState<string>('');
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议验证 customToAddress 的输入

在使用 customToAddress 时,请确保对其进行输入验证,防止无效地址导致的错误。


const currentAccount = useCurrentAccount();
const { isConnected } = useCurrentWallet();
Expand All @@ -64,10 +65,14 @@ function Example() {
return params.signTransaction(currentAccount?.address ?? '');
}, [currentAccount?.address]);

const signTokenTransactionParams = useMemo(() => {
return params.signTokenTransaction(currentAccount?.address ?? '');
}, [currentAccount?.address]);

return (
<>
<ApiGroup title="SignMessage">
<ApiPayload
{/* <ApiPayload
title="signMessage"
description="签名消息, signMessage 不安全已经弃用, 目前(OneKey、Suiet、Sui Wallet、Martian) signMessage 实际实现已经变成了 signPersonalMessage"
presupposeParams={params.signMessage}
Expand All @@ -94,11 +99,11 @@ function Example() {
bytesToHex(currentAccount.publicKey) === bytesToHex(publicKey.toRawBytes())
).toString();
}}
/>
/> */}

<ApiPayload
title="signPersonalMessage"
description="签名消息(SDK 验证依赖网络可能会失败,可以刷新网页重试 或 稍后重试,问题上下文 https://github.com/MystenLabs/sui/issues/17912#issuecomment-2166621747)"
description="签名消息(SDK 验证依赖网络可能失败,可以刷新网页重试 或 稍后重试,问题上下文 https://github.com/MystenLabs/sui/issues/17912#issuecomment-2166621747)"
presupposeParams={params.signPersonalMessage}
onExecute={async (request: string) => {
const res = await signPersonalMessage({
Expand Down Expand Up @@ -196,7 +201,7 @@ function Example() {
const transfer = new TransactionBlock();
transfer.setSender(from);
const [coin] = transfer.splitCoins(transfer.gas, [transfer.pure(amount)]);
transfer.transferObjects([coin], transfer.pure(to));
transfer.transferObjects([coin], transfer.pure(customToAddress || to));

const tx = await sponsorTransaction(
client,
Expand Down Expand Up @@ -276,6 +281,102 @@ function Example() {
).toString();
}}
/>

<ApiPayload
title="signTransactionBlock (BUSD)"
description="BUSD代币转账签名"
presupposeParams={signTokenTransactionParams}
onExecute={async (request: string) => {
const { from, to, amount ,token} = JSON.parse(request);

const transfer = new TransactionBlock();
transfer.setSender(from);

const { data: coins } = await client.getCoins({
owner: from,
coinType: token,
});

if (!coins.length) {
throw new Error('No BUSD coins found');
}

const [coin] = transfer.splitCoins(
transfer.object(coins[0].coinObjectId),
[transfer.pure(amount)]
);
transfer.transferObjects([coin], transfer.pure(to));

const tx = await sponsorTransaction(
client,
from,
await transfer.build({
client,
onlyTransactionKind: true,
}),
);

const res = await signTransactionBlock({
transactionBlock: tx,
account: currentAccount,
});
return JSON.stringify(res);
}}
onValidate={async (request: string, result: string) => {
const { transactionBlockBytes, signature } = JSON.parse(result);
const publicKey = await verifyTransactionBlock(
Buffer.from(transactionBlockBytes, 'base64'),
signature,
);

return (
bytesToHex(currentAccount.publicKey) === bytesToHex(publicKey.toRawBytes())
).toString();
}}
/>
Comment on lines +285 to +336
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议添加输入验证和错误处理

在处理 fromtoamounttoken 参数时,请添加输入验证,确保参数有效。此外,考虑在余额不足时提供友好的提示信息。


<ApiPayload
title="signAndExecuteTransactionBlock (BUSD)"
description="BUSD代币转账签名并执行"
presupposeParams={signTokenTransactionParams}
onExecute={async (request: string) => {
const { from, to, amount, token } = JSON.parse(request);

const transfer = new TransactionBlock();
transfer.setSender(from);

const { data: coins } = await client.getCoins({
owner: from,
coinType: token,
});

if (!coins.length) {
throw new Error('No BUSD coins found');
}

const [coin] = transfer.splitCoins(
transfer.object(coins[0].coinObjectId),
[transfer.pure(BigInt(amount))]
);
transfer.transferObjects([coin], transfer.pure(to));

const tx = await sponsorTransaction(
client,
from,
await transfer.build({
client,
onlyTransactionKind: true,
}),
);

const res = await signAndExecuteTransactionBlock({
transactionBlock: tx,
account: currentAccount,
});

return JSON.stringify(res);
}}
/>
Comment on lines +338 to +379
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议添加输入验证和错误处理

在处理 fromtoamounttoken 参数时,请添加输入验证,确保参数有效。此外,考虑在余额不足时提供友好的提示信息。

</ApiGroup>

<DappList dapps={dapps} />
Expand Down
12 changes: 12 additions & 0 deletions packages/example/components/chains/benfen/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,16 @@ export default {
}),
},
],
signTokenTransaction: (address: string) => [
{
id: 'signUSDTransaction',
name: 'BUSD_TYPE',
value: JSON.stringify({
from: address,
to: address,
amount: 1000, // 0.000001 USD
token: '0x00000000000000000000000000000000000000000000000000000000000000c8::busd::BUSD'
}),
},
],
};
39 changes: 39 additions & 0 deletions packages/example/components/chains/solana/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
TransactionMessage,
VersionedMessage,
VersionedTransaction,
Connection,
} from '@solana/web3.js';
import { TOKEN_PROGRAM_ID, createTransferInstruction, getAssociatedTokenAddress } from '@solana/spl-token';

export const createTransferTransaction = (
publicKey: PublicKey,
Expand Down Expand Up @@ -54,3 +56,40 @@ export const createVersionedTransaction = (
const transaction = new VersionedTransaction(messageV0);
return transaction;
};

// 将 async function 改为箭头函数形式的导出
export const createTokenTransferTransaction = async (
Comment on lines +60 to +61
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

注意:请使用英文注释

当前代码包含中文注释,建议改为英文以保持一致性。

- // 将 async function 改为箭头函数形式的导出
+ // Convert async function to arrow function export
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// async function 改为箭头函数形式的导出
export const createTokenTransferTransaction = async (
// Convert async function to arrow function export
export const createTokenTransferTransaction = async (

connection: Connection,
fromPubkey: PublicKey,
toPubkey: PublicKey,
tokenMint: PublicKey,
recentBlockhash: string,
amount: number,
decimals: number
): Promise<Transaction> => {
const transaction = new Transaction();

const fromTokenAccount = await getAssociatedTokenAddress(
tokenMint,
fromPubkey
);

const toTokenAccount = await getAssociatedTokenAddress(
tokenMint,
toPubkey
);

transaction.add(
createTransferInstruction(
fromTokenAccount,
toTokenAccount,
fromPubkey,
BigInt(amount * Math.pow(10, decimals)),
)
);
Comment on lines +82 to +89
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

建议添加错误处理和金额验证

当前实现缺少必要的错误处理和金额验证。

建议添加以下安全检查:

+ if (amount <= 0) {
+   throw new Error('Amount must be positive');
+ }
+ 
+ const adjustedAmount = amount * Math.pow(10, decimals);
+ if (!Number.isSafeInteger(adjustedAmount)) {
+   throw new Error('Amount overflow after decimal adjustment');
+ }

  transaction.add(
    createTransferInstruction(
      fromTokenAccount,
      toTokenAccount,
      fromPubkey,
-     BigInt(amount * Math.pow(10, decimals)),
+     BigInt(adjustedAmount),
    )
  );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
transaction.add(
createTransferInstruction(
fromTokenAccount,
toTokenAccount,
fromPubkey,
BigInt(amount * Math.pow(10, decimals)),
)
);
if (amount <= 0) {
throw new Error('Amount must be positive');
}
const adjustedAmount = amount * Math.pow(10, decimals);
if (!Number.isSafeInteger(adjustedAmount)) {
throw new Error('Amount overflow after decimal adjustment');
}
transaction.add(
createTransferInstruction(
fromTokenAccount,
toTokenAccount,
fromPubkey,
BigInt(adjustedAmount),
)
);


transaction.feePayer = fromPubkey;
transaction.recentBlockhash = recentBlockhash;

return transaction;
};
69 changes: 60 additions & 9 deletions packages/example/components/chains/solana/example.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return */
import { dapps } from './dapps.config';
import ConnectButton from '../../../components/connect/ConnectButton';
import ConnectButton from '../../connect/ConnectButton';
import { useEffect, useMemo, useRef } from 'react';
import { get, set } from 'lodash-es';
import { IProviderApi, IProviderInfo } from './types';
import { ApiPayload, ApiGroup } from '../../ApiActuator';
import { useWallet } from '../../../components/connect/WalletContext';
import type { IKnownWallet } from '../../../components/connect/types';
import DappList from '../../../components/DAppList';
import { useWallet } from '../../connect/WalletContext';
import type { IKnownWallet } from '../../connect/types';
import DappList from '../../DAppList';
import { Connection, PublicKey, Transaction, VersionedTransaction, clusterApiUrl } from '@solana/web3.js';
import params from './params';
import { createTransferTransaction, createVersionedTransaction } from './builder';
import { createTransferTransaction, createVersionedTransaction, createTokenTransferTransaction } from './builder';
import nacl from 'tweetnacl';
import { toast } from '../../ui/use-toast';
// import { TOKEN_PROGRAM_ID } from '@solana/spl-token';

const NETWORK = clusterApiUrl('mainnet-beta');

Expand Down Expand Up @@ -103,6 +104,21 @@ export default function Example() {
};
}, [account, provider, setAccount]);

console.log('createTokenTransferTransaction exists:', typeof createTokenTransferTransaction);

Comment on lines +107 to +108
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

建议移除调试用的 console.log

console.log 语句可能会在生产环境中造成不必要的日志输出,建议在发布前移除。

- console.log('createTokenTransferTransaction exists:', typeof createTokenTransferTransaction);

const transactionStatus = {
status: 'pending',
message: `
交易尚未在本地确认

这是正常的,因为交易刚刚被广播到网络

交易需要等待网络确认,这个过程需要一些时间

请等待区块确认...
`
};
Comment on lines +109 to +120
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

优化多行字符串的缩进

多行字符串的过度缩进可能会导致输出时出现多余的空格,影响消息的显示。请考虑调整缩进或使用 trim() 方法去除多余的空格。

const transactionStatus = {
  status: 'pending',
  message: `
-    交易尚未在本地确认
-    
-    这是正常的,因为交易刚刚被广播到网络
-    
-    交易需要等待网络确认,这个过程需要一些时间
-    
-    请等待区块确认...
+交易尚未在本地确认

+这是正常的,因为交易刚刚被广播到网络

+交易需要等待网络确认,这个过程需要一些时间

+请等待区块确认...
  `
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const transactionStatus = {
status: 'pending',
message: `
交易尚未在本地确认
这是正常的,因为交易刚刚被广播到网络
交易需要等待网络确认,这个过程需要一些时间
请等待区块确认...
`
};
const transactionStatus = {
status: 'pending',
message: `
交易尚未在本地确认
这是正常的,因为交易刚刚被广播到网络
交易需要等待网络确认,这个过程需要一些时间
请等待区块确认...
`
};


return (
<>
<ConnectButton<IProviderApi>
Expand Down Expand Up @@ -216,16 +232,19 @@ export default function Example() {
return Buffer.from(res.serialize()).toString('hex')
}}
onValidate={async (request: string, result: string) => {
const tx = Transaction.from(Buffer.from(result, 'hex'))
const verified = tx.verifySignatures()
const tx = Transaction.from(Buffer.from(result, 'hex'));
const verified = tx.verifySignatures();

const res = await connection.simulateTransaction(tx, {
sigVerify: false,
});

const res = await connection.simulateTransaction(tx)
return {
success: res.value.err === null,
verified,
tryRun: res,
tx
}
};
}}
/>
<ApiPayload
Expand Down Expand Up @@ -301,6 +320,38 @@ export default function Example() {
return verifiedResult
}}
/>
<ApiPayload
title="transferToken"
description="代币转账"
presupposeParams={params.signAndSendTokenTransaction(account?.publicKey)}
onExecute={async (request: string) => {
const {
tokenMint,
toPubkey,
amount,
decimals
}: {
tokenMint: string;
toPubkey: string;
amount: number;
decimals: number;
} = JSON.parse(request);

const recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
const transaction = await createTokenTransferTransaction(
connection,
new PublicKey(account?.publicKey),
new PublicKey(toPubkey),
new PublicKey(tokenMint),
recentBlockhash,
amount,
decimals
);

const res = await provider?.signAndSendTransaction(transaction);
return JSON.stringify(res);
}}
/>
Comment on lines +323 to +354
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议添加输入验证

在处理用户输入时,请验证 toPubkeyamountdecimals 参数的有效性,防止潜在的安全问题或运行时错误。

</ApiGroup>

<DappList dapps={dapps} />
Expand Down
28 changes: 28 additions & 0 deletions packages/example/components/chains/solana/params.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
const TOKEN_LIST = [
{
symbol: 'USDT',
tokenMint: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
decimals: 6
},
{
symbol: 'USDC',
tokenMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
decimals: 6
},
// {
// symbol: 'RNDR',
// tokenMint: '7atgF8KQo4wJrD5ATGX7t1V2zVvykPJbFfNeVf1icFv1',
// decimals: 8
// },
];
Comment on lines +1 to +17
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

建议改进代币列表管理

建议将代币列表移至配置文件,并添加地址验证。

  1. 创建专门的配置文件
  2. 添加地址格式验证
  3. 考虑使用环境变量
+ import { PublicKey } from '@solana/web3.js';
+
+ const validateTokenMint = (address: string): boolean => {
+   try {
+     new PublicKey(address);
+     return true;
+   } catch {
+     return false;
+   }
+ };

const TOKEN_LIST = [
  {
    symbol: 'USDT',
    tokenMint: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
+   validate() {
+     if (!validateTokenMint(this.tokenMint)) {
+       throw new Error(`Invalid token mint for ${this.symbol}`);
+     }
+   },
    decimals: 6
  },
  // ... other tokens
];

+ TOKEN_LIST.forEach(token => token.validate());

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Gitleaks (8.21.2)

4-4: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


9-9: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


14-14: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

export default {
signMessage: [
{
Expand All @@ -16,6 +33,17 @@ export default {
}),
},
],
signAndSendTokenTransaction: (publicKey: string) =>
TOKEN_LIST.map(token => ({
id: `signAndSendTokenTransaction_${token.symbol}`,
name: `Send ${token.symbol} Token`,
value: JSON.stringify({
tokenMint: token.tokenMint,
toPubkey: publicKey,
amount: 0.000001,
decimals: token.decimals
}),
})),
Comment on lines +36 to +46
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

建议添加金额验证和自定义选项

当前实现使用固定的小额转账值,建议增加灵活性。

建议添加以下改进:

  signAndSendTokenTransaction: (publicKey: string) => 
    TOKEN_LIST.map(token => ({
      id: `signAndSendTokenTransaction_${token.symbol}`,
      name: `Send ${token.symbol} Token`,
      value: JSON.stringify({
        tokenMint: token.tokenMint,
        toPubkey: publicKey,
-       amount: 0.000001,
+       amount: process.env[`DEFAULT_${token.symbol}_AMOUNT`] || 0.000001,
        decimals: token.decimals
      }),
    })),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
signAndSendTokenTransaction: (publicKey: string) =>
TOKEN_LIST.map(token => ({
id: `signAndSendTokenTransaction_${token.symbol}`,
name: `Send ${token.symbol} Token`,
value: JSON.stringify({
tokenMint: token.tokenMint,
toPubkey: publicKey,
amount: 0.000001,
decimals: token.decimals
}),
})),
signAndSendTokenTransaction: (publicKey: string) =>
TOKEN_LIST.map(token => ({
id: `signAndSendTokenTransaction_${token.symbol}`,
name: `Send ${token.symbol} Token`,
value: JSON.stringify({
tokenMint: token.tokenMint,
toPubkey: publicKey,
amount: process.env[`DEFAULT_${token.symbol}_AMOUNT`] || 0.000001,
decimals: token.decimals
}),
})),

signMultipleTransaction: (publicKey: string) => [
{
id: 'signMultipleTransaction',
Expand Down
Loading
Loading