Skip to content

Commit

Permalink
refactor: test
Browse files Browse the repository at this point in the history
  • Loading branch information
franciscotobar committed Aug 28, 2024
1 parent 7d9751d commit 5e5de7e
Show file tree
Hide file tree
Showing 17 changed files with 665 additions and 665 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"typescript": "4.8.2"
},
"peerDependencies": {
"@rsksmart/rif-relay-contracts": "^2.1.1-beta.0",
"@rsksmart/rif-relay-contracts": "github:rsksmart/rif-relay-contracts#estimation-no-signature",
"ethers": "^5.7.0"
},
"publishConfig": {
Expand Down
111 changes: 6 additions & 105 deletions src/AccountManager.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { providers, Wallet, utils } from 'ethers';
import { getAddress, _TypedDataEncoder } from 'ethers/lib/utils';
import { getProvider, isDeployRequest } from './common';
import { Wallet } from 'ethers';
import { getAddress } from 'ethers/lib/utils';
import { getProvider } from './common';
import type { EnvelopingRequest } from './common';
import {
deployRequestType,
EnvelopingMessageTypes,
getEnvelopingRequestDataV4Field,
relayRequestType,
TypedMessage,
} from './typedRequestData.utils';
import { signEnvelopingRequest } from './signer';

export default class AccountManager {
private static instance: AccountManager;
Expand Down Expand Up @@ -56,107 +50,14 @@ export default class AccountManager {
envelopingRequest: EnvelopingRequest,
signerWalletOnTheFly?: Wallet
): Promise<string> {
const callForwarder = envelopingRequest.relayData.callForwarder.toString();
const provider = getProvider();
const { chainId } = await provider.getNetwork();
const fromAddress = getAddress(envelopingRequest.request.from.toString());
const fromAddress = getAddress(await envelopingRequest.request.from);

const signerWallet =
signerWalletOnTheFly ||
this._accounts.find(
(account) => getAddress(account.address) === fromAddress
);

const data = getEnvelopingRequestDataV4Field({
chainId,
verifier: callForwarder,
envelopingRequest,
requestTypes: isDeployRequest(envelopingRequest)
? deployRequestType
: relayRequestType,
});

const { signature, recoveredAddr } = await this._getSignatureFromTypedData(
data,
fromAddress,
signerWallet
).catch((error) => {
throw new Error(
`Failed to sign relayed transaction for ${fromAddress}: ${
error as string
}`
);
});

if (recoveredAddr !== fromAddress) {
throw new Error(
`Internal RelayClient exception: signature is not correct: sender=${fromAddress}, recovered=${recoveredAddr}`
);
}

return signature;
}

private async _getSignatureFromTypedData(
data: TypedMessage<EnvelopingMessageTypes>,
from: string,
wallet?: Wallet
): Promise<{ signature: string; recoveredAddr: string }> {
const signature: string = wallet
? await this._signWithWallet(wallet, data)
: await this._signWithProvider(from, data);
const recoveredAddr = this._recoverSignature(data, signature);

return { signature, recoveredAddr };
}

private _recoverSignature(
data: TypedMessage<EnvelopingMessageTypes>,
signature: string
) {
const { domain, types, value } = data;

return utils.verifyTypedData(domain, types, value, signature);
}

private async _signWithProvider<T>(
from: string,
data: TypedMessage<EnvelopingMessageTypes>,
signatureVersion = 'v4',
jsonStringify = true
): Promise<T> {
const provider = getProvider() as providers.JsonRpcProvider;
if (!provider.send) {
throw new Error(`Not an RPC provider`);
}

const { domain, types, value } = data;

let encondedData: TypedMessage<EnvelopingMessageTypes> | string;
if (jsonStringify) {
encondedData = JSON.stringify(
_TypedDataEncoder.getPayload(domain, types, value)
);
} else {
encondedData = _TypedDataEncoder.getPayload(
domain,
types,
value
) as TypedMessage<EnvelopingMessageTypes>;
}

return (await provider.send(`eth_signTypedData_${signatureVersion}`, [
from,
encondedData,
])) as T;
}

private async _signWithWallet(
wallet: Wallet,
data: TypedMessage<EnvelopingMessageTypes>
): Promise<string> {
const { domain, types, value } = data;

return await wallet._signTypedData(domain, types, value);
return signEnvelopingRequest(envelopingRequest, fromAddress, signerWallet);
}
}
13 changes: 12 additions & 1 deletion src/RelayClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import {
getSmartWalletAddress,
isDataEmpty,
maxPossibleGasVerification,
SERVER_SIGNATURE_REQUIRED,
validateRelayResponse,
} from './utils';

Expand Down Expand Up @@ -322,9 +323,13 @@ class RelayClient extends EnvelopingEventEmitter {
const accountManager = AccountManager.getInstance();

const signerWallet = options?.signerWallet;
const serverSignature = options?.serverSignature;

const metadata: EnvelopingMetadata = {
relayHubAddress: await relayHub,
signature: await accountManager.sign(updatedRelayRequest, signerWallet),
signature: serverSignature
? SERVER_SIGNATURE_REQUIRED
: await accountManager.sign(updatedRelayRequest, signerWallet),
relayMaxNonce,
isCustom: options?.isCustom,
};
Expand Down Expand Up @@ -438,6 +443,10 @@ class RelayClient extends EnvelopingEventEmitter {
envelopingRequest: UserDefinedEnvelopingRequest,
options?: RelayTxOptions
): Promise<Transaction> {
if (options?.serverSignature) {
throw new Error('Transactions can only be relayed with client signature');
}

const { envelopingTx, activeRelay } = await this._getHubEnvelopingTx(
envelopingRequest,
options
Expand Down Expand Up @@ -474,6 +483,8 @@ class RelayClient extends EnvelopingEventEmitter {
`Relay Client - Relay Hub:${envelopingTx.metadata.relayHubAddress.toString()}`
);

console.log(envelopingTx.metadata);

return await this._httpClient.estimateMaxPossibleGas(
url.toString(),
envelopingTx
Expand Down
1 change: 1 addition & 0 deletions src/common/relayClient.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type RelayTxOptions = {
signerWallet?: Wallet;
ignoreVerifications?: Array<IgnoreVerifications>;
isCustom?: boolean;
serverSignature?: boolean;
};

type SmartWalletAddressTxOptions = {
Expand Down
140 changes: 106 additions & 34 deletions src/gasEstimator/gasEstimator.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,109 @@
import { BigNumber, utils } from 'ethers';
import { getSmartWalletAddress, estimatePaymentGas } from '../utils';
import { BigNumber, constants, utils, Wallet } from 'ethers';
import { estimatePaymentGas, estimateInternalCallGas } from '../utils';
import { isDeployRequest } from '../common/relayRequest.utils';
import type { EnvelopingTxRequest } from '../common/relayTransaction.types';
import {
POST_RELAY_GAS_COST,
POST_DEPLOY_GAS_COST,
PRE_RELAY_GAS_COST,
resolveSmartWalletAddress,
standardMaxPossibleGasEstimation,
linearFitMaxPossibleGasEstimation,
} from './utils';
import type { RelayTxOptions } from 'src/common';
import {
getProvider,
type EnvelopingRequest,
type RelayTxOptions,
} from '../common';
import { RelayHub__factory } from '@rsksmart/rif-relay-contracts';
import { signEnvelopingRequest } from '../signer';

//TODO: add validation where tokenAmount is zero
const estimateRelayMaxPossibleGas = async (
envelopingRequest: EnvelopingTxRequest,
relayWorkerAddress: string,
options?: RelayTxOptions
): Promise<BigNumber> => {
const {
const { relayRequest } = envelopingRequest;

const tokenAmount = await relayRequest.request.tokenAmount;

if (!BigNumber.from(tokenAmount).isZero()) {
throw Error('Token amount needs to be 0');
}

const smartWalletAddress = await resolveSmartWalletAddress(
relayRequest,
metadata: { signature },
} = envelopingRequest;
options
);

const tokenEstimation = await estimatePaymentGas({
relayRequest: {
...relayRequest,
request: {
...relayRequest.request,
tokenAmount: utils.formatUnits(1, 'wei'),
},
},
preDeploySWAddress: smartWalletAddress,
});

const maxPossibleGas = await standardMaxPossibleGasEstimation(
envelopingRequest,
relayWorkerAddress
);

const isSmartWalletDeploy = isDeployRequest(relayRequest);
return maxPossibleGas.add(tokenEstimation);
};

const { from, index, recoverer, to, data } = relayRequest.request;
const estimateRelayMaxPossibleGasNoSignature = async (
relayRequest: EnvelopingRequest,
signer: Wallet,
options?: RelayTxOptions
) => {
const tokenAmount = await relayRequest.request.tokenAmount;

const smartWalletIndex = await index;
if (!BigNumber.from(tokenAmount).isZero()) {
throw Error('Token amount needs to be 0');
}

const callForwarder = relayRequest.relayData.callForwarder.toString();
const {
request,
relayData: { gasPrice },
} = relayRequest;

const isCustom = options?.isCustom;
const preDeploySWAddress = isSmartWalletDeploy
? await getSmartWalletAddress({
owner: await from,
smartWalletIndex: smartWalletIndex!,
recoverer: await recoverer,
to: await to,
data: await data,
factoryAddress: callForwarder,
isCustom,
})
: undefined;
let relayEstimation = BigNumber.from(PRE_RELAY_GAS_COST);
let reduction = POST_DEPLOY_GAS_COST;
if (isDeployRequest(relayRequest)) {
const from = signer.address;
const updatedRelayRequest = {
request: {
...relayRequest.request,
from,
to: constants.AddressZero,
},
relayData: {
...relayRequest.relayData,
},
};
const signature = signEnvelopingRequest(updatedRelayRequest, from, signer);
const provider = getProvider();
const relayHub = RelayHub__factory.connect(
await request.relayHub,
provider
);

relayEstimation = await relayHub.estimateGas.deployCall(
updatedRelayRequest,
signature,
{ gasPrice, from }
);

reduction = POST_RELAY_GAS_COST;
}

const smartWalletAddress = await resolveSmartWalletAddress(
relayRequest,
options
);

const tokenEstimation = await estimatePaymentGas({
relayRequest: {
Expand All @@ -48,19 +113,26 @@ const estimateRelayMaxPossibleGas = async (
tokenAmount: utils.formatUnits(1, 'wei'),
},
},
preDeploySWAddress,
preDeploySWAddress: smartWalletAddress,
});

if (signature > '0x0') {
const maxPossibleGas = await standardMaxPossibleGasEstimation(
envelopingRequest,
relayWorkerAddress
);

return maxPossibleGas.add(tokenEstimation);
const { to, data } = relayRequest.request;
let internalEstimation = BigNumber.from(0);
if (to != constants.AddressZero) {
internalEstimation = await estimateInternalCallGas({
data,
from: smartWalletAddress,
to,
gasPrice,
});
}

return await linearFitMaxPossibleGasEstimation(relayRequest, tokenEstimation);
return relayEstimation
.add(tokenEstimation)
.add(internalEstimation)
.add(POST_DEPLOY_GAS_COST)
.add(POST_RELAY_GAS_COST)
.sub(reduction);
};

export { estimateRelayMaxPossibleGas };
export { estimateRelayMaxPossibleGas, estimateRelayMaxPossibleGasNoSignature };
Loading

0 comments on commit 5e5de7e

Please sign in to comment.