Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

estimation-no-signature #96

Merged
merged 10 commits into from
Sep 2, 2024
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",
antomor marked this conversation as resolved.
Show resolved Hide resolved
"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);
}
}
18 changes: 15 additions & 3 deletions 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 @@ -354,7 +359,7 @@ class RelayClient extends EnvelopingEventEmitter {
): Promise<BigNumber> {
const {
request: { tokenGas, tokenAmount },
relayData: { callForwarder },
relayData,
} = envelopingRequest;

const currentTokenAmount = BigNumber.from(tokenAmount);
Expand All @@ -379,6 +384,7 @@ class RelayClient extends EnvelopingEventEmitter {
data: string;
};

const callForwarder = await relayData.callForwarder;
const origin = isDeployment
? await getSmartWalletAddress({
owner: from,
Expand All @@ -387,8 +393,9 @@ class RelayClient extends EnvelopingEventEmitter {
to,
data,
isCustom,
factoryAddress: callForwarder,
})
: await callForwarder;
: callForwarder;

return await estimatePaymentGas({
relayRequest: {
Expand Down Expand Up @@ -438,6 +445,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 @@ -473,6 +484,7 @@ class RelayClient extends EnvelopingEventEmitter {
log.debug(
`Relay Client - Relay Hub:${envelopingTx.metadata.relayHubAddress.toString()}`
);
log.debug('Relay Client - Request metadata:', envelopingTx.metadata);

return await this._httpClient.estimateMaxPossibleGas(
url.toString(),
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
Loading
Loading