Skip to content

Commit

Permalink
Merge pull request #143 from jayden-sudo/develop
Browse files Browse the repository at this point in the history
New function export: 'async getSemiValidSignature(args...) `
  • Loading branch information
jayden-sudo authored Mar 19, 2024
2 parents e415fb3 + 01a952f commit ac8e7f4
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 50 deletions.
5 changes: 5 additions & 0 deletions .changeset/plenty-owls-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@soulwallet/sdk": patch
---

New function export: 'async getSemiValidSignature(args...) `
214 changes: 214 additions & 0 deletions packages/soulwallet-sdk/__tests__/activateWallet.tesdt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { TypedDataDomain, TypedDataField, ethers } from 'ethers';
import {
SoulWallet,
UserOperation,
PackedUserOperation,
UserOpUtils,
UserOpErrors,
UserOpErrorCodes,
L1KeyStore,
Ok, Err, Result,
UserOpReceipt,
UserOpDetail,
UserOpGas,
Bundler,
Transaction,
KeyStoreInfo,
GuardianSignature,
KeyStoreTypedDataType,
InitialKey,
ECCPoint,
SignkeyType,
P256Lib,
WebAuthN,
Base64Url,
WalletFactory
} from '..';
import { describe, expect, test } from '@jest/globals';
import { webcrypto } from 'node:crypto';
import { Hex } from '../src/tools/hex';
import {
randomBytes
} from 'crypto';

