Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/multi safe #52

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified apps/Wallet/.yarn/install-state.gz
Binary file not shown.
3 changes: 2 additions & 1 deletion apps/Wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"typechain": "typechain --target ethers-v5 --out-dir ./src/types/typechain/types/config/abis './src/types/typechain/abis/*.json'"
"typechain": "typechain --target ethers-v5 --always-generate-overloads --out-dir ./src/types/typechain/types/config/abis './src/types/typechain/abis/*.json'"
},
"dependencies": {
"@emotion/react": "11.11.1",
Expand All @@ -27,6 +27,7 @@
"@web3auth/openlogin-adapter": "7.0.1",
"dayjs": "1.11.10",
"ethers": "5.7.2",
"lru-cache": "10.0.1",
"mongoose": "7.6.2",
"next": "13.5.4",
"react": "18",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { UserManagementTransactionBody } from "@/types";
import { SafeTransactionDataPartial } from "@safe-global/safe-core-sdk-types";
import { ethers } from "ethers";
import { Rbac__factory } from "../../../../types/typechain/types/config/abis";
import { getSafe, getSigner } from "../util/utils";
import { Rbac__factory } from "../../../../../types/typechain/types/config/abis";
import { getSafe, getSigner } from "../../util/utils";
import { safeExists } from "../../util/safeExists";

export async function POST(req: Request, { params }: any): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

export async function POST(req: Request): Promise<Response> {
const body = (await req.json()) as UserManagementTransactionBody;

const safeSdk = await getSafe(process.env.OWNER_1_PRIVATE_KEY_GOERLI);
const safeSdk = await getSafe(params.safeAddress, body.pk);
const rbacModuleAddress = process.env.RBAC_MODULE_ADDRESS!;

const callData = await getAddUserCallData(body.pk, rbacModuleAddress, body.address);
Expand Down Expand Up @@ -39,4 +43,4 @@ async function getAddUserCallData(pk: string, rbacModuleAddress: string, delegat
const rbac = Rbac__factory.connect(rbacModuleAddress, signer);
const tx = await rbac.populateTransaction.addDelegate(delegateAddress);
return tx.data;
}
}
15 changes: 15 additions & 0 deletions apps/Wallet/src/app/api/safe/[safeAddress]/balance/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { safeExists } from "@/app/api/safe/util/safeExists";
import { getSafe } from "../../util/utils";
import { NextRequest } from "next/server";
import { NextApiRequest } from "next";

export async function GET(req: NextApiRequest, { params }: any, res: Response): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

const safeSdk = await getSafe(params.safeAddress);

const safeBalance = (await safeSdk.getBalance()).toString(); // using string to circumvent possible number range errors

return new Response(JSON.stringify(safeBalance));
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { SafeMultisigTransactionResponse } from "@safe-global/safe-core-sdk-types";
import { getSafeService } from "../../util/utils";
import { getSafeService } from "../../../util/utils";
import { BigNumber } from "ethers";
import { safeExists } from "../../../util/safeExists";

export async function GET(req: Request, res: Response): Promise<Response> {
export async function GET(req: Request, { params }: any, res: Response): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });
const safeSdk = await getSafeService();

const txs = await safeSdk.getMultisigTransactions(process.env.SAFE_ADDRESS!);
Expand All @@ -11,6 +14,5 @@ export async function GET(req: Request, res: Response): Promise<Response> {
return acc.add(BigNumber.from(tx.value));
}, BigNumber.from(0));


return new Response(JSON.stringify(tokenBalance.toString()));
}
22 changes: 22 additions & 0 deletions apps/Wallet/src/app/api/safe/[safeAddress]/fund-user/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GOERLI_FUND_GAS_AMOUNT } from "@/features/safe-ownership/constants";
import { FundUserTransactionBody } from "@/types/safe-transaction";
import { createTransaction } from "../../util/utils";
import { SafeAddressParams } from "@/types";
import { safeExists } from "../../util/safeExists";

export async function POST(req: Request, { params }: SafeAddressParams): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

let body = (await req.json()) as FundUserTransactionBody;
const pk = process.env.OWNER_1_PRIVATE_KEY_GOERLI!;
const destination = body.destination;
const sendBody = {
destination,
pk,
amount: GOERLI_FUND_GAS_AMOUNT,
};
const txReceipt = await createTransaction(params.safeAddress, sendBody);

