Skip to content

Commit 6a842a4

Browse files
authored
feat(cli-typescript): transfer signer native balance cmd (#235)
1 parent 37b3955 commit 6a842a4

File tree

6 files changed

+135
-3
lines changed

6 files changed

+135
-3
lines changed

cli-typescript/src/cmds/createCommand.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
setTokenURISuffixCmd,
3333
setUriCmd,
3434
transferOwnershipCmd,
35+
transferSignerBalanceCmd,
3536
withdrawContractBalanceCmd,
3637
} from './general';
3738
import listProjectsAction from '../utils/cmdActions/listProjectsAction';
@@ -216,6 +217,7 @@ export const createEvmCommand = ({
216217
newCmd.addCommand(setTokenURISuffixCmd());
217218
newCmd.addCommand(ownerMintCmd());
218219
newCmd.addCommand(checkSignerBalanceCmd());
220+
newCmd.addCommand(transferSignerBalanceCmd());
219221
newCmd.addCommand(getWalletInfoCmd());
220222
newCmd.addCommand(getConfigCmd());
221223

cli-typescript/src/cmds/general.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { Command } from 'commander';
22
import {
33
getCosignerOption,
44
getExpiryTimestampOption,
5-
getForceOption,
65
getFreezeThawOption,
6+
getGasLimitOption,
77
getGlobalWalletLimitOption,
88
getIsMintableOption,
99
getMaxMintableSupplyOption,
@@ -26,14 +26,15 @@ import { setMaxMintableSupplyAction } from '../utils/cmdActions/setMaxMintableSu
2626
import { setCosignerAction } from '../utils/cmdActions/setCosignerAction';
2727
import { setTimestampExpiryAction } from '../utils/cmdActions/setTimestampExpiryAction';
2828
import { withdrawContractBalanceAction } from '../utils/cmdActions/withdrawContractBalanceAction';
29-
import { TOKEN_STANDARD } from '../utils/constants';
29+
import { STANDARD_GAS_LIMIT, TOKEN_STANDARD } from '../utils/constants';
3030
import { freezeThawContractAction } from '../utils/cmdActions/freezeThawContractAction';
3131
import { transferOwnershipAction } from '../utils/cmdActions/transferOwnershipAction';
3232
import { manageAuthorizedMintersAction } from '../utils/cmdActions/manageAuthorizedMinters';
3333
import { setMintableAction } from '../utils/cmdActions/setMintableAction';
3434
import { setTokenUriSuffixAction } from '../utils/cmdActions/setTokenUriSuffixAction';
3535
import { ownerMintAction } from '../utils/cmdActions/ownerMintAction';
3636
import { checkSignerBalanceAction } from '../utils/cmdActions/checkSignerBalanceAction';
37+
import { transferSignerBalanceAction } from '../utils/cmdActions/transferSignerBalanceAction';
3738
import getWalletInfoAction from '../utils/cmdActions/getWalletInfoAction';
3839
import getProjectConfigAction from '../utils/cmdActions/getProjectConfigAction';
3940

@@ -195,3 +196,14 @@ export const checkSignerBalanceCmd = () =>
195196
.alias('csb')
196197
.description('Check the balance of the signer account for the collection.')
197198
.action(checkSignerBalanceAction);
199+
200+
export const transferSignerBalanceCmd = () =>
201+
new Command('transfer-signer-balance')
202+
.command('transfer-signer-balance <symbol>')
203+
.alias('tsb')
204+
.description(
205+
'Transfer all native balance from the signer account for the collection to the specified address.',
206+
)
207+
.addOption(getReceiverOption().makeOptionMandatory())
208+
.addOption(getGasLimitOption().default(STANDARD_GAS_LIMIT + 6000)) // Default gas limit with buffer
209+
.action(transferSignerBalanceAction);

cli-typescript/src/utils/ContractManager.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
import { collapseAddress, isValidEthereumAddress } from './common';
2626
import {
2727
APPLY_LIST_TO_COLLECTION_ABI,
28-
ERC1155M_ABIS,
2928
IS_SETUP_LOCKED_ABI,
3029
MagicDropCloneFactoryAbis,
3130
MagicDropTokenImplRegistryAbis,
@@ -162,6 +161,47 @@ export class ContractManager {
162161
console.log(`Balance: ${balance} ${symbol}`);
163162
}
164163

164+
/**
165+
* Transfers native currency (ETH, MATIC, etc.) to a recipient address
166+
* @param to The recipient address
167+
* @param amount The amount to transfer in wei
168+
* @returns Transaction hash
169+
*/
170+
public async transferNative({
171+
to,
172+
amount,
173+
gasLimit,
174+
}: {
175+
to: Hex;
176+
amount: bigint;
177+
gasLimit?: bigint;
178+
}): Promise<Hex> {
179+
const symbol = getSymbolFromChainId(this.chainId);
180+
showText(
181+
`Transferring ${formatEther(amount)} ${symbol} to ${collapseAddress(to)}...`,
182+
);
183+
184+
// Check if sender has enough balance
185+
const balance = await this.client.getBalance({
186+
address: this.signer,
187+
});
188+
189+
if (balance < amount) {
190+
throw new Error(
191+
`Insufficient balance. Available: ${formatEther(balance)} ${symbol}, Required: ${formatEther(amount)} ${symbol}`,
192+
);
193+
}
194+
195+
const txHash = await this.sendTransaction({
196+
to,
197+
data: '0x',
198+
value: amount,
199+
gasLimit,
200+
});
201+
202+
return txHash;
203+
}
204+
165205
static getContractAddressFromLogs(logs: TransactionReceipt['logs']) {
166206
try {
167207
// Generate the event selector from the event ABI
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { formatEther, Hex } from 'viem';
2+
import { actionPresets } from './common';
3+
import { printTransactionHash, showError, showText } from '../display';
4+
import { collapseAddress } from '../common';
5+
import { getSymbolFromChainId, promptForConfirmation } from '../getters';
6+
7+
export const transferSignerBalanceAction = async (
8+
symbol: string,
9+
params: {
10+
receiver: Hex;
11+
gasLimit: number;
12+
},
13+
) => {
14+
try {
15+
const { cm, config } = await actionPresets(symbol, false);
16+
const currencySymbol = getSymbolFromChainId(cm.chainId);
17+
18+
// Get current balance
19+
const balance = await cm.client.getBalance({
20+
address: cm.signer,
21+
});
22+
23+
// Estimate gas for the transfer
24+
const gasPrice = await cm.client.getGasPrice();
25+
const gasLimit = BigInt(params.gasLimit);
26+
const gasCost = gasPrice * BigInt(gasLimit);
27+
28+
if (balance <= gasCost) {
29+
throw new Error(
30+
`Insufficient balance to cover gas fees. Balance: ${formatEther(balance)} ${currencySymbol}, Gas cost: ${formatEther(gasCost)} ${currencySymbol}`,
31+
);
32+
}
33+
34+
// Transfer amount = balance - gas cost
35+
const transferAmount = balance - gasCost;
36+
37+
const confirm = await promptForConfirmation(
38+
`You are about to transfer all available balance: ${formatEther(balance)} ${currencySymbol} to ${collapseAddress(params.receiver)}. Do you want to proceed?`,
39+
);
40+
if (!confirm) {
41+
showText('Transfer cancelled. Aborting...');
42+
return;
43+
}
44+
45+
showText(
46+
`Transferring all available balance: ${formatEther(transferAmount)} ${currencySymbol} to ${collapseAddress(params.receiver)}`,
47+
`(Reserving ${formatEther(gasCost)} ${currencySymbol} for gas fees)`,
48+
false,
49+
false,
50+
);
51+
52+
const txHash = await cm.transferNative({
53+
to: params.receiver,
54+
amount: transferAmount,
55+
gasLimit,
56+
});
57+
58+
const receipt = await cm.waitForTransactionReceipt(txHash);
59+
60+
if (receipt.status !== 'success') {
61+
throw new Error('Transaction failed');
62+
}
63+
64+
printTransactionHash(receipt.transactionHash, config.chainId);
65+
showText(
66+
`Successfully transferred ${formatEther(transferAmount)} ${currencySymbol} to ${collapseAddress(params.receiver)}`,
67+
);
68+
} catch (error: any) {
69+
showError({
70+
text: `Failed to transfer all native currency: ${error.message}`,
71+
});
72+
}
73+
};

cli-typescript/src/utils/cmdOptions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ export const getTokenUriSuffixOption = () =>
150150
export const getReceiverOption = () =>
151151
new Option('-r --receiver <receiver>', 'the receiver address.');
152152

153+
export const getGasLimitOption = () =>
154+
new Option('-g --gasLimit <gasLimit>', 'the gas limit for the transaction.');
155+
153156
export const getQtyOption = () => new Option('--qty <qty>', 'the token qty.');
154157

155158
export const getConfigFileOption = () =>

cli-typescript/src/utils/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,5 @@ export const DEFAULT_MINT_CURRENCY =
144144
'0x0000000000000000000000000000000000000000';
145145
export const DEFAULT_MERKLE_ROOT =
146146
'0x0000000000000000000000000000000000000000000000000000000000000000';
147+
148+
export const STANDARD_GAS_LIMIT = 21000; // Standard gas limit for native transfers

0 commit comments

Comments
 (0)