describe('ActivateWallet', () => {
test('Activate', async () => {
const RPC = "https://sepolia.base.org";
const BundlerRPC = "https://api-dev.soulwallet.io/appapi/bundler/base-sepolia/rpc";

const SoulWalletDefaultValidator = '0x82621ac52648b738fEdd381a3678851933505762';
const SoulwalletInstance = '0x50E964af1Dcf5fA3c6C8Ee5C0A903838E9Aa7aa4';
const SoulwalletFactory = '0xF78Ae187CED0Ca5Fb98100d3F0EAB7a6461d6fC6';
const DefaultCallbackHandler = '0x880c6eb80583795625935B08AA28EB37F16732C7';
const AaveUsdcSaveAutomationBaseSepolia = '0x8107e6c74980Df3fDDc8478F6597670742e4B542';

const Web3RPC = new ethers.JsonRpcProvider(RPC);

const soulWallet = new SoulWallet(
Web3RPC,
BundlerRPC,
SoulwalletFactory,
SoulWalletDefaultValidator,
DefaultCallbackHandler,
undefined,
undefined
);

// new EOASigner
const signer = ethers.Wallet.createRandom();
//const signer = new ethers.Wallet('0x0000000000000000000000000000000000000000000000000000000000000004');

const index: number = 0;
const initialKeys: InitialKey[] = [signer.address];
const initialGuardianHash: string = "0x";

const _walletAddress = await soulWallet.calcWalletAddress(
index,
initialKeys,
initialGuardianHash
);
expect(_walletAddress.isOk()).toBe(true);
const walletAddress = _walletAddress.OK;
console.log('walletAddress', walletAddress);

const calldata = "0x";
const _userOp = await soulWallet.createUnsignedDeployWalletUserOp(
index,
initialKeys,
initialGuardianHash,
calldata
);

expect(_userOp.isOk()).toBe(true);
const userOp = _userOp.OK;

{

// get gas price
const gasPrice = await Web3RPC.getFeeData();
expect(gasPrice).not.toBeNull();
expect(gasPrice.maxFeePerGas).not.toBeNull();
expect(gasPrice.maxPriorityFeePerGas).not.toBeNull();
userOp.maxFeePerGas = gasPrice.maxFeePerGas!;
userOp.maxPriorityFeePerGas = gasPrice.maxPriorityFeePerGas!;
}

{
const re = await soulWallet.estimateUserOperationGas(SoulWalletDefaultValidator, userOp, SignkeyType.EOA);
if (userOp.callData === '0x') {
userOp.callGasLimit = 1;
}
expect(re.isOk()).toBe(true);
}

let usePaymaster = true;
if (usePaymaster) {
const entryPoint = (await soulWallet.entryPoint()).OK;
const chainId = '0x' + ((await Web3RPC.getNetwork()).chainId).toString(16);
userOp.paymaster = "";
userOp.paymasterVerificationGasLimit = 0;
userOp.paymasterPostOpGasLimit = 0;
userOp.paymasterData = "0x";
userOp.signature = (await soulWallet.getSemiValidSignature(SoulWalletDefaultValidator, userOp, SignkeyType.EOA)).OK;
// const SponsorRPC = new ethers.JsonRpcProvider('https://api.pimlico.io/v2/base-sepolia/rpc?apikey=58e25ab4-7814-4e56-87bb-af3016dae2df');

// try {
// const result = await SponsorRPC.send(
// "pm_sponsorUserOperation", [
// UserOpUtils.userOperationToJSON(userOp),
// {
// entryPoint: entryPoint
// }
// ]);
// console.log('result', result);
// } catch (e) {
// console.log('error', e);
// }
const sponsorUrl = "https://api-dev.soulwallet.io/appapi/sponsor/sponsor-op";
const sponsorData = {
chainId: chainId,
entryPoint: entryPoint,
op: JSON.parse(UserOpUtils.userOperationToJSON(userOp))
};
const re = await fetch(sponsorUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(sponsorData),
});
const json = await re.json();
if (
typeof json === 'object' &&
typeof json.msg === 'string' &&
json.msg === "success" &&
typeof json.data === 'object' &&
typeof json.data.callGasLimit === 'string' &&
typeof json.data.paymaster === 'string' &&
typeof json.data.paymasterData === 'string' &&
typeof json.data.paymasterPostOpGasLimit === 'string' &&
typeof json.data.paymasterVerificationGasLimit === 'string' &&
typeof json.data.verificationGasLimit === 'string'
) {
userOp.callGasLimit = BigInt(json.data.callGasLimit);
userOp.paymaster = json.data.paymaster;
userOp.paymasterData = json.data.paymasterData;
userOp.paymasterPostOpGasLimit = BigInt(json.data.paymasterPostOpGasLimit);
userOp.paymasterVerificationGasLimit = BigInt(json.data.paymasterVerificationGasLimit);
userOp.preVerificationGas = BigInt(json.data.preVerificationGas);
userOp.verificationGasLimit = BigInt(json.data.verificationGasLimit);
} else {
throw new Error('sponsor failed');
}
console.log('json', json);

} else {
const preFund = await soulWallet.preFund(userOp);
expect(preFund.isOk()).toBe(true);
// get balance
const _balance = await Web3RPC.getBalance(walletAddress);
const missfund = BigInt(preFund.OK.missfund);
expect(_balance >= missfund).toBe(true);
}

{
const _userOpHash = await soulWallet.userOpHash(userOp);
expect(_userOpHash.isOk()).toBe(true);
const userOpHash = _userOpHash.OK;
const dateNow = Math.floor(new Date().getTime() / 1000);
const _re = await soulWallet.packRawHash(userOpHash, dateNow - 1000 * 60 * 60, dateNow + 1000 * 60 * 60);
expect(_re.isOk()).toBe(true);
const packedHash: string = _re.OK.packedHash;
const validationData: string = _re.OK.validationData;

const _signature = signer.signMessageSync(ethers.getBytes(packedHash));

const signature = await soulWallet.packUserOpEOASignature(SoulWalletDefaultValidator, _signature, validationData);
expect(signature.isOk()).toBe(true);
userOp.signature = signature.OK;
}
{
const packedUserOp = UserOpUtils.packUserOp(userOp);
const packedUserOpJson = UserOpUtils.packedUserOperationToJSON(packedUserOp);
const tupleStr = UserOpUtils.packedUserOperationToTuple(packedUserOp);
console.log('packedUserOp', packedUserOp);
const re = await soulWallet.sendUserOperation(userOp);
if (re.isErr()) {
debugger;
console.log('error', re.ERR.toString());
}
expect(re.isOk()).toBe(true);
}










}, 1000 * 60);
});

1 change: 0 additions & 1 deletion packages/soulwallet-sdk/__tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ describe('SDK', () => {
"0x8f63d7dD6A3F5938616Ef06016BBf25BD6023315"
], "0x55e85a731014097612c7d462fbdededcb5f50a5cb64b0c2068cfe017b51268d0");
console.log(a2);
debugger;
const userop = await soulwallet.createUnsignedDeployWalletUserOp(0, [
"0x8f63d7dD6A3F5938616Ef06016BBf25BD6023315"
], "0x", undefined, undefined);
Expand Down
2 changes: 1 addition & 1 deletion packages/soulwallet-sdk/src/interface/IUserOpErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class UserOpErrors extends Error {
this.data = data;
}
toString(): string {
return `UserOpErrors: ${this.message} (${this.code})`;
return `UserOpErrors - ${this.code}\t${this.message}`;
}
}

