Skip to content

Commit

Permalink
transfer utxo to evm with 4337
Browse files Browse the repository at this point in the history
  • Loading branch information
frankcrypto committed Aug 23, 2024
1 parent 951f373 commit 6f6abd2
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 71 deletions.
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,5 @@ module.exports = {
'**/build',
'**/public',
'**/.cache',
'!.manifest.json',
],
};
2 changes: 1 addition & 1 deletion packages/snap/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ module.exports = {
},
],

ignorePatterns: ['!.eslintrc.js', 'dist/', '!.manifest.json'],
ignorePatterns: ['!.eslintrc.js', 'dist/'],
};
2 changes: 1 addition & 1 deletion packages/snap/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qngsnap",
"version": "0.1.2",
"version": "0.1.3",
"description": "The Qng of MetaMask Snaps, now written in TypeScript.",
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"version": "0.1.2",
"version": "0.1.3",
"description": "An qng example Snap written in TypeScript.",
"proposedName": "QNG Snap",
"repository": {
"type": "git",
"url": "git+https://github.com/Qitmeer/qng-snap.git"
},
"source": {
"shasum": "dq3m9A23Q1JzN7O0PTKzkz80pOSp2mFkqA+njJdmbs8=",
"shasum": "GrP/wQIijbmoD1EMLhQWtAcS1Ef9UOPzInQbe4bLEUE=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
10 changes: 10 additions & 0 deletions packages/snap/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,44 @@ import type { NetworkConfig } from 'qitmeerts/dist/src/networks';
export type Config = {
entryPointAddress: string;
factoryAddress: string;
paymasterAddress: string;
proxyUrl: string;
networkConf: NetworkConfig;
meerchangeAddress: string;
};

export const PrivateConfig: Config = {
entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
factoryAddress: '0x9406cc6185a346906296840746125a0e44976454',
paymasterAddress: '0x0C170fAe5584421092D624425c85156758c190e0',
proxyUrl: 'http://127.0.0.1:8081',
networkConf: networks.privnet,
meerchangeAddress: '0x422f6F90B35D91D7D4F03aC791c6C07b1c14af1f',
};

export const TestConfig: Config = {
entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
factoryAddress: '0x9406cc6185a346906296840746125a0e44976454',
paymasterAddress: '0x0C170fAe5584421092D624425c85156758c190e0',
proxyUrl: 'https://47.242.255.132',
networkConf: networks.testnet,
meerchangeAddress: '0x422f6F90B35D91D7D4F03aC791c6C07b1c14af1f',
};
export const MixConfig: Config = {
entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
factoryAddress: '0x9406cc6185a346906296840746125a0e44976454',
paymasterAddress: '0x0C170fAe5584421092D624425c85156758c190e0',
proxyUrl: 'http://127.0.0.1:8081',
networkConf: networks.mixnet,
meerchangeAddress: '0x422f6F90B35D91D7D4F03aC791c6C07b1c14af1f',
};
export const MainConfig: Config = {
entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
factoryAddress: '0x9406cc6185a346906296840746125a0e44976454',
paymasterAddress: '0x0C170fAe5584421092D624425c85156758c190e0',
proxyUrl: 'http://127.0.0.1:8081',
networkConf: networks.mainnet,
meerchangeAddress: '0x422f6F90B35D91D7D4F03aC791c6C07b1c14af1f',
};

export const getConfig = (chainId: number): Config => {
Expand Down
88 changes: 40 additions & 48 deletions packages/snap/src/getAbstractAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import {
SimpleAccountAPI,
HttpRpcClient,
// PaymasterAPI,
PaymasterAPI,
// calcPreVerificationGas,
} from '@account-abstraction/sdk';
// import type { BigNumberish} from 'ethers';
Expand All @@ -13,57 +13,48 @@ import { getConfig } from './config';
// entryPointAddress
// const paymasterUrl = ''; // Optional
// Extend the Ethereum Foundation's account-abstraction/sdk's basic paymaster
// class VerifyingPaymasterAPI extends PaymasterAPI {
// // eslint-disable-next-line no-restricted-syntax
// private readonly _paymasterUrl: string;
class MeerChangePaymasterAPI extends PaymasterAPI {
// eslint-disable-next-line no-restricted-syntax
private readonly _paymasterUrl: string;

// // eslint-disable-next-line no-restricted-syntax
// private readonly _entryPoint: string;
// eslint-disable-next-line no-restricted-syntax
private readonly _entryPoint: string;

// constructor(_paymasterUrl: string, entryPoint: string) {
// super();
// this._paymasterUrl = _paymasterUrl;
// this._entryPoint = entryPoint;
// }
constructor(_paymasterUrl: string, entryPoint: string) {
super();
this._paymasterUrl = _paymasterUrl;
this._entryPoint = entryPoint;
}

// async getPaymasterAndData(
// userOp: Partial<UserOperationStruct>,
// ): Promise<string> {
// // Hack: userOp includes empty paymasterAndData which calcPreVerificationGas requires.
// try {
// // userOp.preVerificationGas contains a promise that will resolve to an error.
// await ethers.utils.resolveProperties(userOp);
// // eslint-disable-next-line no-empty
// } catch (_) {}
// const pmOp: Partial<UserOperationStruct> = {
// sender: userOp.sender as string,
// nonce: userOp.nonce as BigNumberish,
// initCode: userOp.initCode as string,
// callData: userOp.callData as string,
// callGasLimit: userOp.callGasLimit as BigNumberish,
// verificationGasLimit: userOp.verificationGasLimit as BigNumberish,
// maxFeePerGas: userOp.maxFeePerGas as BigNumberish,
// maxPriorityFeePerGas: userOp.maxPriorityFeePerGas as BigNumberish,
// // Dummy signatures are required in order to calculate a correct preVerificationGas value.
// paymasterAndData:
// '0x0101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000001010101010100000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101',
// signature:
// '0xa15569dd8f8324dbeabf8073fdec36d4b754f53ce5901e283c6de79af177dc94557fa3c9922cd7af2a96ca94402d35c39f266925ee6407aeb32b31d76978d4ba1c',
// };
// const op = await ethers.utils.resolveProperties(pmOp);
// op.preVerificationGas = calcPreVerificationGas(op);
// op.verificationGasLimit = ethers.BigNumber.from(
// op.verificationGasLimit,
// ).mul(3);
async getPaymasterAndData(userOp: any): Promise<string> {
console.log(userOp);
// Hack: userOp includes empty paymasterAndData which calcPreVerificationGas requires.
const provider = new ethers.providers.Web3Provider(ethereum as any);
const network = await provider.getNetwork();
const conf = getConfig(network.chainId);
return conf.paymasterAddress;
}
}

// // Ask the paymaster to sign the transaction and return a valid paymasterAndData value.
// const params = [await optoJSON(op), this._entryPoint, { "type": "payg" }];
// const provider = new ethers.providers.JsonRpcProvider(paymasterUrl);
// const response = await provider.send("pm_sponsorUserOperation", params);
export const getCurrentGasPrice = async (): Promise<ethers.BigNumber> => {
const provider = new ethers.providers.Web3Provider(ethereum as any);
const gasPrice = await provider.getGasPrice();
console.log(`${ethers.utils.formatUnits(gasPrice, 'gwei')} Gwei`);
return gasPrice;
};

// return response.data.result.toString();
// }
// }
export const getCurrentPriorityFee = async (): Promise<ethers.BigNumber> => {
const provider = new ethers.providers.Web3Provider(ethereum as any);
const feeData = await provider.getFeeData();
console.log(
'Max Priority Fee Per Gas:',
`${ethers.utils.formatUnits(
feeData.maxPriorityFeePerGas as ethers.BigNumber,
'gwei',
)} Gwei`,
);
return feeData.maxPriorityFeePerGas as ethers.BigNumber;
};

// eslint-disable-next-line jsdoc/require-jsdoc
// async function optoJSON(op: Partial<UserOperationStruct>): Promise<any> {
Expand Down Expand Up @@ -114,13 +105,14 @@ export const getAbstractAccount = async (
const conf = getConfig(chainId);
const provider = new ethers.providers.Web3Provider(ethereum as any);
await provider.send('eth_requestAccounts', []);
const paymasterAPI = new MeerChangePaymasterAPI('', conf.entryPointAddress);
const owner = provider.getSigner();
const aa = new SimpleAccountAPI({
provider,
entryPointAddress: conf.entryPointAddress,
owner,
factoryAddress: conf.factoryAddress,
// paymasterAPI,
paymasterAPI,
});
return aa;
};
Expand Down
90 changes: 90 additions & 0 deletions packages/snap/src/meerChangeAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
export const MeerChangeABI = [
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'bytes32',
name: 'txid',
type: 'bytes32',
},
{ indexed: false, internalType: 'uint32', name: 'idx', type: 'uint32' },
],
name: 'Export',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'bytes32',
name: 'txid',
type: 'bytes32',
},
{ indexed: false, internalType: 'uint32', name: 'idx', type: 'uint32' },
{ indexed: false, internalType: 'uint64', name: 'fee', type: 'uint64' },
{ indexed: false, internalType: 'string', name: 'sig', type: 'string' },
],
name: 'Export4337',
type: 'event',
},
{ anonymous: false, inputs: [], name: 'Import', type: 'event' },
{
inputs: [],
name: 'TO_UTXO_PRECISION',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{ internalType: 'bytes32', name: 'txid', type: 'bytes32' },
{ internalType: 'uint32', name: 'idx', type: 'uint32' },
],
name: 'export',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{ internalType: 'bytes32', name: 'txid', type: 'bytes32' },
{ internalType: 'uint32', name: 'idx', type: 'uint32' },
{ internalType: 'uint64', name: 'fee', type: 'uint64' },
{ internalType: 'string', name: 'sig', type: 'string' },
],
name: 'export4337',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'getExportCount',
outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'getImportCount',
outputs: [{ internalType: 'uint64', name: '', type: 'uint64' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'getImportTotal',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'importToUtxo',
outputs: [],
stateMutability: 'payable',
type: 'function',
},
];
61 changes: 55 additions & 6 deletions packages/snap/src/qng.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import { ec, hash, address } from 'qitmeerts';
import * as uint8arraytools from 'uint8array-tools';

