import { Wallet } from '@ethereumjs/wallet';
import {
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
SystemProgram,
Transaction,
TransactionInstruction,
} from '@solana/web3.js';
import {
Actions,
createSecp256k1AuthorityInfo,
findSwigPda,
getCreateSwigInstruction,
getEvmPersonalSignPrefix,
getSignInstructions,
Swig,
SWIG_PROGRAM_ADDRESS,
type SigningFn,
} from '@swig-wallet/classic';
import { hexToBytes, keccak256 } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
// Create an Ethereum wallet account
const userWallet = Wallet.generate();
const privateKeyAccount = privateKeyToAccount(userWallet.getPrivateKeyString());
const transactionPayer = Keypair.generate();
const dappTreasury = Keypair.generate().publicKey;
const id = Uint8Array.from(Array(32).fill(0));
const swigAddress = findSwigPda(id);
const rootActions = Actions.set().all().get();
// Create Swig with secp256k1 authority
const createSwigInstruction = await getCreateSwigInstruction({
authorityInfo: createSecp256k1AuthorityInfo(privateKeyAccount.publicKey),
id,
payer: transactionPayer.publicKey,
actions: rootActions,
});
// Fetch Swig and find role
let swig = fetchSwig(svm, swigAddress);
let rootRole = swig.findRolesBySecp256k1SignerAddress(
privateKeyAccount.address,
)[0];
// Different signing methods available:
// 1. Direct keccak256 hash signing (raw message bytes)
const viemSign: SigningFn = async (message: Uint8Array) => {
const sig = await privateKeyAccount.sign({ hash: keccak256(message) });
return { signature: hexToBytes(sig) };
};
// 2. Manual Ethereum personal sign prefix
const viemSignWithPrefix: SigningFn = async (message: Uint8Array) => {
const prefix = getEvmPersonalSignPrefix(message.length);
const prefixedMessage = new Uint8Array(prefix.length + message.length);
prefixedMessage.set(prefix);
prefixedMessage.set(message, prefix.length);
const sig = await privateKeyAccount.sign({
hash: keccak256(prefixedMessage)
});
return { signature: hexToBytes(sig), prefix };
};
// 3. Viem signMessage method (automatically adds Ethereum personal sign prefix)
const viemSignMessage: SigningFn = async (message: Uint8Array) => {
const sig = await privateKeyAccount.signMessage({
message: { raw: message }
});
return {
signature: hexToBytes(sig),
prefix: getEvmPersonalSignPrefix(message.length),
};
};
// Create and sign a transfer transaction
const transfer = SystemProgram.transfer({
fromPubkey: swigAddress,
toPubkey: dappTreasury,
lamports: 0.1 * LAMPORTS_PER_SOL,
});
const signTransfer = await getSignInstructions(
swig,
rootRole.id,
[transfer],
false,
{
currentSlot: svm.getClock().slot,
signingFn: viemSign, // or viemSignWithPrefix, or viemSignMessage
payer: transactionPayer.publicKey,
},
);