From 3b67ce82658d334539092ae102f97e258d3ad19c Mon Sep 17 00:00:00 2001 From: franciscotobar <100875069+franciscotobar@users.noreply.github.com> Date: Tue, 10 Sep 2024 02:03:32 -0600 Subject: [PATCH] Estimation no signature (#270) * test: integration test * chore: update relay packages * test: format and lint * refactor: rebase --------- Co-authored-by: Antonio --- test/relayclient/RelayClient.test.ts | 731 ++++++++++++- test/relayserver/RelayServer.test.ts | 1471 ++++++++++++++------------ 2 files changed, 1522 insertions(+), 680 deletions(-) diff --git a/test/relayclient/RelayClient.test.ts b/test/relayclient/RelayClient.test.ts index fdf2510a..e12787b6 100644 --- a/test/relayclient/RelayClient.test.ts +++ b/test/relayclient/RelayClient.test.ts @@ -10,6 +10,7 @@ import { SupportedSmartWallet, } from '../utils/TestUtils'; import { + MinimalBoltzSmartWalletFactory, RelayHub, TestBoltzDeployVerifierEverythingAccepted, TestRecipient, @@ -29,12 +30,18 @@ import { EnvelopingTxRequest, HttpClient, HttpWrapper, + estimateRelayMaxPossibleGasNoSignature, + estimateRelayMaxPossibleGas, + POST_RELAY_DEPLOY_GAS_COST, } from '@rsksmart/rif-relay-client'; import { constants, Wallet } from 'ethers'; import { ethers } from 'hardhat'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { loadConfiguration } from '../relayserver/ServerTestUtils'; -import { getInitiatedServer } from '../relayserver/ServerTestEnvironments'; +import { + createEnvelopingTxRequest, + getInitiatedServer, +} from '../relayserver/ServerTestEnvironments'; import { AppConfig, getServerConfig, @@ -42,16 +49,18 @@ import { RelayServer, ServerConfigParams, } from '@rsksmart/rif-relay-server'; + +import { Server } from 'http'; +import express from 'express'; +import bodyParser from 'body-parser'; +import config from 'config'; import { BoltzSmartWallet, BoltzSmartWalletFactory, + MinimalBoltzSmartWallet, SmartWallet, SmartWalletFactory, } from '@rsksmart/rif-relay-contracts'; -import { Server } from 'http'; -import express from 'express'; -import bodyParser from 'body-parser'; -import config from 'config'; const SERVER_WORK_DIR = './tmp/enveloping/test/server'; @@ -361,25 +370,8 @@ describe('RelayClient', function () { const minIndex = 0; const maxIndex = 1000000000; let nextWalletIndex: number; - let deployClient: RelayClient; let smartWalletAddress: string; - before(function () { - const { - app: { url: serverUrl }, - } = getServerConfig(); - setEnvelopingConfig({ - preferredRelays: [serverUrl], - chainId, - relayHubAddress: relayHub.address, - relayVerifierAddress: relayVerifier.address, - deployVerifierAddress: deployVerifier.address, - smartWalletFactoryAddress: smartWalletFactory.address, - logLevel: 5, - }); - deployClient = new RelayClient(); - }); - beforeEach(async function () { nextWalletIndex = Math.floor( Math.random() * (maxIndex - minIndex + 1) + minIndex @@ -390,6 +382,10 @@ describe('RelayClient', function () { tokenContract: token.address, index: nextWalletIndex, }, + relayData: { + callForwarder: smartWalletFactory.address, + callVerifier: deployVerifier.address, + }, }; smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( gaslessAccount.address, @@ -400,7 +396,7 @@ describe('RelayClient', function () { }); it('without gasLimit estimation - gasPrice - callForwarder', async function () { - const { hash, to } = await deployClient.relayTransaction( + const { hash, to } = await relayClient.relayTransaction( envelopingDeployRequest ); @@ -430,7 +426,7 @@ describe('RelayClient', function () { const initialWorkerBalance = await token.balanceOf(relayWorkerAddress); const initialSwBalance = await token.balanceOf(smartWalletAddress); - const { hash, to } = await deployClient.relayTransaction( + const { hash, to } = await relayClient.relayTransaction( updatedDeployRequest ); @@ -466,7 +462,7 @@ describe('RelayClient', function () { const initialWorkerBalance = await token.balanceOf(relayWorkerAddress); const initialSwBalance = await token.balanceOf(smartWalletAddress); - const { hash, to } = await deployClient.relayTransaction( + const { hash, to } = await relayClient.relayTransaction( updatedDeployRequest ); @@ -500,7 +496,7 @@ describe('RelayClient', function () { }, }; - const { gasPrice, to, hash } = await deployClient.relayTransaction( + const { gasPrice, to, hash } = await relayClient.relayTransaction( updatedDeployRequest ); @@ -526,7 +522,7 @@ describe('RelayClient', function () { }, }; - const { to, hash } = await deployClient.relayTransaction( + const { to, hash } = await relayClient.relayTransaction( updatedDeployRequest ); @@ -586,7 +582,7 @@ describe('RelayClient', function () { }, }; - const { hash, to } = await deployClient.relayTransaction( + const { hash, to } = await relayClient.relayTransaction( updatedDeployRequest ); @@ -828,4 +824,683 @@ describe('RelayClient', function () { await localClient.relayTransaction(envelopingRelayRequest); }); }); + + describe('estimation', function () { + const comparisonTable: Array<{ + withSignature: number; + withoutSignature: number; + difference: number; + }> = []; + + describe('with boltz smart wallet', function () { + let smartWalletFactory: BoltzSmartWalletFactory; + + before(async function () { + const smartWalletTemplate: BoltzSmartWallet = await deployContract( + 'BoltzSmartWallet' + ); + smartWalletFactory = await createSmartWalletFactory( + smartWalletTemplate, + fundedAccount, + 'Boltz' + ); + }); + + describe('during a deploy transaction', function () { + let envelopingDeployRequest: UserDefinedDeployRequest; + const minIndex = 0; + const maxIndex = 1000000000; + let nextWalletIndex: number; + let smartWalletAddress: string; + + beforeEach(async function () { + nextWalletIndex = Math.floor( + Math.random() * (maxIndex - minIndex + 1) + minIndex + ); + envelopingDeployRequest = { + request: { + from: gaslessAccount.address, + tokenContract: token.address, + index: nextWalletIndex, + }, + relayData: { + callForwarder: smartWalletFactory.address, + }, + }; + smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( + gaslessAccount.address, + constants.AddressZero, + nextWalletIndex + ); + }); + + it('with token', async function () { + // We send some native token to force the smart wallet to transfer back the balance + await fundedAccount.sendTransaction({ + to: smartWalletAddress, + value: ethers.utils.parseEther('1'), + }); + await token.mint(1000, smartWalletAddress); + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + { + ...envelopingDeployRequest, + request: { + ...envelopingDeployRequest.request, + tokenContract: token.address, + }, + }, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + }); + + it('with native token', async function () { + await fundedAccount.sendTransaction({ + to: smartWalletAddress, + value: ethers.utils.parseEther('1'), + }); + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + { + ...envelopingDeployRequest, + request: { + ...envelopingDeployRequest.request, + tokenContract: constants.AddressZero, + }, + }, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + }); + + describe('with contract execution', function () { + let data: string; + let swap: TestSwap; + + beforeEach(async function () { + swap = await deployContract('TestSwap'); + smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( + gaslessAccount.address, + constants.AddressZero, + nextWalletIndex + ); + const claimedValue = ethers.utils.parseEther('0.5'); + data = await addSwapHash({ + swap, + amount: claimedValue, + claimAddress: smartWalletAddress, + refundAddress: Wallet.createRandom().address, + }); + }); + + it('with token', async function () { + // We send some native token to force the smart wallet to transfer back the balance + await fundedAccount.sendTransaction({ + to: smartWalletAddress, + value: ethers.utils.parseEther('1'), + }); + await token.mint(1000, smartWalletAddress); + + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + { + ...envelopingDeployRequest, + request: { + ...envelopingDeployRequest.request, + to: swap.address, + data, + tokenContract: token.address, + }, + }, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + }); + + it('with native token', async function () { + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + { + ...envelopingDeployRequest, + request: { + ...envelopingDeployRequest.request, + to: swap.address, + data, + tokenContract: constants.AddressZero, + }, + }, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + }); + }); + }); + + describe('during a relay transaction', function () { + let smartWallet: BoltzSmartWallet; + let testRecipient: TestRecipient; + let envelopingRelayRequest: UserDefinedRelayRequest; + const message = 'hello world'; + + before(async function () { + smartWallet = await createSupportedSmartWallet({ + relayHub: relayWorker.address, + sender: relayWorker, + owner: gaslessAccount, + factory: smartWalletFactory, + }); + testRecipient = await deployContract('TestRecipient'); + const encodeData = testRecipient.interface.encodeFunctionData( + 'emitMessage', + [message] + ); + envelopingRelayRequest = { + request: { + to: testRecipient.address, + data: encodeData, + from: gaslessAccount.address, + tokenContract: token.address, + }, + relayData: { + callForwarder: smartWallet.address, + }, + }; + }); + + it('with token', async function () { + // We send some native token to force the smart wallet to transfer back the balance + await fundedAccount.sendTransaction({ + to: smartWallet.address, + value: ethers.utils.parseEther('1'), + }); + await token.mint(1000, smartWallet.address); + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + { + ...envelopingRelayRequest, + request: { + ...envelopingRelayRequest.request, + tokenContract: token.address, + }, + }, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + }); + + it('with native token', async function () { + // We send some native token to force the smart wallet to transfer back the balance + await fundedAccount.sendTransaction({ + to: smartWallet.address, + value: ethers.utils.parseEther('1'), + }); + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + { + ...envelopingRelayRequest, + request: { + ...envelopingRelayRequest.request, + tokenContract: constants.AddressZero, + }, + }, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + }); + }); + }); + + describe('with minimal boltz smart wallet', function () { + let smartWalletFactory: MinimalBoltzSmartWalletFactory; + + before(async function () { + const smartWalletTemplate: MinimalBoltzSmartWallet = + await deployContract('MinimalBoltzSmartWallet'); + smartWalletFactory = await createSmartWalletFactory( + smartWalletTemplate, + fundedAccount, + 'MinimalBoltz' + ); + }); + + describe('during a deploy transaction', function () { + let envelopingDeployRequest: UserDefinedDeployRequest; + const minIndex = 0; + const maxIndex = 1000000000; + let nextWalletIndex: number; + let smartWalletAddress: string; + + beforeEach(async function () { + nextWalletIndex = Math.floor( + Math.random() * (maxIndex - minIndex + 1) + minIndex + ); + envelopingDeployRequest = { + request: { + from: gaslessAccount.address, + tokenContract: token.address, + index: nextWalletIndex, + }, + relayData: { + callForwarder: smartWalletFactory.address, + }, + }; + smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( + gaslessAccount.address, + constants.AddressZero, + nextWalletIndex + ); + }); + + describe('with contract execution', function () { + let data: string; + let swap: TestSwap; + + beforeEach(async function () { + swap = await deployContract('TestSwap'); + const claimedValue = ethers.utils.parseEther('0.5'); + data = await addSwapHash({ + swap, + amount: claimedValue, + claimAddress: smartWalletAddress, + refundAddress: Wallet.createRandom().address, + }); + }); + + it('with native token', async function () { + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + { + ...envelopingDeployRequest, + request: { + ...envelopingDeployRequest.request, + to: swap.address, + data, + tokenContract: constants.AddressZero, + }, + }, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + }); + }); + }); + }); + + describe('with smart wallet', function () { + let smartWalletFactory: SmartWalletFactory; + + before(async function () { + const smartWalletTemplate: SmartWallet = await deployContract( + 'SmartWallet' + ); + smartWalletFactory = await createSmartWalletFactory( + smartWalletTemplate, + fundedAccount + ); + }); + + describe('during a deploy transaction', function () { + let envelopingDeployRequest: UserDefinedDeployRequest; + const minIndex = 0; + const maxIndex = 1000000000; + let nextWalletIndex: number; + let smartWalletAddress: string; + + beforeEach(async function () { + nextWalletIndex = Math.floor( + Math.random() * (maxIndex - minIndex + 1) + minIndex + ); + envelopingDeployRequest = { + request: { + from: gaslessAccount.address, + tokenContract: token.address, + index: nextWalletIndex, + }, + relayData: { + callForwarder: smartWalletFactory.address, + }, + }; + smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( + gaslessAccount.address, + constants.AddressZero, + nextWalletIndex + ); + }); + + it('with token', async function () { + await token.mint(1000, smartWalletAddress); + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + envelopingDeployRequest, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature + .sub(POST_RELAY_DEPLOY_GAS_COST + 3500) + .toNumber(), + difference: estimationNoSignature + .sub(POST_RELAY_DEPLOY_GAS_COST + 3500) + .sub(estimationWithSignature) + .toNumber(), + }); + + console.log( + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature + .sub(POST_RELAY_DEPLOY_GAS_COST + 3500) + .toString()}` + ); + + // SmartWallet cannot transfer back native token during initialization + // POST_RELAY_DEPLOY_GAS_COST subtracted to simulate the scenario + // POST_DEPLOY_NO_EXECUTION subtracted to simulate the scenario + expect( + estimationWithSignature.lte( + estimationNoSignature.sub(POST_RELAY_DEPLOY_GAS_COST + 3500) + ), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature + .sub(POST_RELAY_DEPLOY_GAS_COST + 3500) + .toString()}` + ).to.be.true; + }); + }); + + describe('during a relay transaction', function () { + let smartWallet: SmartWallet; + let testRecipient: TestRecipient; + let envelopingRelayRequest: UserDefinedRelayRequest; + const message = 'hello world'; + + before(async function () { + smartWallet = await createSupportedSmartWallet({ + relayHub: relayWorker.address, + sender: relayWorker, + owner: gaslessAccount, + factory: smartWalletFactory, + }); + testRecipient = await deployContract('TestRecipient'); + const encodeData = testRecipient.interface.encodeFunctionData( + 'emitMessage', + [message] + ); + envelopingRelayRequest = { + request: { + to: testRecipient.address, + data: encodeData, + from: gaslessAccount.address, + tokenContract: token.address, + }, + relayData: { + callForwarder: smartWallet.address, + }, + }; + }); + + it('with token', async function () { + // We send some native token to force the smart wallet to transfer back the balance + await fundedAccount.sendTransaction({ + to: smartWallet.address, + value: ethers.utils.parseEther('1'), + }); + await token.mint(1000, smartWallet.address); + const hubInfo = relayServer.getChainInfo(); + const envelopingTxRequest = await createEnvelopingTxRequest( + envelopingRelayRequest, + relayClient, + hubInfo + ); + + const estimationWithSignature = await estimateRelayMaxPossibleGas( + envelopingTxRequest, + hubInfo.relayWorkerAddress + ); + const relayWorkerWallet = + relayServer.transactionManager.workersKeyManager.getWallet( + hubInfo.relayWorkerAddress + ); + + const estimationNoSignature = + await estimateRelayMaxPossibleGasNoSignature( + envelopingTxRequest.relayRequest, + relayWorkerWallet + ); + + comparisonTable.push({ + withSignature: estimationWithSignature.toNumber(), + withoutSignature: estimationNoSignature.toNumber(), + difference: estimationNoSignature + .sub(estimationWithSignature) + .toNumber(), + }); + + expect( + estimationWithSignature.lte(estimationNoSignature), + `${estimationWithSignature.toString()} should less than or equal ${estimationNoSignature.toString()}` + ).to.be.true; + + console.table(comparisonTable); + }); + }); + }); + }); }); diff --git a/test/relayserver/RelayServer.test.ts b/test/relayserver/RelayServer.test.ts index 39c0b088..05cfd0e4 100644 --- a/test/relayserver/RelayServer.test.ts +++ b/test/relayserver/RelayServer.test.ts @@ -93,6 +93,21 @@ const basicAppConfig: Partial = { const provider = ethers.provider; +const TYPE_OF_ESTIMATIONS = [ + { + description: 'with signature', + options: { + serverSignature: true, + }, + }, + { + description: 'without signature', + options: { + serverSignature: false, + }, + }, +]; + describe('RelayServer', function () { type RelayServerExposed = { _alerted: boolean; @@ -942,11 +957,798 @@ describe('RelayServer', function () { relayServer = await getInitiatedServer({ relayOwner }); hubInfo = relayServer.getChainInfo(); AccountManager.getInstance().addAccount(owner); + const { chainId } = provider.network; + const { + app: { url: serverUrl }, + } = getServerConfig(); + setEnvelopingConfig({ + preferredRelays: [serverUrl], + chainId, + relayHubAddress: relayHub.address, + deployVerifierAddress: deployVerifier.address, + relayVerifierAddress: relayVerifier.address, + logLevel: 1, + }); + relayClient = new RelayClient(); }); + for (const { description, options } of TYPE_OF_ESTIMATIONS) { + describe(description, function () { + describe('with boltz smart wallet', function () { + let smartWalletFactory: BoltzSmartWalletFactory; + let recipient: TestRecipient; + let encodedData: string; + + beforeEach(async function () { + const [, fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress, + SignerWithAddress + ]; + const smartWalletTemplate: BoltzSmartWallet = await deployContract( + 'BoltzSmartWallet' + ); + smartWalletFactory = await createSmartWalletFactory( + smartWalletTemplate, + fundedAccount, + 'Boltz' + ); + recipient = await deployContract('TestRecipient'); + encodedData = recipient.interface.encodeFunctionData( + 'emitMessage', + ['hello'] + ); + }); + + describe('relay transaction', function () { + let smartWallet: BoltzSmartWallet; + + beforeEach(async function () { + const [worker] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + smartWallet = await createSupportedSmartWallet({ + relayHub: worker.address, + sender: worker, + owner, + factory: smartWalletFactory, + type: 'Boltz', + }); + }); + + it('should estimate paying with native', async function () { + const [fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + + await fundedAccount.sendTransaction({ + to: smartWallet.address, + value: utils.parseEther('10'), + }); + + const userDefinedRelayRequest = createRelayUserDefinedRequest( + { + from: owner.address, + to: recipient.address, + data: encodedData, + tokenContract: constants.AddressZero, + }, + { + callForwarder: smartWallet.address, + callVerifier: relayVerifier.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await provider.getBalance( + feesReceiverAddress + ); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await provider.getBalance( + feesReceiverAddress + ); + + expect(balanceAfter).to.be.equal( + balanceBefore.add(estimation.requiredTokenAmount) + ); + }); + + it('should estimate paying with erc20 token', async function () { + const [fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + + await fundedAccount.sendTransaction({ + to: smartWallet.address, + value: utils.parseEther('10'), + }); + + const token = await prepareToken('TestToken'); + + await mintTokens( + token, + 'TestToken', + utils.parseEther('10'), + smartWallet.address + ); + + const userDefinedRelayRequest = createRelayUserDefinedRequest( + { + from: owner.address, + to: recipient.address, + data: encodedData, + tokenContract: token.address, + }, + { + callForwarder: smartWallet.address, + callVerifier: relayVerifier.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await token.balanceOf(smartWallet.address); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await token.balanceOf(smartWallet.address); + + expect(balanceAfter).to.be.equal( + balanceBefore.sub(estimation.requiredTokenAmount) + ); + }); + }); + + describe('deploy transaction', function () { + const minIndex = 1; + const maxIndex = 1000000000; + let nextWalletIndex: number; + let smartWalletAddress: string; + + beforeEach(async function () { + nextWalletIndex = Math.floor( + Math.random() * (maxIndex - minIndex + 1) + minIndex + ); + smartWalletAddress = + await smartWalletFactory.getSmartWalletAddress( + owner.address, + constants.AddressZero, + nextWalletIndex + ); + }); + + it('should estimate paying with native', async function () { + const [fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + + await fundedAccount.sendTransaction({ + to: smartWalletAddress, + value: utils.parseEther('10'), + }); + + const userDefinedRelayRequest = createDeployUserDefinedRequest( + { + from: owner.address, + tokenContract: constants.AddressZero, + index: nextWalletIndex, + }, + { + callForwarder: smartWalletFactory.address, + callVerifier: boltzDeployVerifier.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await provider.getBalance( + feesReceiverAddress + ); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await provider.getBalance( + feesReceiverAddress + ); + + expect(balanceAfter).to.be.equal( + balanceBefore.add(estimation.requiredTokenAmount) + ); + }); + + it('should estimate paying with erc20 token', async function () { + const [fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + + await fundedAccount.sendTransaction({ + to: smartWalletAddress, + value: utils.parseEther('10'), + }); + const token = await prepareToken('TestToken'); + + await mintTokens( + token, + 'TestToken', + utils.parseEther('100'), + smartWalletAddress + ); + + const userDefinedRelayRequest = createDeployUserDefinedRequest( + { + from: owner.address, + tokenContract: token.address, + index: nextWalletIndex, + }, + { + callForwarder: smartWalletFactory.address, + callVerifier: boltzDeployVerifier.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await token.balanceOf(smartWalletAddress); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await token.balanceOf(smartWalletAddress); + + expect(balanceAfter).to.be.equal( + balanceBefore.sub(estimation.requiredTokenAmount) + ); + }); + + describe('with contract execution', function () { + it('should estimate paying with native', async function () { + const [fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + + await fundedAccount.sendTransaction({ + to: smartWalletAddress, + value: utils.parseEther('10'), + }); + + const userDefinedRelayRequest = createDeployUserDefinedRequest( + { + from: owner.address, + to: recipient.address, + data: encodedData, + tokenContract: constants.AddressZero, + index: nextWalletIndex, + }, + { + callForwarder: smartWalletFactory.address, + callVerifier: boltzDeployVerifier.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await provider.getBalance( + feesReceiverAddress + ); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await provider.getBalance( + feesReceiverAddress + ); + + expect(balanceAfter).to.be.equal( + balanceBefore.add(estimation.requiredTokenAmount) + ); + }); + + it('should estimate paying with erc20 token', async function () { + const token = await prepareToken('TestToken'); + + await mintTokens( + token, + 'TestToken', + utils.parseEther('100'), + smartWalletAddress + ); + + const userDefinedRelayRequest = createDeployUserDefinedRequest( + { + from: owner.address, + to: recipient.address, + data: encodedData, + tokenContract: token.address, + index: nextWalletIndex, + }, + { + callForwarder: smartWalletFactory.address, + callVerifier: boltzDeployVerifier.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await token.balanceOf(smartWalletAddress); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await token.balanceOf(smartWalletAddress); + + expect(balanceAfter).to.be.equal( + balanceBefore.sub(estimation.requiredTokenAmount) + ); + }); + }); + }); + }); + + describe('with minimal boltz smart wallet', function () { + let smartWalletFactory: MinimalBoltzSmartWalletFactory; + + beforeEach(async function () { + const [, fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress, + SignerWithAddress + ]; + const smartWalletTemplate: MinimalBoltzSmartWallet = + await deployContract('MinimalBoltzSmartWallet'); + smartWalletFactory = await createSmartWalletFactory( + smartWalletTemplate, + fundedAccount, + 'MinimalBoltz' + ); + }); + + describe('deploy transaction', function () { + const minIndex = 1; + const maxIndex = 1000000000; + let nextWalletIndex: number; + let smartWalletAddress: string; + let recipient: TestSwap; + let encodedData: string; + + beforeEach(async function () { + nextWalletIndex = Math.floor( + Math.random() * (maxIndex - minIndex + 1) + minIndex + ); + smartWalletAddress = + await smartWalletFactory.getSmartWalletAddress( + owner.address, + constants.AddressZero, + nextWalletIndex + ); + recipient = await deployContract('TestSwap'); + encodedData = await addSwapHash({ + swap: recipient, + amount: ethers.utils.parseEther('10'), + claimAddress: smartWalletAddress, + refundAddress: smartWalletAddress, + }); + }); + + it('should estimate paying with native', async function () { + const userDefinedRelayRequest = createDeployUserDefinedRequest( + { + from: owner.address, + to: recipient.address, + data: encodedData, + tokenContract: constants.AddressZero, + index: nextWalletIndex, + }, + { + callForwarder: smartWalletFactory.address, + callVerifier: boltzDeployVerifier.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await provider.getBalance( + feesReceiverAddress + ); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await provider.getBalance( + feesReceiverAddress + ); + + expect(balanceAfter).to.be.equal( + balanceBefore.add(estimation.requiredTokenAmount) + ); + }); + }); + }); + + describe('with smart wallet', function () { + let smartWalletFactory: SmartWalletFactory; + let recipient: TestRecipient; + let encodedData: string; + + beforeEach(async function () { + const [, fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress, + SignerWithAddress + ]; + const smartWalletTemplate: SmartWallet = await deployContract( + 'SmartWallet' + ); + smartWalletFactory = await createSmartWalletFactory( + smartWalletTemplate, + fundedAccount, + 'Default' + ); + recipient = await deployContract('TestRecipient'); + encodedData = recipient.interface.encodeFunctionData( + 'emitMessage', + ['hello'] + ); + }); + + describe('relay transaction', function () { + let smartWallet: SmartWallet; + + beforeEach(async function () { + const [worker] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + smartWallet = await createSupportedSmartWallet({ + relayHub: worker.address, + sender: worker, + owner, + factory: smartWalletFactory, + type: 'Default', + }); + }); + + it('should estimate paying with erc20 token', async function () { + const [fundedAccount] = (await ethers.getSigners()) as [ + SignerWithAddress + ]; + + await fundedAccount.sendTransaction({ + to: smartWallet.address, + value: utils.parseEther('10'), + }); + + const token = await prepareToken('TestToken'); + + await mintTokens( + token, + 'TestToken', + utils.parseEther('10'), + smartWallet.address + ); + + const userDefinedRelayRequest = createRelayUserDefinedRequest( + { + from: owner.address, + to: recipient.address, + data: encodedData, + tokenContract: token.address, + }, + { + callForwarder: smartWallet.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await token.balanceOf(smartWallet.address); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await token.balanceOf(smartWallet.address); + + expect(balanceAfter).to.be.equal( + balanceBefore.sub(estimation.requiredTokenAmount) + ); + }); + }); + + describe('deploy transaction', function () { + const minIndex = 1; + const maxIndex = 1000000000; + let nextWalletIndex: number; + let smartWalletAddress: string; + + beforeEach(async function () { + nextWalletIndex = Math.floor( + Math.random() * (maxIndex - minIndex + 1) + minIndex + ); + smartWalletAddress = + await smartWalletFactory.getSmartWalletAddress( + owner.address, + constants.AddressZero, + nextWalletIndex + ); + }); + + it('should estimate paying with erc20 token', async function () { + const token = await prepareToken('TestToken'); + + await mintTokens( + token, + 'TestToken', + utils.parseEther('100'), + smartWalletAddress + ); + + const userDefinedRelayRequest = createDeployUserDefinedRequest( + { + from: owner.address, + tokenContract: token.address, + index: nextWalletIndex, + }, + { + callForwarder: smartWalletFactory.address, + } + ); + + const httpEnvelopingTxRequest = + await createAndStringifyEnvelopingTxRequest( + userDefinedRelayRequest, + relayClient, + hubInfo, + options + ); + + const estimation = await relayServer.estimateMaxPossibleGas( + httpEnvelopingTxRequest + ); + + const httpEnvelopingTxRequestWithEstimation = + await createAndStringifyEnvelopingTxRequest( + { + ...userDefinedRelayRequest, + request: { + ...userDefinedRelayRequest.request, + tokenAmount: estimation.requiredTokenAmount, + }, + }, + relayClient, + hubInfo + ); + + const balanceBefore = await token.balanceOf(smartWalletAddress); + + await expect( + relayServer.createRelayTransaction( + httpEnvelopingTxRequestWithEstimation + ) + ).to.be.fulfilled; + + const balanceAfter = await token.balanceOf(smartWalletAddress); + + expect(balanceAfter).to.be.equal( + balanceBefore.sub(estimation.requiredTokenAmount) + ); + }); + }); + }); + }); + } - describe('with boltz smart wallet', function () { - let smartWalletFactory: BoltzSmartWalletFactory; + describe('with custom smart wallet', function () { + let smartWalletFactory: CustomSmartWalletFactory; let recipient: TestRecipient; + let customLogic: SuccessCustomLogic; let encodedData: string; beforeEach(async function () { @@ -954,36 +1756,23 @@ describe('RelayServer', function () { SignerWithAddress, SignerWithAddress ]; - const smartWalletTemplate: BoltzSmartWallet = await deployContract( - 'BoltzSmartWallet' + const smartWalletTemplate: CustomSmartWallet = await deployContract( + 'CustomSmartWallet' ); smartWalletFactory = await createSmartWalletFactory( smartWalletTemplate, fundedAccount, - 'Boltz' + 'Custom' ); - const { chainId } = provider.network; - const { - app: { url: serverUrl }, - } = getServerConfig(); - setEnvelopingConfig({ - preferredRelays: [serverUrl], - chainId, - relayHubAddress: relayHub.address, - deployVerifierAddress: boltzDeployVerifier.address, - relayVerifierAddress: relayVerifier.address, - smartWalletFactoryAddress: smartWalletFactory.address, - logLevel: 1, - }); - relayClient = new RelayClient(); recipient = await deployContract('TestRecipient'); + customLogic = await deployContract('SuccessCustomLogic'); encodedData = recipient.interface.encodeFunctionData('emitMessage', [ 'hello', ]); }); describe('relay transaction', function () { - let smartWallet: BoltzSmartWallet; + let smartWallet: CustomSmartWallet; beforeEach(async function () { const [worker] = (await ethers.getSigners()) as [SignerWithAddress]; @@ -992,69 +1781,9 @@ describe('RelayServer', function () { sender: worker, owner, factory: smartWalletFactory, - type: 'Boltz', - }); - }); - - it('should estimate paying with native', async function () { - const [fundedAccount] = (await ethers.getSigners()) as [ - SignerWithAddress - ]; - - await fundedAccount.sendTransaction({ - to: smartWallet.address, - value: utils.parseEther('10'), + logicAddr: customLogic.address, + type: 'Custom', }); - - const userDefinedRelayRequest = createRelayUserDefinedRequest( - { - from: owner.address, - to: recipient.address, - data: encodedData, - tokenContract: constants.AddressZero, - }, - { - callForwarder: smartWallet.address, - } - ); - - const httpEnvelopingTxRequest = - await createAndStringifyEnvelopingTxRequest( - userDefinedRelayRequest, - relayClient, - hubInfo - ); - - const estimation = await relayServer.estimateMaxPossibleGas( - httpEnvelopingTxRequest - ); - - const httpEnvelopingTxRequestWithEstimation = - await createAndStringifyEnvelopingTxRequest( - { - ...userDefinedRelayRequest, - request: { - ...userDefinedRelayRequest.request, - tokenAmount: estimation.requiredTokenAmount, - }, - }, - relayClient, - hubInfo - ); - - const balanceBefore = await provider.getBalance(feesReceiverAddress); - - await expect( - relayServer.createRelayTransaction( - httpEnvelopingTxRequestWithEstimation - ) - ).to.be.fulfilled; - - const balanceAfter = await provider.getBalance(feesReceiverAddress); - - expect(balanceAfter).to.be.equal( - balanceBefore.add(estimation.requiredTokenAmount) - ); }); it('should estimate paying with erc20 token', async function () { @@ -1083,7 +1812,8 @@ describe('RelayServer', function () { await createAndStringifyEnvelopingTxRequest( userDefinedRelayRequest, relayClient, - hubInfo + hubInfo, + { isCustom: true } ); const estimation = await relayServer.estimateMaxPossibleGas( @@ -1132,72 +1862,12 @@ describe('RelayServer', function () { smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( owner.address, constants.AddressZero, + customLogic.address, + utils.keccak256('0x00'), nextWalletIndex ); }); - it('should estimate paying with native', async function () { - const [fundedAccount] = (await ethers.getSigners()) as [ - SignerWithAddress - ]; - - await fundedAccount.sendTransaction({ - to: smartWalletAddress, - value: utils.parseEther('10'), - }); - - const userDefinedRelayRequest = createDeployUserDefinedRequest( - { - from: owner.address, - to: recipient.address, - data: encodedData, - tokenContract: constants.AddressZero, - index: nextWalletIndex, - }, - { - callForwarder: smartWalletFactory.address, - } - ); - - const httpEnvelopingTxRequest = - await createAndStringifyEnvelopingTxRequest( - userDefinedRelayRequest, - relayClient, - hubInfo - ); - - const estimation = await relayServer.estimateMaxPossibleGas( - httpEnvelopingTxRequest - ); - - const httpEnvelopingTxRequestWithEstimation = - await createAndStringifyEnvelopingTxRequest( - { - ...userDefinedRelayRequest, - request: { - ...userDefinedRelayRequest.request, - tokenAmount: estimation.requiredTokenAmount, - }, - }, - relayClient, - hubInfo - ); - - const balanceBefore = await provider.getBalance(feesReceiverAddress); - - await expect( - relayServer.createRelayTransaction( - httpEnvelopingTxRequestWithEstimation - ) - ).to.be.fulfilled; - - const balanceAfter = await provider.getBalance(feesReceiverAddress); - - expect(balanceAfter).to.be.equal( - balanceBefore.add(estimation.requiredTokenAmount) - ); - }); - it('should estimate paying with erc20 token', async function () { const token = await prepareToken('TestToken'); @@ -1211,10 +1881,9 @@ describe('RelayServer', function () { const userDefinedRelayRequest = createDeployUserDefinedRequest( { from: owner.address, - to: recipient.address, - data: encodedData, tokenContract: token.address, index: nextWalletIndex, + to: customLogic.address, }, { callForwarder: smartWalletFactory.address, @@ -1225,512 +1894,10 @@ describe('RelayServer', function () { await createAndStringifyEnvelopingTxRequest( userDefinedRelayRequest, relayClient, - hubInfo - ); - - const estimation = await relayServer.estimateMaxPossibleGas( - httpEnvelopingTxRequest - ); - - const httpEnvelopingTxRequestWithEstimation = - await createAndStringifyEnvelopingTxRequest( + hubInfo, { - ...userDefinedRelayRequest, - request: { - ...userDefinedRelayRequest.request, - tokenAmount: estimation.requiredTokenAmount, - }, - }, - relayClient, - hubInfo - ); - - const balanceBefore = await token.balanceOf(smartWalletAddress); - - await expect( - relayServer.createRelayTransaction( - httpEnvelopingTxRequestWithEstimation - ) - ).to.be.fulfilled; - - const balanceAfter = await token.balanceOf(smartWalletAddress); - - expect(balanceAfter).to.be.equal( - balanceBefore.sub(estimation.requiredTokenAmount) - ); - }); - }); - }); - - describe('with minimal boltz smart wallet', function () { - let smartWalletFactory: MinimalBoltzSmartWalletFactory; - - beforeEach(async function () { - const [, fundedAccount] = (await ethers.getSigners()) as [ - SignerWithAddress, - SignerWithAddress - ]; - const smartWalletTemplate: MinimalBoltzSmartWallet = - await deployContract('MinimalBoltzSmartWallet'); - smartWalletFactory = await createSmartWalletFactory( - smartWalletTemplate, - fundedAccount, - 'MinimalBoltz' - ); - const { chainId } = provider.network; - const { - app: { url: serverUrl }, - } = getServerConfig(); - setEnvelopingConfig({ - preferredRelays: [serverUrl], - chainId, - relayHubAddress: relayHub.address, - deployVerifierAddress: boltzDeployVerifier.address, - relayVerifierAddress: relayVerifier.address, - smartWalletFactoryAddress: smartWalletFactory.address, - logLevel: 1, - }); - relayClient = new RelayClient(); - }); - - describe('deploy transaction', function () { - const minIndex = 1; - const maxIndex = 1000000000; - let nextWalletIndex: number; - let smartWalletAddress: string; - let recipient: TestSwap; - let encodedData: string; - - beforeEach(async function () { - nextWalletIndex = Math.floor( - Math.random() * (maxIndex - minIndex + 1) + minIndex - ); - smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( - owner.address, - constants.AddressZero, - nextWalletIndex - ); - recipient = await deployContract('TestSwap'); - encodedData = await addSwapHash({ - swap: recipient, - amount: ethers.utils.parseEther('10'), - claimAddress: smartWalletAddress, - refundAddress: smartWalletAddress, - }); - }); - - it('should estimate paying with native', async function () { - const userDefinedRelayRequest = createDeployUserDefinedRequest( - { - from: owner.address, - to: recipient.address, - data: encodedData, - tokenContract: constants.AddressZero, - index: nextWalletIndex, - }, - { - callForwarder: smartWalletFactory.address, - } - ); - - const httpEnvelopingTxRequest = - await createAndStringifyEnvelopingTxRequest( - userDefinedRelayRequest, - relayClient, - hubInfo - ); - - const estimation = await relayServer.estimateMaxPossibleGas( - httpEnvelopingTxRequest - ); - - const httpEnvelopingTxRequestWithEstimation = - await createAndStringifyEnvelopingTxRequest( - { - ...userDefinedRelayRequest, - request: { - ...userDefinedRelayRequest.request, - tokenAmount: estimation.requiredTokenAmount, - }, - }, - relayClient, - hubInfo - ); - - const balanceBefore = await provider.getBalance(feesReceiverAddress); - - await expect( - relayServer.createRelayTransaction( - httpEnvelopingTxRequestWithEstimation - ) - ).to.be.fulfilled; - - const balanceAfter = await provider.getBalance(feesReceiverAddress); - - expect(balanceAfter).to.be.equal( - balanceBefore.add(estimation.requiredTokenAmount) - ); - }); - }); - }); - - describe('with smart wallet', function () { - let smartWalletFactory: SmartWalletFactory; - let recipient: TestRecipient; - let encodedData: string; - - beforeEach(async function () { - const [, fundedAccount] = (await ethers.getSigners()) as [ - SignerWithAddress, - SignerWithAddress - ]; - const smartWalletTemplate: SmartWallet = await deployContract( - 'SmartWallet' - ); - smartWalletFactory = await createSmartWalletFactory( - smartWalletTemplate, - fundedAccount, - 'Default' - ); - const { chainId } = provider.network; - const { - app: { url: serverUrl }, - } = getServerConfig(); - setEnvelopingConfig({ - preferredRelays: [serverUrl], - chainId, - relayHubAddress: relayHub.address, - deployVerifierAddress: deployVerifier.address, - relayVerifierAddress: relayVerifier.address, - smartWalletFactoryAddress: smartWalletFactory.address, - logLevel: 1, - }); - relayClient = new RelayClient(); - recipient = await deployContract('TestRecipient'); - encodedData = recipient.interface.encodeFunctionData('emitMessage', [ - 'hello', - ]); - }); - - describe('relay transaction', function () { - let smartWallet: SmartWallet; - - beforeEach(async function () { - const [worker] = (await ethers.getSigners()) as [SignerWithAddress]; - smartWallet = await createSupportedSmartWallet({ - relayHub: worker.address, - sender: worker, - owner, - factory: smartWalletFactory, - type: 'Default', - }); - }); - - it('should estimate paying with erc20 token', async function () { - const token = await prepareToken('TestToken'); - - await mintTokens( - token, - 'TestToken', - utils.parseEther('10'), - smartWallet.address - ); - - const userDefinedRelayRequest = createRelayUserDefinedRequest( - { - from: owner.address, - to: recipient.address, - data: encodedData, - tokenContract: token.address, - }, - { - callForwarder: smartWallet.address, - } - ); - - const httpEnvelopingTxRequest = - await createAndStringifyEnvelopingTxRequest( - userDefinedRelayRequest, - relayClient, - hubInfo - ); - - const estimation = await relayServer.estimateMaxPossibleGas( - httpEnvelopingTxRequest - ); - - const httpEnvelopingTxRequestWithEstimation = - await createAndStringifyEnvelopingTxRequest( - { - ...userDefinedRelayRequest, - request: { - ...userDefinedRelayRequest.request, - tokenAmount: estimation.requiredTokenAmount, - }, - }, - relayClient, - hubInfo - ); - - const balanceBefore = await token.balanceOf(smartWallet.address); - - await expect( - relayServer.createRelayTransaction( - httpEnvelopingTxRequestWithEstimation - ) - ).to.be.fulfilled; - - const balanceAfter = await token.balanceOf(smartWallet.address); - - expect(balanceAfter).to.be.equal( - balanceBefore.sub(estimation.requiredTokenAmount) - ); - }); - }); - - describe('deploy transaction', function () { - const minIndex = 1; - const maxIndex = 1000000000; - let nextWalletIndex: number; - let smartWalletAddress: string; - - beforeEach(async function () { - nextWalletIndex = Math.floor( - Math.random() * (maxIndex - minIndex + 1) + minIndex - ); - smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( - owner.address, - constants.AddressZero, - nextWalletIndex - ); - }); - - it('should estimate paying with erc20 token', async function () { - const token = await prepareToken('TestToken'); - - await mintTokens( - token, - 'TestToken', - utils.parseEther('100'), - smartWalletAddress - ); - - const userDefinedRelayRequest = createDeployUserDefinedRequest( - { - from: owner.address, - tokenContract: token.address, - index: nextWalletIndex, - }, - { - callForwarder: smartWalletFactory.address, - } - ); - - const httpEnvelopingTxRequest = - await createAndStringifyEnvelopingTxRequest( - userDefinedRelayRequest, - relayClient, - hubInfo - ); - - const estimation = await relayServer.estimateMaxPossibleGas( - httpEnvelopingTxRequest - ); - - const httpEnvelopingTxRequestWithEstimation = - await createAndStringifyEnvelopingTxRequest( - { - ...userDefinedRelayRequest, - request: { - ...userDefinedRelayRequest.request, - tokenAmount: estimation.requiredTokenAmount, - }, - }, - relayClient, - hubInfo - ); - - const balanceBefore = await token.balanceOf(smartWalletAddress); - - await expect( - relayServer.createRelayTransaction( - httpEnvelopingTxRequestWithEstimation - ) - ).to.be.fulfilled; - - const balanceAfter = await token.balanceOf(smartWalletAddress); - - expect(balanceAfter).to.be.equal( - balanceBefore.sub(estimation.requiredTokenAmount) - ); - }); - }); - }); - - describe('with custom smart wallet', function () { - let smartWalletFactory: CustomSmartWalletFactory; - let recipient: TestRecipient; - let customLogic: SuccessCustomLogic; - let encodedData: string; - - beforeEach(async function () { - const [, fundedAccount] = (await ethers.getSigners()) as [ - SignerWithAddress, - SignerWithAddress - ]; - const smartWalletTemplate: CustomSmartWallet = await deployContract( - 'CustomSmartWallet' - ); - smartWalletFactory = await createSmartWalletFactory( - smartWalletTemplate, - fundedAccount, - 'Custom' - ); - const { chainId } = provider.network; - const { - app: { url: serverUrl }, - } = getServerConfig(); - setEnvelopingConfig({ - preferredRelays: [serverUrl], - chainId, - relayHubAddress: relayHub.address, - deployVerifierAddress: deployVerifier.address, - relayVerifierAddress: relayVerifier.address, - smartWalletFactoryAddress: smartWalletFactory.address, - logLevel: 1, - }); - relayClient = new RelayClient(); - recipient = await deployContract('TestRecipient'); - customLogic = await deployContract('SuccessCustomLogic'); - encodedData = recipient.interface.encodeFunctionData('emitMessage', [ - 'hello', - ]); - }); - - describe('relay transaction', function () { - let smartWallet: CustomSmartWallet; - - beforeEach(async function () { - const [worker] = (await ethers.getSigners()) as [SignerWithAddress]; - smartWallet = await createSupportedSmartWallet({ - relayHub: worker.address, - sender: worker, - owner, - factory: smartWalletFactory, - logicAddr: customLogic.address, - type: 'Custom', - }); - }); - - it('should estimate paying with erc20 token', async function () { - const token = await prepareToken('TestToken'); - - await mintTokens( - token, - 'TestToken', - utils.parseEther('10'), - smartWallet.address - ); - - const userDefinedRelayRequest = createRelayUserDefinedRequest( - { - from: owner.address, - to: recipient.address, - data: encodedData, - tokenContract: token.address, - }, - { - callForwarder: smartWallet.address, - } - ); - - const httpEnvelopingTxRequest = - await createAndStringifyEnvelopingTxRequest( - userDefinedRelayRequest, - relayClient, - hubInfo - ); - - const estimation = await relayServer.estimateMaxPossibleGas( - httpEnvelopingTxRequest - ); - - const httpEnvelopingTxRequestWithEstimation = - await createAndStringifyEnvelopingTxRequest( - { - ...userDefinedRelayRequest, - request: { - ...userDefinedRelayRequest.request, - tokenAmount: estimation.requiredTokenAmount, - }, - }, - relayClient, - hubInfo - ); - - const balanceBefore = await token.balanceOf(smartWallet.address); - - await expect( - relayServer.createRelayTransaction( - httpEnvelopingTxRequestWithEstimation - ) - ).to.be.fulfilled; - - const balanceAfter = await token.balanceOf(smartWallet.address); - - expect(balanceAfter).to.be.equal( - balanceBefore.sub(estimation.requiredTokenAmount) - ); - }); - }); - - describe('deploy transaction', function () { - const minIndex = 1; - const maxIndex = 1000000000; - let nextWalletIndex: number; - let smartWalletAddress: string; - - beforeEach(async function () { - nextWalletIndex = Math.floor( - Math.random() * (maxIndex - minIndex + 1) + minIndex - ); - smartWalletAddress = await smartWalletFactory.getSmartWalletAddress( - owner.address, - constants.AddressZero, - customLogic.address, - utils.keccak256('0x00'), - nextWalletIndex - ); - }); - - it('should estimate paying with erc20 token', async function () { - const token = await prepareToken('TestToken'); - - await mintTokens( - token, - 'TestToken', - utils.parseEther('100'), - smartWalletAddress - ); - - const userDefinedRelayRequest = createDeployUserDefinedRequest( - { - from: owner.address, - tokenContract: token.address, - index: nextWalletIndex, - to: customLogic.address, - }, - { - callForwarder: smartWalletFactory.address, - } - ); - - const httpEnvelopingTxRequest = - await createAndStringifyEnvelopingTxRequest( - userDefinedRelayRequest, - relayClient, - hubInfo, - { isCustom: true } + isCustom: true, + } ); const estimation = await relayServer.estimateMaxPossibleGas(