- Add following content to .npmrc:
@b2network:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=<REPLACE WITH YOUR PERSONAL ACCESS TOKEN WITH PACKAGE READ PERM>
- Then install the package with:
yarn add @b2network/aa-sdk
- Refer to b2-account-infra repo to collect deployed contract addresses
import { SimpleWeightedECDSAProvider, SmartAccountSigner } from '@b2network/aa-sdk';
import { type WalletClient, type Chain, type Hex, hexToBigInt, keccak256, concatHex } from "viem";
const convertWalletClientToAccountSigner = (
client: WalletClient
): SmartAccountSigner => {
return {
async getAddress() {
return Promise.resolve((await client.getAddresses())[0] as `0x${string}`)
},
async signMessage(message: Uint8Array | Hex | string) {
const sig = await client.signMessage({
account: client.account!,
message:
typeof message === 'string'
? message
: {
raw: message,
},
})
return concatHex(['0x00', sig])
},
async signTypedData(params: SignTypedDataParams) {
const sig = await client.signTypedData({
...params,
account: client.account!,
})
return concatHex(['0x00', sig])
},
}
}
const selectedSigner = convertWalletClientToAccountSigner(signer)
const ownerHash = keccak256(
Buffer.from((await selectedSigner.getAddress()).toLowerCase(), "utf-8")
);
const provider = SimpleWeightedECDSAProvider.init({
projectId: "0",
selectedSigner,
guardians: ['0x7d25AF0015dB2625a5560b3914565FcD7AE49C78'],
ids: ['0x0000000000000000000000000000000000000000000000000000000000000000'],
weights: [1],
threshold: 1,
opts: {
providerConfig: {
chain,
rpcUrl: BUNDLER_RPC_URL,
entryPointAddress: ENTRYPOINT_ADDRESS,
opts: {
txRetryMulitplier: 1,
txRetryIntervalMs: 3000,
}
},
accountConfig: {
entryPointAddress: ENTRYPOINT_ADDRESS,
factoryAddress: KERNEL_FACTORY_ADDRESS,
implAddress: KERNEL_IMPL_ADDRESS,
index: hexToBigInt(ownerHash),
},
validatorConfig: {
entryPointAddress: ENTRYPOINT_ADDRESS,
validatorAddress: SW_VALIDATOR_ADDRESS,
},
paymasterConfig: {
policy: "VERIFYING_PAYMASTER",
baseURL: PM_BASE_URL,
}
},
})
const { hash } = await provider.sendUserOperation({
target: "0xtarget",
data: "0xcallData",
value: 0n
});
await provider.waitForUserOperationTransaction(
result.hash as Hex
);
const { hash } = await provider.sendUserOperation([
{
target: "0xtarget1",
data: "0xcallData1",
value: 0n,
},
{
target: "0xtarget2",
data: "0xcallData2",
value: 0n,
},
]);
The primary interfaces are the ZeroDevProvider
, KernelSmartContractAccount
and KernelBaseValidator
The ZeroDevProvider
is an ERC-1193 compliant Provider built on top of Alchemy's SmartAccountProvider
sendUserOperation
-- this takes intarget
,callData
, and an optionalvalue
which then constructs a UserOperation (UO), sends it, and returns thehash
of the UO. It handles estimating gas, fetching fee data, (optionally) requesting paymasterAndData, and lastly signing. This is done via a middleware stack that runs in a specific order. The middleware order isgetDummyPaymasterData
=>estimateGas
=>getFeeData
=>getPaymasterAndData
. The paymaster fields are set to0x
by default. They can be changed usingprovider.withPaymasterMiddleware
.sendTransaction
-- this takes in a traditional Transaction Request object which then gets converted into a UO. Currently, the only data being used from the Transaction Request object isfrom
,to
,data
andvalue
. Support for other fields is coming soon.
KernelSmartContractAccount
is Kernel's implementation of BaseSmartContractAccount
. The following methods are implemented:
getDummySignature
-- this method should return a signature that will notrevert
during validation. It does not have to pass validation, just not cause the contract to revert. This is required for gas estimation so that the gas estimate are accurate.encodeExecute
-- this method should return the abi encoded function data for a call to your contract'sexecute
methodencodeExecuteDelegate
-- this method should return the abi encoded function data for adelegate
call to your contract'sexecute
methodsignMessage
-- used to sign messagessignTypedData
-- used to sign typed datasignMessageWith6492
-- likesignMessage
but supports ERC-6492signTypedDataWith6492
-- likesignTypedData
but supports ERC-6492getAccountInitCode
-- this should return the init code that will be used to create an account if one does not exist. Usually this is the concatenation of the account's factory address and the abi encoded function data of the account factory'screateAccount
method.
The KernelBaseValidator
is a plugin that modify how transactions are validated. It allows for extension and implementation of arbitrary validation logic. It implements 3 main methods:
getAddress
-- this returns the address of the validatorgetOwner
-- this returns the eligible signer's address for the active smart walletgetSignature
-- this method signs the userop hash using signer object and then concats additional params based on validator mode.
-
Create a new validator class that extends
KernelBaseValidator
similar toECDSAValidator
. -
Make sure to pass the
validatorAddress
of your validator to theKernelBaseValidator
base class. -
Create a new validator provider that extends
ValidatorProvider
similar toECDSAValidatorProvider
. -
Use the newly created validator provider as per above examples.
signer()
-- this method should return the signer as per your validator's implementation. For example, for Multi-Signature validator, this method should return one of the owner signer which is connected to the multisig wallet contract and currently using the DAPP.getOwner()
-- this method should return the address of the signer. For example, for Multi-Signature validator, this method should return the address of the signer which is connected to the multisig wallet contract and currently using the DAPP.getEnableData()
-- this method should return the bytes data for theenable
method of your validator contract. For example, in ECDSA validator, this method returnsowner
address as bytes data. This method is used to enable the validator for the first time while creating the account wallet.encodeEnable(enableData: Hex)
-- this method should return the abi encoded function data for theenable
method of your validator contract. For example, in ECDSA validator, this method returns the abi encoded function data for theenable
method with owner address as bytes param.encodeDisable(disableData: Hex)
-- this method should return the abi encoded function data for thedisable
method of your validator contract. For example, in ECDSA validator, this method returns the abi encoded function data for thedisable
method with empty bytes param since ECDSA Validator doesn't require any param.signMessage(message: Uint8Array | string | Hex)
-- this method should return the signature of the message using the connected signer.signUserOp(userOp: UserOperationRequest)
-- this method should return the signature of the userOp hash using the connected signer.