Skip to content

Commit

Permalink
Merge pull request #25 from bcnmy/dev-v2
Browse files Browse the repository at this point in the history
Dev v2 sync
  • Loading branch information
livingrockrises authored Dec 5, 2023
2 parents cfcaf2b + c5bf62b commit c4b6750
Show file tree
Hide file tree
Showing 8 changed files with 563 additions and 512 deletions.
102 changes: 64 additions & 38 deletions src/components/Modules/CreateSession.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import { SessionKeyManagerModule } from "@biconomy/modules";
import Button from "../Button";
import { useAccount } from "wagmi";
import { useSmartAccountContext } from "../../contexts/SmartAccountContext";
import { showErrorMessage, showSuccessMessage } from "../../utils";
import { configInfo as config, showErrorMessage, showInfoMessage } from "../../utils";
import { defaultAbiCoder } from "ethers/lib/utils";
import { getActionForErrorMessage } from "../../utils/error-utils";
import { DEFAULT_SESSION_KEY_MANAGER_MODULE } from "@biconomy/modules";
import { ERC20_SESSION_VALIDATION_MODULE } from "../../utils/chainConfig";
import { useEthersSigner } from "../../contexts/ethers";

const CreateSession: React.FC = () => {
const classes = useStyles();
const { address } = useAccount();
const signer = useEthersSigner();
const { smartAccount, scwAddress } = useSmartAccountContext();
const [loading, setLoading] = useState(false);
const [isSessionKeyModuleEnabled, setIsSessionKeyModuleEnabled] =
Expand All @@ -26,17 +29,19 @@ const CreateSession: React.FC = () => {
}
try {
let biconomySmartAccount = smartAccount;
const managerModuleAddr = DEFAULT_SESSION_KEY_MANAGER_MODULE;
const sessionKeyManagerModuleAddr = DEFAULT_SESSION_KEY_MANAGER_MODULE;
// Checks if Session Key Manager module is enabled on the smart account.
// Before using session keys this module must be enabled.
// If not, createSession transaction will also enable this module along with storing session info on-chain.
const isEnabled = await biconomySmartAccount.isModuleEnabled(
managerModuleAddr
sessionKeyManagerModuleAddr
);
console.log("isSessionKeyModuleEnabled", isEnabled);
setIsSessionKeyModuleEnabled(isEnabled);
return;
} catch (err: any) {
console.error(err);
setLoading(false);
showErrorMessage("Error in getting session key module status");
setIsSessionKeyModuleEnabled(false);
return;
}
Expand All @@ -51,39 +56,65 @@ const CreateSession: React.FC = () => {
}
try {
let biconomySmartAccount = smartAccount;
const managerModuleAddr = DEFAULT_SESSION_KEY_MANAGER_MODULE;
const erc20ModuleAddr = "0x000000D50C68705bd6897B2d17c7de32FB519fDA";
const sessionKeyManagerModuleAddr = DEFAULT_SESSION_KEY_MANAGER_MODULE;
const erc20SessionValidationModuleAddr = ERC20_SESSION_VALIDATION_MODULE;

// -----> setMerkle tree tx flow
// create dapp side session key
const sessionSigner = ethers.Wallet.createRandom();
const sessionKeyEOA = await sessionSigner.getAddress();
console.log("sessionKeyEOA", sessionKeyEOA);
// BREWARE JUST FOR DEMO: update local storage with session key

// Optional: JUST FOR DEMO: update local storage with session key
// If you have session key-pair on the client side you can keep using those without making part of any storage
window.localStorage.setItem("sessionPKey", sessionSigner.privateKey);

// generate sessionModule
const sessionModule = await SessionKeyManagerModule.create({
moduleAddress: managerModuleAddr,
// Create an instanbce of Session Key Manager module from modules package
// This module is responsible for below tasks/helpers:
// a. Maintain session leaf storage in defined storage client (Biconomy by default using browser local storage which works for front-end apps)
// b. Generate dummy signature for userOp estimations
// c. Provides helpers to sign userOpHash with session key in the right format and generate proof for particular leaf
const sessionManagerModule = await SessionKeyManagerModule.create({
moduleAddress: sessionKeyManagerModuleAddr,
smartAccountAddress: scwAddress,
});

// cretae session key data
const tokenContract = new ethers.Contract(
config.usdc.address,
config.usdc.abi,
signer
);
let decimals = 18;

try {
decimals = await tokenContract.decimals();
} catch (error) {
throw new Error("invalid token address supplied");
}

// Cretae session key data
// Session key data is always corrsponding to the Session Validation Module being used
// It always requires the public address of the session key
// Rest of the details depends on the actual permissions
// Here, our ERC20 Session Validation Module verifies ERC20 address, receiver and max amount
//
const sessionKeyData = defaultAbiCoder.encode(
["address", "address", "address", "uint256"],
[
sessionKeyEOA,
"0xdA5289fCAAF71d52a80A254da614a192b693e977", // erc20 token address
"0x42138576848E839827585A3539305774D36B9602", // receiver address
ethers.utils.parseUnits("50".toString(), 6).toHexString(), // 50 usdc amount
config.usdc.address, // erc20 token address
"0x42138576848E839827585A3539305774D36B9602", // receiver address // You must send to same receiver when making use of the session
ethers.utils.parseUnits("50".toString(), decimals).toHexString(), // 50 usdc amount
]
);

const sessionTxData = await sessionModule.createSessionData([
// Below helper gives you tx data to be used to make a call from Smart Account to enable session on-chain
// This transaction needs a user signature and for gas sponsorship or ERC20 paymaster can be used.
const sessionTxData = await sessionManagerModule.createSessionData([
{
validUntil: 0,
validAfter: 0,
sessionValidationModule: erc20ModuleAddr,
validUntil: 0, // 0 value means extremes
validAfter: 0, // 0 value means extremes
sessionValidationModule: erc20SessionValidationModuleAddr,
sessionPublicKey: sessionKeyEOA,
sessionKeyData: sessionKeyData,
},
Expand All @@ -93,43 +124,38 @@ const CreateSession: React.FC = () => {

// tx to set session key
const tx2 = {
to: managerModuleAddr, // session manager module address
to: sessionKeyManagerModuleAddr, // session manager module address
data: sessionTxData.data,
};

let transactionArray = [];
if (enableSessionKeyModule) {
// -----> enableModule session manager module
const tx1 = await biconomySmartAccount.getEnableModuleData(
managerModuleAddr
sessionKeyManagerModuleAddr
);
transactionArray.push(tx1);
}
transactionArray.push(tx2);

// Building the user operation
// If you're going to use sponsorship paymaster details can be provided at this step
let partialUserOp = await biconomySmartAccount.buildUserOp(
transactionArray,
{
skipBundlerGasEstimation: false,
}
);

const userOpResponse = await smartAccount.sendUserOp(partialUserOp);
console.log("userOpHash", userOpResponse);
const { transactionHash } = await userOpResponse.waitForTxHash();
console.log("txHash", transactionHash);
showSuccessMessage(
`Session Created Successfully ${transactionHash}`,
transactionHash
// This will send user operation to potentially enable session key manager module and set the session
const userOpResponse = await biconomySmartAccount.sendUserOp(
partialUserOp
);

// update the session key //enableModule
/*await sessionRouterModule.updateSessionStatus(
{
sessionPublicKey: sessionKeyEOA,
sessionValidationModule: erc20ModuleAddr,
},
"ACTIVE"
);*/
console.log(`userOp Hash: ${userOpResponse.userOpHash}`);
const transactionDetails = await userOpResponse.wait();
console.log("txHash", transactionDetails.receipt.transactionHash);
showInfoMessage("Session Created Successfully");
} catch (err: any) {
console.error(err);
setLoading(false);
Expand Down Expand Up @@ -166,12 +192,12 @@ const CreateSession: React.FC = () => {
) : (
<div>
<p style={{ marginBottom: 20 }}>
This is single transaction to enable the sesion manager module and
set merkle root.
This is a single transaction to enable the sesion key manager module and
make a session active on-chain using ERC20 session validation module.
</p>

<Button
title="Enable And Create Session"
title="Enable Module And Create Session"
isLoading={loading}
onClickFunc={() => {
createSession(true);
Expand Down
1 change: 0 additions & 1 deletion src/components/Modules/ERC20RouterTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const ERC20RouterTransfer: React.FC = () => {
// get session key from local storage
const sessionKeyPrivKey = window.localStorage.getItem("sessionPKey");

console.log("sessionKeyPrivKey", sessionKeyPrivKey);
if (!sessionKeyPrivKey) {
showErrorMessage("Session key not found");
return;
Expand Down
1 change: 0 additions & 1 deletion src/components/Modules/ERC20Transfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const ERC20Transfer: React.FC = () => {
// get session key from local storage
const sessionKeyPrivKey = window.localStorage.getItem("sessionPKey");

console.log("sessionKeyPrivKey", sessionKeyPrivKey);
if (!sessionKeyPrivKey) {
showErrorMessage("Session key not found");
return;
Expand Down
15 changes: 1 addition & 14 deletions src/components/Modules/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,7 @@ const cardItems = [
}}
/>
),
},
{
title: "Mint NFT using Session",
description: "",
index: 13,
icon: (
<ImageIcon
style={{
color: "#FFB999",
fontSize: 72,
}}
/>
),
},
}
];

const SessionFlow: React.FC<Props> = ({
Expand Down
12 changes: 6 additions & 6 deletions src/components/TabsBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import ERC20Transfer from "./Modules/ERC20Transfer";
import CreateBatchRouter from "./Modules/CreateBatchRouter";
import ERC20RouterTransfer from "./Modules/ERC20RouterTransfer";

const drawerWidth = 260;
const drawerWidth = 350;
const onboardingList = [
{
name: "Home",
Expand Down Expand Up @@ -92,19 +92,19 @@ const ForwardList = [

const SessionList = [
{
name: "Create Session Module",
name: "Create ERC20 transfer Session",
icon: <GamesIcon />,
},
{
name: "Create Batch Router",
name: "Create Sessions (using Batch Router)",
icon: <GamesIcon />,
},
{
name: "ERC20 Transfer Session",
name: "ERC20 Transfer using Session",
icon: <CookieIcon />,
},
{
name: "ERC20 Batch Router",
name: "Use different Sessions in a batch call",
icon: <CookieIcon />,
}
];
Expand Down Expand Up @@ -344,7 +344,7 @@ const TabsBody = ({ loading }: { loading: boolean }) => {
<FiberNewIcon />
</ListItemIcon>
<ListItemText
primary="Session Module Demo"
primary="Session Keys Demo"
sx={{ opacity: open ? 1 : 0 }}
/>
{isSessionOpen ? <ExpandLess /> : <ExpandMore />}
Expand Down
5 changes: 5 additions & 0 deletions src/utils/chainConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export const supportedChains = [
ChainId.ARBITRUM_GOERLI
];

export const ERC20_SESSION_VALIDATION_MODULE = "0x000000D50C68705bd6897B2d17c7de32FB519fDA"

// Do not use this in production
export const MOCK_SESSION_VALIDATION_MODULE = ""

export const getRPCProvider = (chainId: number) => {
switch (chainId) {
case 1:
Expand Down
2 changes: 1 addition & 1 deletion src/utils/error-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type ErrorCode = "AA21" | "AA10";

// Add more error code and action items as needed
const errorCodeToActionMapping = {
"AA21" : "Send some native tokens in your smart wallet to be able to resolve the error.",
"AA21" : "Send some native tokens in your smart wallet to be able to resolve the error. Or use the paymaster!",
"AA10" : "Your smart wallet is already created but you are still sending initcode in userOp"
}

Expand Down
Loading

0 comments on commit c4b6750

Please sign in to comment.