return new Response(JSON.stringify(txReceipt));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { safeExists } from "../../../util/safeExists";
import { getSafeService } from "../../../util/utils";

export async function GET(req: Request, { params }: any): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

const safeSdk = await getSafeService();

const txs = await safeSdk.getModuleTransactions(params.safeAddress);

const history = txs.results;
return new Response(JSON.stringify(history));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { NextRequest } from "next/server";
import { getSafe } from "../../../util/utils";
import { safeExists } from "../../../util/safeExists";

export async function GET(req: NextRequest, { params }: any): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

const safeSdk = await getSafe(params.safeAddress);

const isOwner = await safeSdk.isOwner(params.ownerAddress);
return new Response(JSON.stringify(isOwner));
}
21 changes: 21 additions & 0 deletions apps/Wallet/src/app/api/safe/[safeAddress]/owner/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SafeAddressParams, UserManagementTransactionBody } from "@/types";
import { getSafe } from "../../util/utils";
import { safeExists } from "../../util/safeExists";

export async function POST(req: Request, { params }: SafeAddressParams): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

const body = (await req.json()) as UserManagementTransactionBody;

const safeSdk = await getSafe(params.safeAddress, body.pk);

let safeTransaction = await safeSdk.createAddOwnerTx({ ownerAddress: body.address });
safeTransaction = await safeSdk.signTransaction(safeTransaction);

const executeTxResponse = await safeSdk.executeTransaction(safeTransaction);

const receipt = await executeTxResponse.transactionResponse?.wait();
console.log(receipt);
return new Response(JSON.stringify(receipt));
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { UserManagementTransactionBody } from "@/types";
import { SafeAddressParams, UserManagementTransactionBody } from "@/types";
import { SafeTransactionDataPartial } from "@safe-global/safe-core-sdk-types";
import { ethers } from "ethers";
import { Rbac__factory } from "../../../../types/typechain/types/config/abis";
import { getSafe, getSigner } from "../util/utils";
import { Rbac__factory } from "../../../../../types/typechain/types/config/abis";
import { getSafe, getSigner } from "../../util/utils";
import { safeExists } from "../../util/safeExists";

export async function POST(req: Request, { params }: SafeAddressParams): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

export async function POST(req: Request): Promise<Response> {
const body = (await req.json()) as UserManagementTransactionBody;

const safeSdk = await getSafe(process.env.OWNER_1_PRIVATE_KEY_GOERLI);
const safeSdk = await getSafe(params.safeAddress, body.pk);
const rbacModuleAddress = process.env.RBAC_MODULE_ADDRESS!;

const callData = await getRemoveUserCallData(body.pk, rbacModuleAddress, body.address);
Expand Down Expand Up @@ -39,4 +43,4 @@ async function getRemoveUserCallData(pk: string, rbacModuleAddress: string, dele
const rbac = Rbac__factory.connect(rbacModuleAddress, signer);
const tx = await rbac.populateTransaction.removeDelegate(delegateAddress);
return tx.data;
}
}
13 changes: 13 additions & 0 deletions apps/Wallet/src/app/api/safe/[safeAddress]/transaction/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { safeExists } from "../../util/safeExists";
import { createTransaction } from "../../util/utils";
import { SafeAddressParams, SafeTransactionBody } from "@/types";

export async function POST(req: Request, { params }: SafeAddressParams): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

const body = (await req.json()) as SafeTransactionBody;
const txReceipt = await createTransaction(params.safeAddress, body);

return new Response(JSON.stringify(txReceipt));
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ExecuteUserTransactionBody } from "@/types";
import { Rbac__factory } from "../../../../../types/typechain/types/config/abis";
import { getEtherscanSigner } from "../../util/utils";
import { ethers } from "ethers";
import { Rbac__factory } from "../../../../types/typechain/types/config/abis";
import { getEtherscanSigner } from "../util/utils";

export async function POST(req: Request): Promise<Response> {
const body = (await req.json()) as ExecuteUserTransactionBody;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import dbConnect from "@/lib/dbConnect";
import user, { User } from "@/models/User";
import { safeExists } from "../../../util/safeExists";

export async function GET(req: Request, { params }: any): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

await dbConnect();

const usr = await user.findOne<User>({ publicKey: params.publicKey, safeAddress: params.safeAddress });

return new Response(JSON.stringify(usr));
}
14 changes: 14 additions & 0 deletions apps/Wallet/src/app/api/safe/[safeAddress]/users/is-user/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { AddressTransactionBody } from "@/types";
import { Rbac__factory } from "../../../../../../types/typechain/types/config/abis";
import { getEtherscanProvider } from "../../../util/utils";