Expand Down
100 changes: 54 additions & 46 deletions packages/soulwallet-sdk/src/soulWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ export class SoulWallet implements ISoulWallet {
);
}

async estimateUserOperationGas(validatorAddress: string, userOp: UserOperation, signkeyType?: SignkeyType, semiValidGuardHookInputData?: GuardHookInputData): Promise<Result<true, UserOpErrors>> {
async getSemiValidSignature(validatorAddress: string, userOp: UserOperation, signkeyType?: SignkeyType, semiValidGuardHookInputData?: GuardHookInputData): Promise<Result<string, UserOpErrors>> {
if (semiValidGuardHookInputData !== undefined) {
if (semiValidGuardHookInputData.sender.toLowerCase() !== userOp.sender.toLowerCase()) {
return new Err(
Expand All @@ -560,59 +560,67 @@ export class SoulWallet implements ISoulWallet {
);
}
}
// semiValidSignature
const validationData = (BigInt(68719476735) << BigInt(160)) + (BigInt(1599999999) << BigInt(160 + 48));
let signatureRet: Result<string, Error>;
if (signkeyType === SignkeyType.P256) {
signatureRet = await this.packUserOpP256Signature(
validatorAddress,
{
messageHash: "0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd",
publicKey: {
x: "0xe89e8b4be943fadb4dc599fe2e8af87a79b438adde328a3b72d43324506cd5b6",
y: "0x4fbfe4a2f9934783c3b1af712ee87abc08f576e79346efc3b8355d931bd7b976"
},
r: "0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a",
s: "0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6",
authenticatorData: "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000",
clientDataSuffix: "\",\"origin\":\"http://localhost:5500\",\"crossOrigin\":false}"
},
`0x${validationData.toString(16)}`,
semiValidGuardHookInputData
);
} else if (signkeyType === SignkeyType.RS256) {
signatureRet = await this.packUserOpRS256Signature(
validatorAddress,
{
messageHash: "0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd",
publicKey: {
e: "0x010001",
n: "0xc6807412ed8d616565508c879292686bb1db6f0561b62c7b66a2d3806d161c0ccef888d2c1efcf061e268a15e61e7d023646014c33b1ead31bef0e5379558e6ff71249b143c03abec33a2b055fc8e0a947393512e7e26ad33f0ad4aabfe32d0642965856d8e20204a44d78e36cc90db2a12cfbc37fa97360efd3a735c625ab814d6f6bb7c63abe261bbd9c52681c6221f936d617dc84de61556074f6c1d73b3ffd242d2940d3c02c5a269e390bd8e6b6301a5a0a339910f6480403d27d32c2ff2b9bf33bae45c36f423025ca41f05c97be5148b2cb276b31441274100bf3ca0b50da1ee04511be9bdbb4f12b7579ab3da780bc2c615e2a49f5e1f750b034d0af"
},
signature: "0x357a51b26e22dcfb87346bb6938cfb2b066d48d4c36cafd30ac105fe345199966f24c87fa66791d4c2341b97fa07421ef4115a9923e6249c53887b6f2313df60654083758fe7104286490e1a37481246395dcb097a86645dc3251afa5c87e4bc8f2960cfe3efa34c44bbee0fe3d602866c81a5fc432709443c623595556670a427502c63c1e6a86761c8b326b5f503bdcfdcf1f00871f330a9fddf6ae11adcff4a5f411edec30019c86936f8064b70f88cdb56ba6635175f7ef5c74f52de9db5498e4c4d4b75c8a3210e5b1a631af271c4b613a8752b2a1cea499bd81115d9ed34305d9ab4af753dc9b9630478fdb0787e5f5e0efb76504d15eff5fd02a38bf1",
authenticatorData: "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000001",
clientDataSuffix: "\",\"origin\":\"http://localhost:5500\",\"crossOrigin\":false}"
},
`0x${validationData.toString(16)}`,
semiValidGuardHookInputData
);
} else {
const signature = "0xb91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c";
signatureRet = await this.packUserOpEOASignature(validatorAddress, signature, `0x${validationData.toString(16)}`, semiValidGuardHookInputData);
}
if (signatureRet.isErr() === true) {
return new Err(
new UserOpErrors(UserOpErrorCodes.UnknownError, signatureRet.ERR.message)
);
}
return new Ok(signatureRet.OK);
}

async estimateUserOperationGas(validatorAddress: string, userOp: UserOperation, signkeyType?: SignkeyType, semiValidGuardHookInputData?: GuardHookInputData): Promise<Result<true, UserOpErrors>> {
const semiValidSignature = userOp.signature === "0x";
const _onChainConfig = await this.getOnChainConfig();
if (_onChainConfig.isErr() === true) {
return new Err(new UserOpErrors(UserOpErrorCodes.UnknownError, _onChainConfig.ERR.message));
}
try {
if (semiValidSignature) {
// semiValidSignature
const validationData = (BigInt(68719476735) << BigInt(160)) + (BigInt(1599999999) << BigInt(160 + 48));
let signatureRet: Result<string, Error>;
if (signkeyType === SignkeyType.P256) {
signatureRet = await this.packUserOpP256Signature(
validatorAddress,
{
messageHash: "0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd",
publicKey: {
x: "0xe89e8b4be943fadb4dc599fe2e8af87a79b438adde328a3b72d43324506cd5b6",
y: "0x4fbfe4a2f9934783c3b1af712ee87abc08f576e79346efc3b8355d931bd7b976"
},
r: "0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a",
s: "0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6",
authenticatorData: "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000",
clientDataSuffix: "\",\"origin\":\"http://localhost:5500\",\"crossOrigin\":false}"
},
`0x${validationData.toString(16)}`,
semiValidGuardHookInputData
);
} else if (signkeyType === SignkeyType.RS256) {
signatureRet = await this.packUserOpRS256Signature(
validatorAddress,
{
messageHash: "0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd",
publicKey: {
e: "0x010001",
n: "0xc6807412ed8d616565508c879292686bb1db6f0561b62c7b66a2d3806d161c0ccef888d2c1efcf061e268a15e61e7d023646014c33b1ead31bef0e5379558e6ff71249b143c03abec33a2b055fc8e0a947393512e7e26ad33f0ad4aabfe32d0642965856d8e20204a44d78e36cc90db2a12cfbc37fa97360efd3a735c625ab814d6f6bb7c63abe261bbd9c52681c6221f936d617dc84de61556074f6c1d73b3ffd242d2940d3c02c5a269e390bd8e6b6301a5a0a339910f6480403d27d32c2ff2b9bf33bae45c36f423025ca41f05c97be5148b2cb276b31441274100bf3ca0b50da1ee04511be9bdbb4f12b7579ab3da780bc2c615e2a49f5e1f750b034d0af"
},
signature: "0x357a51b26e22dcfb87346bb6938cfb2b066d48d4c36cafd30ac105fe345199966f24c87fa66791d4c2341b97fa07421ef4115a9923e6249c53887b6f2313df60654083758fe7104286490e1a37481246395dcb097a86645dc3251afa5c87e4bc8f2960cfe3efa34c44bbee0fe3d602866c81a5fc432709443c623595556670a427502c63c1e6a86761c8b326b5f503bdcfdcf1f00871f330a9fddf6ae11adcff4a5f411edec30019c86936f8064b70f88cdb56ba6635175f7ef5c74f52de9db5498e4c4d4b75c8a3210e5b1a631af271c4b613a8752b2a1cea499bd81115d9ed34305d9ab4af753dc9b9630478fdb0787e5f5e0efb76504d15eff5fd02a38bf1",
authenticatorData: "0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000001",
clientDataSuffix: "\",\"origin\":\"http://localhost:5500\",\"crossOrigin\":false}"
},
`0x${validationData.toString(16)}`,
semiValidGuardHookInputData
);
} else {
const signature = "0xb91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c";
signatureRet = await this.packUserOpEOASignature(validatorAddress, signature, `0x${validationData.toString(16)}`, semiValidGuardHookInputData);
}
if (signatureRet.isErr() === true) {
return new Err(
new UserOpErrors(UserOpErrorCodes.UnknownError, signatureRet.ERR.message)
);
const re = await this.getSemiValidSignature(validatorAddress, userOp, signkeyType, semiValidGuardHookInputData);
if (re.isErr() === true) {
return new Err(re.ERR);
}
userOp.signature = signatureRet.OK;
userOp.signature = re.OK;
}
const userOpGasRet = await this.Bundler.eth_estimateUserOperationGas(_onChainConfig.OK.entryPoint, userOp);
if (userOpGasRet.isErr() === true) {
Expand Down
Loading

0 comments on commit ac8e7f4

Please sign in to comment.