-
Notifications
You must be signed in to change notification settings - Fork 9
feat: session key support and Safe multisig tooling #416
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
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| /** | ||
| * Generate a session key and Safe multisig calldata for registering it | ||
| * on the SessionKeyRegistry contract. | ||
| * | ||
| * Usage: | ||
| * node scripts/create-session-key-safe.mjs [--network mainnet|calibration] [--expiry-days 90] [--session-key 0x...] | ||
| * | ||
| * If --session-key is omitted, a random key is generated. | ||
| * | ||
| * Outputs: | ||
| * 1. Session key address and private key | ||
| * 2. Permission hashes being registered | ||
| * 3. Safe transaction details (target, calldata, value) | ||
| * 4. Verification: decoded calldata for review | ||
| * 5. Env vars for DealBot deployment | ||
| * | ||
| * The calldata should be submitted as a custom transaction in the Safe UI | ||
| * (app.safe.global) from the DealBot multisig wallet. | ||
| */ | ||
|
|
||
| import { calibration, mainnet } from "@filoz/synapse-core/chains"; | ||
| import { | ||
| AddPiecesPermission, | ||
| CreateDataSetPermission, | ||
| DefaultFwssPermissions, | ||
| DeleteDataSetPermission, | ||
| loginCall, | ||
| SchedulePieceRemovalsPermission, | ||
| } from "@filoz/synapse-core/session-key"; | ||
| import { decodeFunctionData, encodeFunctionData } from "viem"; | ||
| import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; | ||
|
|
||
| // Parse CLI args | ||
| const args = process.argv.slice(2); | ||
| function getArg(name) { | ||
| const idx = args.indexOf(name); | ||
| return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined; | ||
| } | ||
|
|
||
| const networkName = getArg("--network") || "calibration"; | ||
| const expiryDays = Number(getArg("--expiry-days") || "90"); | ||
| const sessionPrivateKey = getArg("--session-key") || generatePrivateKey(); | ||
|
|
||
| const chain = networkName === "mainnet" ? mainnet : calibration; | ||
| const sessionAccount = privateKeyToAccount(sessionPrivateKey); | ||
| const expiresAt = BigInt(Math.floor(Date.now() / 1000) + expiryDays * 24 * 60 * 60); | ||
| const expiryDate = new Date(Number(expiresAt) * 1000); | ||
|
|
||
| // Use the SDK's loginCall to get the exact ABI and args | ||
| const call = loginCall({ | ||
| chain, | ||
| address: sessionAccount.address, | ||
| permissions: DefaultFwssPermissions, | ||
| expiresAt, | ||
| origin: "dealbot", | ||
| }); | ||
|
|
||
| // Encode the calldata | ||
| const calldata = encodeFunctionData({ | ||
| abi: call.abi, | ||
| functionName: call.functionName, | ||
| args: call.args, | ||
| }); | ||
|
|
||
| // Verify by decoding it back | ||
| const decoded = decodeFunctionData({ | ||
| abi: call.abi, | ||
| data: calldata, | ||
| }); | ||
|
|
||
| // Permission labels for display | ||
| const permissionLabels = { | ||
| [CreateDataSetPermission]: "CreateDataSet", | ||
| [AddPiecesPermission]: "AddPieces", | ||
| [SchedulePieceRemovalsPermission]: "SchedulePieceRemovals", | ||
| [DeleteDataSetPermission]: "DeleteDataSet", | ||
| }; | ||
|
|
||
| // Output | ||
| console.log("=== Session Key Registration for Safe Multisig ==="); | ||
| console.log(); | ||
| console.log(`Network: ${networkName} (chain ${chain.id})`); | ||
| console.log(`Session key addr: ${sessionAccount.address}`); | ||
| console.log(`Expiry: ${expiryDate.toISOString()} (${expiryDays} days)`); | ||
| console.log(`Origin: dealbot`); | ||
| console.log(); | ||
| console.log("--- Permissions ---"); | ||
| for (const hash of DefaultFwssPermissions) { | ||
| console.log(` ${permissionLabels[hash] || "Unknown"}: ${hash}`); | ||
| } | ||
| console.log(); | ||
| console.log("--- Safe Transaction ---"); | ||
| console.log(`Target (SessionKeyRegistry): ${call.address}`); | ||
| console.log(`Value: 0`); | ||
| console.log(`Calldata:`); | ||
| console.log(calldata); | ||
| console.log(); | ||
| console.log("--- Verification (decoded calldata) ---"); | ||
| console.log(`Function: ${decoded.functionName}`); | ||
| console.log(`Args:`); | ||
| console.log(` signer: ${decoded.args[0]}`); | ||
| console.log(` expiry: ${decoded.args[1]} (${new Date(Number(decoded.args[1]) * 1000).toISOString()})`); | ||
| console.log(` permissions: [`); | ||
| for (const p of decoded.args[2]) { | ||
| console.log(` ${p} (${permissionLabels[p] || "Unknown"})`); | ||
| } | ||
| console.log(` ]`); | ||
| console.log(` origin: "${decoded.args[3]}"`); | ||
| console.log(); | ||
| console.log("--- Safe UI Steps ---"); | ||
| console.log("1. Go to safe.filecoin.io and open the DealBot multisig"); | ||
| console.log("2. New Transaction > Transaction Builder"); | ||
| console.log(`3. Enter contract address: ${call.address}`); | ||
| console.log('4. Select "Custom data (hex encoded)"'); | ||
| console.log("5. Paste the calldata above"); | ||
| console.log("6. Value: 0"); | ||
| console.log("7. Review, sign, and collect required signatures"); | ||
| console.log(); | ||
| console.log("--- DealBot Env Vars (for SOPS secrets) ---"); | ||
| console.log(`SESSION_KEY_PRIVATE_KEY=${sessionPrivateKey}`); | ||
| console.log(); | ||
| console.log("--- Renewal ---"); | ||
| console.log("To renew, run this script again with the same --session-key"); | ||
| console.log("and submit the new calldata via Safe. The contract overwrites"); | ||
| console.log("the previous registration for the same signer address."); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| /** | ||
| * Generate Safe multisig calldata for depositing USDFC into Filecoin Pay | ||
| * and approving FWSS as an operator. | ||
| * | ||
| * Usage: | ||
| * node scripts/fund-safe.mjs --network mainnet|calibration --amount 50 --wallet-address 0x... | ||
| * | ||
| * Outputs a 3-transaction batch for the Safe Transaction Builder: | ||
| * 1. USDFC.approve(FilecoinPay, amount) | ||
| * 2. FilecoinPay.deposit(USDFC, walletAddress, amount) | ||
| * 3. FilecoinPay.setOperatorApproval(USDFC, FWSS, true, maxUint256, maxUint256, maxUint256) | ||
| * | ||
| * Prerequisites: the multisig must hold USDFC tokens (ERC20 balance, not | ||
| * Filecoin Pay balance). Transfer USDFC to the multisig first if needed. | ||
| */ | ||
|
|
||
| import { calibration, mainnet } from "@filoz/synapse-core/chains"; | ||
| import { encodeFunctionData, parseUnits } from "viem"; | ||
|
|
||
| const args = process.argv.slice(2); | ||
| function getArg(name) { | ||
| const idx = args.indexOf(name); | ||
| return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined; | ||
| } | ||
|
|
||
| const networkName = getArg("--network") || "calibration"; | ||
| const amountStr = getArg("--amount"); | ||
| const walletAddress = getArg("--wallet-address"); | ||
|
|
||
| if (!amountStr) { | ||
| console.error("--amount is required (e.g. --amount 50 for 50 USDFC)"); | ||
| process.exit(1); | ||
| } | ||
| if (!walletAddress) { | ||
| console.error("--wallet-address is required (the multisig address to credit)"); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const chain = networkName === "mainnet" ? mainnet : calibration; | ||
| const amount = parseUnits(amountStr, 18); | ||
| const maxUint256 = 2n ** 256n - 1n; | ||
|
|
||
| const usdfcAddress = chain.contracts.usdfc.address; | ||
| const filecoinPayAddress = chain.contracts.filecoinPay.address; | ||
| const fwssAddress = chain.contracts.fwss.address; | ||
|
|
||
| // Transaction 1: ERC20 approve | ||
| const approveCalldata = encodeFunctionData({ | ||
| abi: [ | ||
| { | ||
| type: "function", | ||
| name: "approve", | ||
| inputs: [ | ||
| { name: "spender", type: "address" }, | ||
| { name: "amount", type: "uint256" }, | ||
| ], | ||
| outputs: [{ type: "bool" }], | ||
| stateMutability: "nonpayable", | ||
| }, | ||
| ], | ||
| functionName: "approve", | ||
| args: [filecoinPayAddress, amount], | ||
| }); | ||
|
|
||
| // Transaction 2: deposit | ||
| const depositCalldata = encodeFunctionData({ | ||
| abi: chain.contracts.filecoinPay.abi, | ||
| functionName: "deposit", | ||
| args: [usdfcAddress, walletAddress, amount], | ||
| }); | ||
|
|
||
| // Transaction 3: setOperatorApproval | ||
| const approveOperatorCalldata = encodeFunctionData({ | ||
| abi: chain.contracts.filecoinPay.abi, | ||
| functionName: "setOperatorApproval", | ||
| args: [usdfcAddress, fwssAddress, true, maxUint256, maxUint256, maxUint256], | ||
| }); | ||
|
|
||
| console.log("=== Payment Setup for Safe Multisig ==="); | ||
| console.log(); | ||
| console.log(`Network: ${networkName} (chain ${chain.id})`); | ||
| console.log(`Wallet: ${walletAddress}`); | ||
| console.log(`Deposit: ${amountStr} USDFC`); | ||
| console.log(`USDFC: ${usdfcAddress}`); | ||
| console.log(`FilecoinPay: ${filecoinPayAddress}`); | ||
| console.log(`FWSS: ${fwssAddress}`); | ||
| console.log(); | ||
| console.log("--- Transaction 1: Approve USDFC spend ---"); | ||
| console.log(`Target: ${usdfcAddress}`); | ||
| console.log(`Value: 0`); | ||
| console.log(`Data: ${approveCalldata}`); | ||
| console.log(); | ||
| console.log("--- Transaction 2: Deposit into Filecoin Pay ---"); | ||
| console.log(`Target: ${filecoinPayAddress}`); | ||
| console.log(`Value: 0`); | ||
| console.log(`Data: ${depositCalldata}`); | ||
| console.log(); | ||
| console.log("--- Transaction 3: Approve FWSS operator ---"); | ||
| console.log(`Target: ${filecoinPayAddress}`); | ||
| console.log(`Value: 0`); | ||
| console.log(`Data: ${approveOperatorCalldata}`); | ||
| console.log(); | ||
| console.log("--- Safe UI Steps ---"); | ||
| console.log("1. Go to safe.filecoin.io and open the multisig"); | ||
| console.log("2. New Transaction > Transaction Builder"); | ||
| console.log("3. Add all 3 transactions above (target + calldata for each)"); | ||
| console.log("4. Send Batch, review, sign, and collect required signatures"); | ||
| console.log(); | ||
| console.log("Note: The multisig must hold USDFC tokens before executing."); | ||
| console.log("Transfer USDFC to the multisig address first if needed."); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.