From 356b50ba027ab694e07367d8c4b39d705a178130 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Thu, 8 May 2025 18:21:29 -0600 Subject: [PATCH 01/12] chore(release): 0.1.20-beta.0 --- CHANGELOG.md | 2 ++ package.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57c9fb88..c065cab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.20-beta.0](https://github.com/bandohq/widget/compare/v0.1.19...v0.1.20-beta.0) (2025-05-09) + ### [0.1.19](https://github.com/bandohq/widget/compare/v0.1.19-beta.0...v0.1.19) (2025-05-05) ### [0.1.19-beta.0](https://github.com/bandohq/widget/compare/v0.1.18...v0.1.19-beta.0) (2025-05-05) diff --git a/package.json b/package.json index d902655b..67c19a81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bandohq/widget", - "version": "0.1.19", + "version": "0.1.20-beta.0", "license": "Apache-2.0", "type": "commonjs", "main": "dist/index.js", @@ -51,6 +51,7 @@ "@mui/material": "^6.1.5", "@mui/system": "^6.1.6", "@phosphor-icons/react": "^2.1.7", + "@safe-global/safe-apps-sdk": "^9.1.0", "@solana/wallet-adapter-base": "^0.9.23", "@solana/web3.js": "^1.95.3", "@tanstack/react-virtual": "^3.10.9", From e078f172e7703fdccea973909c2cfe618775c881 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Thu, 8 May 2025 18:21:54 -0600 Subject: [PATCH 02/12] add config for multisig --- src/hooks/useTransactionHelpers.ts | 88 ++++++++++++++++++++++-------- src/utils/checkAllowance.ts | 28 +++++++--- src/utils/safeFunctions.ts | 30 ++++++++++ 3 files changed, 116 insertions(+), 30 deletions(-) create mode 100644 src/utils/safeFunctions.ts diff --git a/src/hooks/useTransactionHelpers.ts b/src/hooks/useTransactionHelpers.ts index edaf99d0..6755de2f 100644 --- a/src/hooks/useTransactionHelpers.ts +++ b/src/hooks/useTransactionHelpers.ts @@ -1,15 +1,16 @@ -import { defineChain, parseUnits } from "viem"; +import { defineChain, encodeFunctionData, parseUnits } from "viem"; import { transformToChainConfig } from "../utils/TransformToChainConfig"; import BandoRouter from "@bandohq/contract-abis/abis/BandoRouterV1.json"; import nativeTokenCatalog from "../utils/nativeTokenCatalog"; -import { writeContract } from '@wagmi/core' -import { ERC20ApproveABI } from "../utils/abis"; +import { writeContract } from "@wagmi/core"; +import { ERC20ApproveABI } from "../utils/abis"; import { validateReference } from "../utils/validateReference"; import { useConfig } from "wagmi"; import { useNotificationContext } from "../providers/AlertProvider/NotificationProvider"; import { checkAllowance } from "../utils/checkAllowance"; import { useSteps } from "../providers/StepsProvider/StepsProvider"; import { useCallback } from "react"; +import { detectMultisig, sdk, sendViaSafe } from "../utils/safeFunctions"; export const useTransactionHelpers = () => { const config = useConfig(); @@ -28,7 +29,20 @@ export const useTransactionHelpers = () => { chain, config ) => { + const isMultisig = await detectMultisig(); + try { + if (isMultisig) { + sendViaSafe({ + to: tokenAddress, + abi: ERC20ApproveABI, + functionName: "approve", + args: [spenderAddress, amount], + }); + + return true; + } + await writeContract(config, { address: tokenAddress, abi: ERC20ApproveABI, @@ -54,6 +68,8 @@ export const useTransactionHelpers = () => { serviceID, formattedChain, }) => { + const isMultisig = await detectMultisig(); + const requestServiceABI = BandoRouter.abi.find( (item) => item.name === "requestService" ); @@ -68,7 +84,9 @@ export const useTransactionHelpers = () => { ); const payload = { - payer: account?.address, + payer: isMultisig + ? (await sdk.safe.getInfo()).safeAddress + : account?.address, fiatAmount: formatFiatAmount(quote?.totalAmount), serviceRef: txId, weiAmount, @@ -76,15 +94,23 @@ export const useTransactionHelpers = () => { addStep({ message: "form.status.signTransaction", type: "info" }); - await writeContract(config, { - value, - address: chain?.protocolContracts?.BandoRouterProxy, - abi: [requestServiceABI], - functionName: "requestService", - args: [serviceID, payload], - chain: formattedChain, - account: account?.address, - }); + if (isMultisig) { + await sendViaSafe({ + to: chain?.protocolContracts?.BandoRouterProxy, + abi: [requestServiceABI], + functionName: "requestService", + args: [serviceID, payload], + }); + } else { + await writeContract(config, { + value, + address: chain?.protocolContracts?.BandoRouterProxy, + abi: [requestServiceABI], + functionName: "requestService", + chain: formattedChain, + account: account?.address, + }); + } updateStep({ message: "form.status.signTransactionCompleted", @@ -100,6 +126,7 @@ export const useTransactionHelpers = () => { serviceID, token, }) => { + const isMultisig = await detectMultisig(); const totalAmount = parseFloat(quote?.totalAmount); const increaseAmount = totalAmount * 1.01; //Add 1% to the total amount for allowance issue const amountInUnits = parseUnits( @@ -112,7 +139,7 @@ export const useTransactionHelpers = () => { variables: { amount: increaseAmount, tokenSymbol: token?.symbol }, }); - await approveERC20( + const approveResult = await approveERC20( chain?.protocolContracts?.BandoRouterProxy, amountInUnits, token.address, @@ -121,6 +148,10 @@ export const useTransactionHelpers = () => { config ); + if (!approveResult) { + throw new Error("Failed to approve tokens"); + } + updateStep({ message: "form.status.validateAllowance", type: "loading" }); await checkAllowance( @@ -142,7 +173,9 @@ export const useTransactionHelpers = () => { ); const payload = { - payer: account?.address, + payer: isMultisig + ? (await sdk.safe.getInfo()).safeAddress + : account?.address, fiatAmount: formatFiatAmount(quote?.totalAmount), serviceRef: txId, token: token.address, @@ -154,14 +187,23 @@ export const useTransactionHelpers = () => { addStep({ message: "form.status.signTransaction", type: "info" }); - await writeContract(config, { - address: chain?.protocolContracts?.BandoRouterProxy, - abi: [requestERC20ServiceABI], - functionName: "requestERC20Service", - args: [serviceID, payload], - chain: chain.chainId, - account: account?.address, - }); + if (isMultisig) { + await sendViaSafe({ + to: chain?.protocolContracts?.BandoRouterProxy, + abi: [requestERC20ServiceABI], + functionName: "requestERC20Service", + args: [serviceID, payload], + }); + } else { + await writeContract(config, { + address: chain?.protocolContracts?.BandoRouterProxy, + abi: [requestERC20ServiceABI], + functionName: "requestERC20Service", + args: [serviceID, payload], + chain: chain.chainId, + account: account?.address, + }); + } updateStep({ message: "form.status.signTransactionCompleted", diff --git a/src/utils/checkAllowance.ts b/src/utils/checkAllowance.ts index 249040c9..6a9247d3 100644 --- a/src/utils/checkAllowance.ts +++ b/src/utils/checkAllowance.ts @@ -1,5 +1,6 @@ import { readContract } from "@wagmi/core"; import { ERC20AllowanceABI } from "../utils/abis"; +import { detectMultisig, sdk } from "./safeFunctions"; export const checkAllowance = async ( spenderAddress, @@ -13,17 +14,30 @@ export const checkAllowance = async ( const delay = 5000; let attempt = 0; let allowance = BigInt(0); + const isMultisig = await detectMultisig(); while (attempt < maxAttempts) { try { attempt++; - allowance = (await readContract(config, { - address: tokenAddress, - abi: ERC20AllowanceABI, - functionName: "allowance", - args: [account?.address, spenderAddress], - chainId: chain?.chainId, - })) as bigint; + + if (isMultisig) { + const safeInfo = await sdk.safe.getInfo(); + allowance = (await readContract(config, { + address: tokenAddress, + abi: ERC20AllowanceABI, + functionName: "allowance", + args: [safeInfo.safeAddress, spenderAddress], + chainId: chain?.chainId, + })) as bigint; + } else { + allowance = (await readContract(config, { + address: tokenAddress, + abi: ERC20AllowanceABI, + functionName: "allowance", + args: [account?.address, spenderAddress], + chainId: chain?.chainId, + })) as bigint; + } console.log( `Allowance check attempt ${attempt}:`, diff --git a/src/utils/safeFunctions.ts b/src/utils/safeFunctions.ts new file mode 100644 index 00000000..62cdff80 --- /dev/null +++ b/src/utils/safeFunctions.ts @@ -0,0 +1,30 @@ +import SafeAppsSDK from "@safe-global/safe-apps-sdk"; +import { encodeFunctionData } from "viem"; + +export const sdk = new SafeAppsSDK(); + +export const detectMultisig = async (): Promise => { + try { + const info = await Promise.race([ + sdk.safe.getInfo(), + new Promise((res) => setTimeout(res, 300)), + ]); + return !!info?.safeAddress; + } catch { + return false; + } +}; + +export const sendViaSafe = async ({ to, abi, functionName, args }) => { + const data = encodeFunctionData({ + abi, + functionName, + args, + }); + + await sdk.txs.send({ + txs: [{ to, value: "0", data }], + }); + + return { success: true }; +}; From bc61537223bcd3dd4270bf92cdc2463a9d39a317 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Thu, 8 May 2025 18:22:07 -0600 Subject: [PATCH 03/12] chore(release): 0.1.20-beta.1 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c065cab1..796b0bdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.20-beta.1](https://github.com/bandohq/widget/compare/v0.1.20-beta.0...v0.1.20-beta.1) (2025-05-09) + ### [0.1.20-beta.0](https://github.com/bandohq/widget/compare/v0.1.19...v0.1.20-beta.0) (2025-05-09) ### [0.1.19](https://github.com/bandohq/widget/compare/v0.1.19-beta.0...v0.1.19) (2025-05-05) diff --git a/package.json b/package.json index 67c19a81..592cdad6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bandohq/widget", - "version": "0.1.20-beta.0", + "version": "0.1.20-beta.1", "license": "Apache-2.0", "type": "commonjs", "main": "dist/index.js", From 670b85d826f2806ee8a61d2946fcbd968ab01f26 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Fri, 9 May 2025 15:14:41 -0600 Subject: [PATCH 04/12] chore(release): 0.1.20-beta.2 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 796b0bdd..1d21c867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.20-beta.2](https://github.com/bandohq/widget/compare/v0.1.20-beta.1...v0.1.20-beta.2) (2025-05-09) + ### [0.1.20-beta.1](https://github.com/bandohq/widget/compare/v0.1.20-beta.0...v0.1.20-beta.1) (2025-05-09) ### [0.1.20-beta.0](https://github.com/bandohq/widget/compare/v0.1.19...v0.1.20-beta.0) (2025-05-09) diff --git a/package.json b/package.json index 592cdad6..96d8c2c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bandohq/widget", - "version": "0.1.20-beta.1", + "version": "0.1.20-beta.2", "license": "Apache-2.0", "type": "commonjs", "main": "dist/index.js", From 4d674581ecfafb908cefc1102c9aec4d5f2067ea Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Fri, 9 May 2025 15:21:00 -0600 Subject: [PATCH 05/12] add async call to approval --- src/hooks/useTransactionHelpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useTransactionHelpers.ts b/src/hooks/useTransactionHelpers.ts index 6755de2f..ea413b1f 100644 --- a/src/hooks/useTransactionHelpers.ts +++ b/src/hooks/useTransactionHelpers.ts @@ -1,4 +1,4 @@ -import { defineChain, encodeFunctionData, parseUnits } from "viem"; +import { defineChain, parseUnits } from "viem"; import { transformToChainConfig } from "../utils/TransformToChainConfig"; import BandoRouter from "@bandohq/contract-abis/abis/BandoRouterV1.json"; import nativeTokenCatalog from "../utils/nativeTokenCatalog"; @@ -33,7 +33,7 @@ export const useTransactionHelpers = () => { try { if (isMultisig) { - sendViaSafe({ + await sendViaSafe({ to: tokenAddress, abi: ERC20ApproveABI, functionName: "approve", From 0fa36cbcd557ba8f2c341eabc78175b6d9fcbb9d Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Mon, 12 May 2025 14:05:11 -0600 Subject: [PATCH 06/12] fixes for multisig transactions --- src/hooks/useTransactionHelpers.ts | 84 ++++++++++++++++++++++-------- src/utils/checkAllowance.ts | 9 +++- src/utils/safeFunctions.ts | 4 +- 3 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/hooks/useTransactionHelpers.ts b/src/hooks/useTransactionHelpers.ts index ea413b1f..af4ef150 100644 --- a/src/hooks/useTransactionHelpers.ts +++ b/src/hooks/useTransactionHelpers.ts @@ -33,14 +33,16 @@ export const useTransactionHelpers = () => { try { if (isMultisig) { - await sendViaSafe({ + const safeResponse = await sendViaSafe({ to: tokenAddress, abi: ERC20ApproveABI, functionName: "approve", args: [spenderAddress, amount], }); - return true; + console.log("Safe transaction submitted:", safeResponse); + + return { success: true, isMultisig: true, safeResponse }; } await writeContract(config, { @@ -52,11 +54,11 @@ export const useTransactionHelpers = () => { account: account?.address, }); - return true; + return { success: true, isMultisig: false }; } catch (error) { showNotification("error", "Error on approving tokens, try later"); console.error("Error on approving tokens:", error); - return false; + return { success: false }; } }; @@ -95,12 +97,22 @@ export const useTransactionHelpers = () => { addStep({ message: "form.status.signTransaction", type: "info" }); if (isMultisig) { - await sendViaSafe({ + const safeResponse = await sendViaSafe({ to: chain?.protocolContracts?.BandoRouterProxy, abi: [requestServiceABI], functionName: "requestService", args: [serviceID, payload], }); + + console.log("Safe transaction submitted:", safeResponse); + + updateStep({ + message: "form.status.signTransaction", + type: "info", + description: "form.status.wait", + }); + + return { success: true, isMultisig: true, safeResponse }; } else { await writeContract(config, { value, @@ -110,12 +122,14 @@ export const useTransactionHelpers = () => { chain: formattedChain, account: account?.address, }); - } - updateStep({ - message: "form.status.signTransactionCompleted", - type: "completed", - }); + updateStep({ + message: "form.status.signTransactionCompleted", + type: "completed", + }); + + return { success: true, isMultisig: false }; + } }; const handleERC20TokenRequest = async ({ @@ -148,10 +162,20 @@ export const useTransactionHelpers = () => { config ); - if (!approveResult) { + if (!approveResult.success) { throw new Error("Failed to approve tokens"); } + if (approveResult.isMultisig) { + updateStep({ + message: "form.status.approveTokens", + type: "info", + description: "form.status.wait", + variables: { amount: increaseAmount, tokenSymbol: token?.symbol }, + }); + return approveResult; + } + updateStep({ message: "form.status.validateAllowance", type: "loading" }); await checkAllowance( @@ -160,7 +184,8 @@ export const useTransactionHelpers = () => { account, chain, config, - parseUnits(quote?.totalAmount.toString(), token?.decimals) + parseUnits(quote?.totalAmount.toString(), token?.decimals), + isMultisig ); updateStep({ @@ -188,12 +213,22 @@ export const useTransactionHelpers = () => { addStep({ message: "form.status.signTransaction", type: "info" }); if (isMultisig) { - await sendViaSafe({ + const safeResponse = await sendViaSafe({ to: chain?.protocolContracts?.BandoRouterProxy, abi: [requestERC20ServiceABI], functionName: "requestERC20Service", args: [serviceID, payload], }); + + console.log("Safe ERC20 transaction submitted:", safeResponse); + + updateStep({ + message: "form.status.signTransaction", + type: "info", + description: "form.status.wait", + }); + + return { success: true, isMultisig: true, safeResponse }; } else { await writeContract(config, { address: chain?.protocolContracts?.BandoRouterProxy, @@ -203,12 +238,14 @@ export const useTransactionHelpers = () => { chain: chain.chainId, account: account?.address, }); - } - updateStep({ - message: "form.status.signTransactionCompleted", - type: "completed", - }); + updateStep({ + message: "form.status.signTransactionCompleted", + type: "completed", + }); + + return { success: true, isMultisig: false }; + } }; const handleServiceRequest = useCallback( @@ -247,8 +284,9 @@ export const useTransactionHelpers = () => { return; } + let result; if (token.key === nativeToken?.native_token.symbol) { - await handleNativeTokenRequest({ + result = await handleNativeTokenRequest({ chain, account, quote, @@ -257,7 +295,7 @@ export const useTransactionHelpers = () => { formattedChain, }); } else { - await handleERC20TokenRequest({ + result = await handleERC20TokenRequest({ chain, account, quote, @@ -267,7 +305,11 @@ export const useTransactionHelpers = () => { }); } - clearStep(); + if (!result.isMultisig) { + clearStep(); + } else { + showNotification("warning", "form.status.wait"); + } } catch (error) { clearStep(); showNotification("error", "Error in handleServiceRequest"); diff --git a/src/utils/checkAllowance.ts b/src/utils/checkAllowance.ts index 6a9247d3..39d688a6 100644 --- a/src/utils/checkAllowance.ts +++ b/src/utils/checkAllowance.ts @@ -8,7 +8,8 @@ export const checkAllowance = async ( account, chain, config, - amount + amount, + skipWaitForSafe = false ) => { const maxAttempts = 10; const delay = 5000; @@ -16,6 +17,12 @@ export const checkAllowance = async ( let allowance = BigInt(0); const isMultisig = await detectMultisig(); + // For safe transactions, if skipWaitForSafe is true, we don't wait for the allowance check + if (isMultisig && skipWaitForSafe) { + console.log("Safe transaction detected, skipping allowance check wait"); + return BigInt(amount.toString()); + } + while (attempt < maxAttempts) { try { attempt++; diff --git a/src/utils/safeFunctions.ts b/src/utils/safeFunctions.ts index 62cdff80..f39399a5 100644 --- a/src/utils/safeFunctions.ts +++ b/src/utils/safeFunctions.ts @@ -22,9 +22,9 @@ export const sendViaSafe = async ({ to, abi, functionName, args }) => { args, }); - await sdk.txs.send({ + const response = await sdk.txs.send({ txs: [{ to, value: "0", data }], }); - return { success: true }; + return response; }; From 5a954adaae786b829060008c62bf1ed54a310bc8 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Mon, 12 May 2025 14:05:27 -0600 Subject: [PATCH 07/12] chore(release): 0.1.20-beta.3 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d21c867..934f54a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.20-beta.3](https://github.com/bandohq/widget/compare/v0.1.20-beta.2...v0.1.20-beta.3) (2025-05-12) + ### [0.1.20-beta.2](https://github.com/bandohq/widget/compare/v0.1.20-beta.1...v0.1.20-beta.2) (2025-05-09) ### [0.1.20-beta.1](https://github.com/bandohq/widget/compare/v0.1.20-beta.0...v0.1.20-beta.1) (2025-05-09) diff --git a/package.json b/package.json index 96d8c2c2..164bdd55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bandohq/widget", - "version": "0.1.20-beta.2", + "version": "0.1.20-beta.3", "license": "Apache-2.0", "type": "commonjs", "main": "dist/index.js", From 0c40018a814de5ba1b278a070e7ec9f045c567bc Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Tue, 13 May 2025 15:06:58 -0600 Subject: [PATCH 08/12] add batch functionality to safe functions --- src/hooks/useTransactionHelpers.ts | 132 ++++++++++++++++++++++++++++- src/utils/safeFunctions.ts | 28 +++++- 2 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/hooks/useTransactionHelpers.ts b/src/hooks/useTransactionHelpers.ts index af4ef150..ac9d7a36 100644 --- a/src/hooks/useTransactionHelpers.ts +++ b/src/hooks/useTransactionHelpers.ts @@ -2,7 +2,7 @@ import { defineChain, parseUnits } from "viem"; import { transformToChainConfig } from "../utils/TransformToChainConfig"; import BandoRouter from "@bandohq/contract-abis/abis/BandoRouterV1.json"; import nativeTokenCatalog from "../utils/nativeTokenCatalog"; -import { writeContract } from "@wagmi/core"; +import { writeContract, readContract } from "@wagmi/core"; import { ERC20ApproveABI } from "../utils/abis"; import { validateReference } from "../utils/validateReference"; import { useConfig } from "wagmi"; @@ -10,7 +10,12 @@ import { useNotificationContext } from "../providers/AlertProvider/NotificationP import { checkAllowance } from "../utils/checkAllowance"; import { useSteps } from "../providers/StepsProvider/StepsProvider"; import { useCallback } from "react"; -import { detectMultisig, sdk, sendViaSafe } from "../utils/safeFunctions"; +import { + detectMultisig, + sdk, + sendViaSafe, + sendBatchViaSafe, +} from "../utils/safeFunctions"; export const useTransactionHelpers = () => { const config = useConfig(); @@ -102,6 +107,7 @@ export const useTransactionHelpers = () => { abi: [requestServiceABI], functionName: "requestService", args: [serviceID, payload], + value: value.toString(), }); console.log("Safe transaction submitted:", safeResponse); @@ -132,6 +138,32 @@ export const useTransactionHelpers = () => { } }; + const checkCurrentAllowance = async ( + spenderAddress, + tokenAddress, + account, + chain, + config + ) => { + try { + const safeAddress = (await sdk.safe.getInfo()).safeAddress; + const ownerAddress = safeAddress || account?.address; + + const allowance = await readContract(config, { + address: tokenAddress, + abi: ERC20ApproveABI, + functionName: "allowance", + args: [ownerAddress, spenderAddress], + chainId: chain.chainId, + }); + + return allowance; + } catch (error) { + console.error("Error checking allowance:", error); + return BigInt(0); + } + }; + const handleERC20TokenRequest = async ({ chain, account, @@ -153,6 +185,18 @@ export const useTransactionHelpers = () => { variables: { amount: increaseAmount, tokenSymbol: token?.symbol }, }); + if (isMultisig) { + return await handleERC20BatchMultisig({ + chain, + account, + quote, + txId, + serviceID, + token, + amountInUnits, + }); + } + const approveResult = await approveERC20( chain?.protocolContracts?.BandoRouterProxy, amountInUnits, @@ -248,6 +292,89 @@ export const useTransactionHelpers = () => { } }; + const handleERC20BatchMultisig = async ({ + chain, + account, + quote, + txId, + serviceID, + token, + amountInUnits, + }) => { + const safeAddress = (await sdk.safe.getInfo()).safeAddress; + const requestERC20ServiceABI = BandoRouter.abi.find( + (item) => item.name === "requestERC20Service" + ); + + const payload = { + payer: safeAddress, + fiatAmount: formatFiatAmount(quote?.totalAmount), + serviceRef: txId, + token: token.address, + tokenAmount: parseUnits( + quote?.digitalAssetAmount.toString(), + token?.decimals + ), + }; + + // Verificar si ya tiene suficiente allowance + updateStep({ message: "form.status.checkingAllowance", type: "loading" }); + const requiredAmount = parseUnits( + quote?.totalAmount.toString(), + token?.decimals + ); + const currentAllowance = await checkCurrentAllowance( + chain?.protocolContracts?.BandoRouterProxy, + token.address, + account, + chain, + config + ); + + let transactions = []; + + // Solo agregar la transacción de approve si es necesario + if ( + typeof currentAllowance === "bigint" && + currentAllowance < BigInt(requiredAmount) + ) { + transactions.push({ + to: token.address, + abi: ERC20ApproveABI, + functionName: "approve", + args: [chain?.protocolContracts?.BandoRouterProxy, amountInUnits], + }); + } + + // Agregar la transacción de solicitud de servicio + transactions.push({ + to: chain?.protocolContracts?.BandoRouterProxy, + abi: [requestERC20ServiceABI], + functionName: "requestERC20Service", + args: [serviceID, payload], + }); + + updateStep({ + message: + transactions.length > 1 + ? "form.status.batchTransaction" + : "form.status.signTransaction", + type: "info", + description: "form.status.wait", + }); + + try { + const safeResponse = await sendBatchViaSafe(transactions); + console.log("Safe batch transaction submitted:", safeResponse); + + return { success: true, isMultisig: true, safeResponse }; + } catch (error) { + showNotification("error", "Error al enviar transacción batch"); + console.error("Error en batch transaction:", error); + throw error; + } + }; + const handleServiceRequest = useCallback( async ({ txId, chain, account, quote, product, token }) => { try { @@ -323,5 +450,6 @@ export const useTransactionHelpers = () => { return { approveERC20, handleServiceRequest, + handleERC20BatchMultisig, }; }; diff --git a/src/utils/safeFunctions.ts b/src/utils/safeFunctions.ts index f39399a5..d5508230 100644 --- a/src/utils/safeFunctions.ts +++ b/src/utils/safeFunctions.ts @@ -15,7 +15,13 @@ export const detectMultisig = async (): Promise => { } }; -export const sendViaSafe = async ({ to, abi, functionName, args }) => { +export const sendViaSafe = async ({ + to, + abi, + functionName, + args, + value = "0", +}) => { const data = encodeFunctionData({ abi, functionName, @@ -23,8 +29,26 @@ export const sendViaSafe = async ({ to, abi, functionName, args }) => { }); const response = await sdk.txs.send({ - txs: [{ to, value: "0", data }], + txs: [{ to, value, data }], }); return response; }; + +export const sendBatchViaSafe = async (transactions) => { + const txs = transactions.map( + ({ to, abi, functionName, args, value = "0" }) => { + const data = encodeFunctionData({ + abi, + functionName, + args, + }); + + return { to, value, data }; + } + ); + + const response = await sdk.txs.send({ txs }); + + return response; +}; From a111227511439bd4b5e9e546bf45079fd1e93f6d Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Tue, 13 May 2025 15:08:09 -0600 Subject: [PATCH 09/12] chore(release): 0.1.20-beta.4 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 934f54a5..64656cc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.20-beta.4](https://github.com/bandohq/widget/compare/v0.1.20-beta.3...v0.1.20-beta.4) (2025-05-13) + ### [0.1.20-beta.3](https://github.com/bandohq/widget/compare/v0.1.20-beta.2...v0.1.20-beta.3) (2025-05-12) ### [0.1.20-beta.2](https://github.com/bandohq/widget/compare/v0.1.20-beta.1...v0.1.20-beta.2) (2025-05-09) diff --git a/package.json b/package.json index 164bdd55..f73856dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bandohq/widget", - "version": "0.1.20-beta.3", + "version": "0.1.20-beta.4", "license": "Apache-2.0", "type": "commonjs", "main": "dist/index.js", From 9411c1d8019ae41b965fca1297b89b354b840f31 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Tue, 13 May 2025 19:30:09 -0600 Subject: [PATCH 10/12] improve comments --- src/hooks/useTransactionHelpers.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hooks/useTransactionHelpers.ts b/src/hooks/useTransactionHelpers.ts index ac9d7a36..0f935dbe 100644 --- a/src/hooks/useTransactionHelpers.ts +++ b/src/hooks/useTransactionHelpers.ts @@ -317,7 +317,7 @@ export const useTransactionHelpers = () => { ), }; - // Verificar si ya tiene suficiente allowance + // Check if the user has enough allowance updateStep({ message: "form.status.checkingAllowance", type: "loading" }); const requiredAmount = parseUnits( quote?.totalAmount.toString(), @@ -333,7 +333,7 @@ export const useTransactionHelpers = () => { let transactions = []; - // Solo agregar la transacción de approve si es necesario + // Only add the approve transaction if necessary if ( typeof currentAllowance === "bigint" && currentAllowance < BigInt(requiredAmount) @@ -346,7 +346,7 @@ export const useTransactionHelpers = () => { }); } - // Agregar la transacción de solicitud de servicio + // Add the service request transaction transactions.push({ to: chain?.protocolContracts?.BandoRouterProxy, abi: [requestERC20ServiceABI], @@ -369,8 +369,8 @@ export const useTransactionHelpers = () => { return { success: true, isMultisig: true, safeResponse }; } catch (error) { - showNotification("error", "Error al enviar transacción batch"); - console.error("Error en batch transaction:", error); + showNotification("error", "Error on transaction"); + console.error("Error on batch transaction:", error); throw error; } }; From c1168a6ff6c038190381dfb802e93c84c975c6d7 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Wed, 14 May 2025 12:19:51 -0600 Subject: [PATCH 11/12] add safe support for withdraw transaction --- .../TransactionHistory/TransactionDetail.tsx | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/src/pages/TransactionHistory/TransactionDetail.tsx b/src/pages/TransactionHistory/TransactionDetail.tsx index 64441132..64ddbf0f 100644 --- a/src/pages/TransactionHistory/TransactionDetail.tsx +++ b/src/pages/TransactionHistory/TransactionDetail.tsx @@ -4,7 +4,7 @@ import { PageContainer } from "../../components/PageContainer"; import { useHeader } from "../../hooks/useHeader"; import { useAccount } from "@lifi/wallet-management"; import { BottomSheet } from "../../components/BottomSheet/BottomSheet"; -import { defineChain } from "viem"; +import { defineChain, encodeFunctionData } from "viem"; import { Button, List, @@ -30,7 +30,7 @@ import { useEffect, useState } from "react"; import { useNotificationContext } from "../../providers/AlertProvider/NotificationProvider"; import { executeRefund } from "../../utils/refunds"; import { useTheme } from "@mui/system"; -import { s } from "vite/dist/node/types.d-aGj9QkWt"; +import { detectMultisig, sendViaSafe } from "../../utils/safeFunctions"; export const TransactionsDetailPage = () => { const { t, i18n } = useTranslation(); @@ -83,23 +83,49 @@ export const TransactionsDetailPage = () => { if (serviceId && formattedChain) { try { const isNativeToken = nativeToken.key === token.key; + const isMultisig = await detectMultisig(); - await executeRefund({ - config, - chain: formattedChain, - contractAddress: chain?.protocolContracts?.BandoRouterProxy, - abiName: isNativeToken ? "withdrawRefund" : "withdrawERC20Refund", - abi: BandoRouter.abi, - functionName: isNativeToken + if (isMultisig) { + const abiName = isNativeToken ? "withdrawRefund" - : "withdrawERC20Refund", - args: [serviceId, transactionData?.recordId], - accountAddress: account?.address, - }); + : "withdrawERC20Refund"; + const contractABI = BandoRouter.abi.find( + (item) => item.name === abiName + ); - setLoading(false); - setOpen(false); - showNotification("success", t("history.refundSuccess")); + if (!contractABI) { + throw new Error(`ABI for function ${abiName} not found`); + } + + const safeResponse = await sendViaSafe({ + to: chain?.protocolContracts?.BandoRouterProxy, + abi: [contractABI], + functionName: abiName, + args: [serviceId, transactionData?.recordId], + }); + + console.log("Safe refund transaction submitted:", safeResponse); + setLoading(false); + setOpen(false); + showNotification("success", t("history.refundSuccess")); + } else { + await executeRefund({ + config, + chain: formattedChain, + contractAddress: chain?.protocolContracts?.BandoRouterProxy, + abiName: isNativeToken ? "withdrawRefund" : "withdrawERC20Refund", + abi: BandoRouter.abi, + functionName: isNativeToken + ? "withdrawRefund" + : "withdrawERC20Refund", + args: [serviceId, transactionData?.recordId], + accountAddress: account?.address, + }); + + setLoading(false); + setOpen(false); + showNotification("success", t("history.refundSuccess")); + } } catch (error) { setLoading(false); setOpen(false); From f3e84be38c0daf77a5332ebc8a64e4aeb9b04e12 Mon Sep 17 00:00:00 2001 From: Celso Cardenas Date: Wed, 14 May 2025 12:26:21 -0600 Subject: [PATCH 12/12] chore(release): 0.1.20-beta.5 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64656cc4..cefc92d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.20-beta.5](https://github.com/bandohq/widget/compare/v0.1.20-beta.4...v0.1.20-beta.5) (2025-05-14) + ### [0.1.20-beta.4](https://github.com/bandohq/widget/compare/v0.1.20-beta.3...v0.1.20-beta.4) (2025-05-13) ### [0.1.20-beta.3](https://github.com/bandohq/widget/compare/v0.1.20-beta.2...v0.1.20-beta.3) (2025-05-12) diff --git a/package.json b/package.json index f73856dc..ecd5de82 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bandohq/widget", - "version": "0.1.20-beta.4", + "version": "0.1.20-beta.5", "license": "Apache-2.0", "type": "commonjs", "main": "dist/index.js",