export async function GET(req: Request, res: Response): Promise<Response> {
const body = (await req.json()) as AddressTransactionBody;
const provider = getEtherscanProvider();
const rbacModuleAddress = process.env.RBAC_MODULE_ADDRESS!;
const rbac = Rbac__factory.connect(rbacModuleAddress, provider);
const safe = process.env.SAFE_ADDRESS!;
const isUser = await rbac.isDelegate(safe, body.address);

return new Response(JSON.stringify(isUser));
}
41 changes: 41 additions & 0 deletions apps/Wallet/src/app/api/safe/[safeAddress]/users/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import dbConnect from "@/lib/dbConnect";
import user, { User } from "@/models/User";
import { User as IUser, SafeAddressParams } from "@/types";
import { NextRequest } from "next/server";
import { safeExists } from "../../util/safeExists";

export async function POST(req: Request, { params }: SafeAddressParams): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

await dbConnect();

const body = (await req.json()) as IUser;
const userResponse = await user.create<User>({ ...body, safeAddress: params.safeAddress });

return new Response(JSON.stringify(userResponse));
}

export async function GET(req: NextRequest, { params }: SafeAddressParams): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

await dbConnect();

const role = req.nextUrl.searchParams.getAll("role");
const users = await user.find<User>({ safeAddress: params.safeAddress, role: { $in: role } });

return new Response(JSON.stringify(users));
}

export async function PUT(req: Request, { params }: SafeAddressParams): Promise<Response> {
const exists = await safeExists(params.safeAddress);
if (!exists) return Response.json({}, { status: 404, statusText: "Safe not found." });

await dbConnect();

const body = (await req.json()) as Partial<IUser>;
const userResponse = await user.findOneAndUpdate<User>({ publicKey: body.publicKey }, body);

return new Response(JSON.stringify(userResponse));
}
9 changes: 0 additions & 9 deletions apps/Wallet/src/app/api/safe/balance/route.ts

This file was deleted.

18 changes: 0 additions & 18 deletions apps/Wallet/src/app/api/safe/fund-user/route.ts

This file was deleted.

11 changes: 0 additions & 11 deletions apps/Wallet/src/app/api/safe/history/transactions/route.ts

This file was deleted.

9 changes: 0 additions & 9 deletions apps/Wallet/src/app/api/safe/owner/[ownerAddress]/route.ts

This file was deleted.

17 changes: 0 additions & 17 deletions apps/Wallet/src/app/api/safe/owner/route.ts

This file was deleted.

41 changes: 41 additions & 0 deletions apps/Wallet/src/app/api/safe/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { DeploySafeTransactionBody } from "@/types";
import { getEthAdapter } from "./util/utils";
import Safe, { SafeAccountConfig, SafeFactory } from "@safe-global/protocol-kit";
import dbConnect from "@/lib/dbConnect";
import safe from "@/models/Safe";

export async function POST(req: Request): Promise<Response> {
try {
await dbConnect();

const body = (await req.json()) as DeploySafeTransactionBody;

const ethAdapter = getEthAdapter(body.pk);
const signerAddress = await ethAdapter.getSignerAddress();

const safeFactory = await SafeFactory.create({ ethAdapter });
const safeAccountConfig: SafeAccountConfig = {
owners: [signerAddress!],
threshold: 1,
};

const saltNonce = (Math.floor(Math.random() * (9999 - 1000 + 1)) + 1000).toString();

const safeSdkOwner1 = await safeFactory.deploySafe({ safeAccountConfig, saltNonce });
let enableModuleTx = await safeSdkOwner1.createEnableModuleTx(process.env.RBAC_MODULE_ADDRESS!);
enableModuleTx = await safeSdkOwner1.signTransaction(enableModuleTx);

const transaction = (await safeSdkOwner1.executeTransaction(enableModuleTx)).transactionResponse;

const storedSafe = await safe.create<Safe>({
name: body.name,
owner: body.address,
address: await safeSdkOwner1.getAddress(),
creation: transaction?.timestamp || Date.now(),
});

return new Response(JSON.stringify(storedSafe));
} catch (error: any) {
return Response.json({}, { status: 500, statusText: "Something went wrong whilst creating a wallet." });
}
}
Loading