import { getConfig } from './config';
import {
getAbstractAccount,
bundlerProvider,
getCurrentGasPrice,
getCurrentPriorityFee,
} from './getAbstractAccount';
import { MeerChangeABI } from './meerChangeAbi';
import {
qngTransferUtxo,
qngGetUTXOBalance,
transferUTXOToEvmWithEthSign,
getInputHash,
qngGetAvailableUtxos,
crossSendtoBunder,
handleSignStr,
trimHexPrefix,
handleSignStr,
} from './qngweb3';

export const CRYPTO_CURVE = 'secp256k1';
Expand Down Expand Up @@ -53,15 +59,50 @@ export const getQngBalance = async (chainId: number): Promise<string> => {
const ba = await qngGetUTXOBalance(addr, chainId);
return ba;
};

export const sendToBundler = async (
txid: string,
idx: number,
fee: number,
signature: string,
chainId: number,
): Promise<string> => {
const conf = getConfig(chainId);
const aa = await getAbstractAccount(chainId);
const iface = new ethers.utils.Interface(MeerChangeABI);
const data = iface.encodeFunctionData('export4337', [
ethers.utils.hexZeroPad(`0x${txid}`, 32),
idx,
fee,
signature,
]);
const userOp = await aa.createSignedUserOp({
target: conf.meerchangeAddress,
value: 0,
data,
gasLimit: 200000,
maxFeePerGas: await getCurrentGasPrice(),
maxPriorityFeePerGas: await getCurrentPriorityFee(),
});
const bp = await bundlerProvider(chainId);
const userOpHash = await bp.sendUserOpToBundler(userOp);
const txhash = await aa.getUserOpReceipt(userOpHash);
return txhash as string;
};
export const ethSign = async (
txid: string,
idx: number,
fee: number,
chainId: number,
): Promise<string> => {
const signRes = await transferUTXOToEvmWithEthSign(txid, idx, fee, chainId);
return signRes;
const signature = await transferUTXOToEvmWithEthSign(txid, idx, fee);
const txhash = await sendToBundler(
txid,
idx,
fee,
handleSignStr(signature),
chainId,
);
return txhash;
};

export const walletSign = async (
Expand All @@ -79,13 +120,21 @@ export const walletSign = async (
uint8arraytools.toHex(privKey.privateKey as Uint8Array),
);
const signature = await wallet.signMessage(getInputHash(txid, idx, fee));
const txhash = await crossSendtoBunder(

const txhash = await sendToBundler(
txid,
idx,
fee,
handleSignStr(signature),
chainId,
);
// const txhash = await crossSendtoBunder(
// txid,
// idx,
// fee,
// handleSignStr(signature),
// chainId,
// );
// get the eth address
return `${wallet.address}:${txhash}`;
};
Expand Down
Loading

0 comments on commit 6f6abd2

Please sign in to comment.