diff --git a/src/methods/boost/transactions/lock/common.ts b/src/methods/boost/transactions/lock/common.ts index 12414b63..37a8984d 100644 --- a/src/methods/boost/transactions/lock/common.ts +++ b/src/methods/boost/transactions/lock/common.ts @@ -1,4 +1,4 @@ -import { ZeroAddress } from 'ethers' +import { MaxUint256, ZeroAddress } from 'ethers' import type { LockInput } from './types' import { validateArgs } from '../../../../utils' @@ -14,12 +14,19 @@ type CommonLogicInput = LockInput & { export const commonLogic = async (values: CommonLogicInput) => { const { contracts, options, provider, amount, vaultAddress, userAddress, referrerAddress = ZeroAddress, - permitParams, mockPermitSignature, + mockPermitSignature, } = values validateArgs.bigint({ amount }) validateArgs.address({ vaultAddress, userAddress, referrerAddress }) + const code = await provider.getCode(userAddress) + const isMultiSig = code !== '0x' + + let multiSigData = null + + const permitParams = isMultiSig ? null : values.permitParams + if (permitParams) { validateArgs.object({ permitParams }) @@ -66,7 +73,15 @@ export const commonLogic = async (values: CommonLogicInput) => { const isPermitRequired = allowance < amount if (isPermitRequired) { - if (mockPermitSignature) { + // It is hard to make permit action for MultiSig e.g. Safe wallet, + // so we need to use approve instead + if (isMultiSig) { + multiSigData = { + contract: contracts.tokens.mintToken, + approveArgs: [ strategyProxy, MaxUint256 ] as [ string, bigint ], + } + } + else if (mockPermitSignature) { params.push({ method: 'permit', args: [ @@ -105,9 +120,12 @@ export const commonLogic = async (values: CommonLogicInput) => { }) return { - ...multicallArgs, - request: { - params, + multiSigData, + multicallArgs: { + ...multicallArgs, + request: { + params, + }, }, } } diff --git a/src/methods/boost/transactions/lock/index.ts b/src/methods/boost/transactions/lock/index.ts index c99dac8a..7dbe3069 100644 --- a/src/methods/boost/transactions/lock/index.ts +++ b/src/methods/boost/transactions/lock/index.ts @@ -6,7 +6,18 @@ import { boostMulticall } from '../../../../contracts' const lock: Lock = async (values) => { - const multicallArgs = await commonLogic(values) + const { provider, userAddress } = values + + const { multiSigData, multicallArgs } = await commonLogic(values) + + if (multiSigData) { + const signer = await provider.getSigner(userAddress) + const signedContract = multiSigData.contract.connect(signer) + + const { hash } = await signedContract.approve(...multiSigData.approveArgs) + + await provider.waitForTransaction(hash) + } const result = await boostMulticall<{ hash: string }>(multicallArgs) diff --git a/src/methods/boost/transactions/lock/lock.md b/src/methods/boost/transactions/lock/lock.md index cd2d9e21..76c9f48c 100644 --- a/src/methods/boost/transactions/lock/lock.md +++ b/src/methods/boost/transactions/lock/lock.md @@ -9,14 +9,14 @@ Boost your osToken apy using leverage staking #### Arguments: -| Name | Type | Required | Description | -|-----------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| amount | `bigint` | **Yes** | Boost amount | -| userAddress | `string` | **Yes** | The user address | -| vaultAddress | `string` | **Yes** | The address of the vault that will mint osTokens for leverage staking | -| boostAddress | `string` | **Yes** | The address of the strategy proxy (TODO method) | -| referrerAddress | `string` | **No** | The address of the referrer | -| permitParams | `PermitParams` | **No** | The permit signature it is required only if there is not enough osToken allowance for the strategy proxy contract. It will be obtained automatically using the [utils.getPermitSignature](/utils/getpermitsignature) method | +| Name | Type | Required | Description | +|-----------------|----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| amount | `bigint` | **Yes** | Boost amount | +| userAddress | `string` | **Yes** | The user address | +| vaultAddress | `string` | **Yes** | The address of the vault that will mint osTokens for leverage staking | +| boostAddress | `string` | **Yes** | The address of the strategy proxy using the [sdk.boost.getLeverageStrategyProxy](/boost/requests/getleveragestrategyproxy) method | +| referrerAddress | `string` | **No** | The address of the referrer | +| permitParams | `PermitParams` | **No** | The permit signature is required if there isn’t enough osToken allowance for the strategy proxy contract.

**For MultiSig**
The permit signature is not necessary for Multi Sig (e.g. Safe Wallet), as it should use `sdk.contracts.mintToken.approve(boostAddress, MaxUint256)` instead of a permit call to set up osToken allowance. This will be called in the action if needed.

**For other wallets**
The permit signature is optional since it will be obtained automatically using the [utils.getPermitSignature](/utils/getpermitsignature) method. | ```ts type PermitParams = { @@ -42,7 +42,10 @@ const params = { // Send transaction const hash = await sdk.boost.lock(params) // When you sign transactions on the backend (for custodians) -const { data, to, value } = await sdk.boost.lock.encode(params) +// `lockTxData` will always be returned, while `approveTxData` will only be returned for MultiSig e.g. Safe Wallet +// if there isn’t enough osToken allowance, otherwise it will be null +const { lockTxData, approveTxData } = await sdk.boost.lock.encode(params) + // Get an approximate gas per transaction const gas = await sdk.boost.lock.estimateGas(params) ``` diff --git a/src/methods/boost/transactions/lock/lockEncode.ts b/src/methods/boost/transactions/lock/lockEncode.ts index 6e9d4a1d..32187000 100644 --- a/src/methods/boost/transactions/lock/lockEncode.ts +++ b/src/methods/boost/transactions/lock/lockEncode.ts @@ -3,16 +3,31 @@ import { commonLogic } from './common' import { boostMulticall } from '../../../../contracts' -const lockEncode = async (values: LockInput): Promise => { - const multicallArgs = await commonLogic(values) +type Output = { + lockTxData: StakeWise.TransactionData + approveTxData: StakeWise.TransactionData | null +} + +const lockEncode = async (values: LockInput): Promise => { + const { multiSigData, multicallArgs } = await commonLogic(values) + + const [ lockTxData, approveTxData ] = await Promise.all([ + boostMulticall<{ data: string, to: string }>({ + ...multicallArgs, + request: { + ...multicallArgs.request, + transactionData: true, + }, + }), + multiSigData + ? multiSigData.contract.approve.populateTransaction(...multiSigData.approveArgs) + : Promise.resolve(null), + ]) - return boostMulticall<{ data: string, to: string }>({ - ...multicallArgs, - request: { - ...multicallArgs.request, - transactionData: true, - }, - }) + return { + lockTxData, + approveTxData, + } } diff --git a/src/methods/boost/transactions/lock/lockGas.ts b/src/methods/boost/transactions/lock/lockGas.ts index 8cc877fc..ece7e129 100644 --- a/src/methods/boost/transactions/lock/lockGas.ts +++ b/src/methods/boost/transactions/lock/lockGas.ts @@ -5,17 +5,26 @@ import { boostMulticall } from '../../../../contracts' const lockGas = async (values: LockInput) => { - const { provider } = values + const { provider, userAddress } = values - const multicallArgs = await commonLogic({ ...values, mockPermitSignature: true }) + const { multiSigData, multicallArgs } = await commonLogic({ ...values, mockPermitSignature: true }) - const estimatedGas = await boostMulticall({ - ...multicallArgs, - request: { - ...multicallArgs.request, - estimateGas: true, - }, - }) + const [ estimatedGasMulticall, estimatedGasApprove ] = await Promise.all([ + boostMulticall({ + ...multicallArgs, + request: { + ...multicallArgs.request, + estimateGas: true, + }, + }), + multiSigData + ? multiSigData.contract.approve.estimateGas(...multiSigData.approveArgs, { + from: userAddress, + }) + : Promise.resolve(0n), + ]) + + const estimatedGas = estimatedGasMulticall + estimatedGasApprove return getGas({ estimatedGas, provider }) }