Skip to content

Commit

Permalink
Hotfix for TokenPaymaster unable to convert price causing exception (#…
Browse files Browse the repository at this point in the history
…969)

* Avoid unnecessary call to 'getSigner' without try/catch in LightTruffleContract

* Wrap view calls to the Permit Paymaster with try/catch

* Avoid conversion to wei for really large token amounts (usually approvals)
  • Loading branch information
forshtat authored Apr 11, 2023
1 parent a435a57 commit 0f42e4a
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 9 deletions.
5 changes: 2 additions & 3 deletions packages/common/src/LightTruffleContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ function retype (outputs?: readonly JsonFragment[], ret?: any): any {

export class Contract<T> {
provider!: JsonRpcProvider
signer!: JsonRpcSigner

constructor (readonly contractName: string, readonly abi: JsonFragment[]) {
}
Expand All @@ -74,7 +73,8 @@ export class Contract<T> {
// the 'getAddress' may throw if the underlying provider does not return addresses.
let signer: JsonRpcSigner | undefined
try {
const signerFromAddress = await this.signer.getAddress()
const noAddressSetSigner: JsonRpcSigner = this.provider.getSigner()
const signerFromAddress = await noAddressSetSigner.getAddress()
signer = this.provider.getSigner(signerFromAddress)
} catch (e: any) {
// nothing to do here - signer does not have accounts and can only work with ephemeral keys
Expand Down Expand Up @@ -129,7 +129,6 @@ export class Contract<T> {

setProvider (provider: JsonRpcProvider, _: unknown): void {
this.provider = provider
this.signer = provider.getSigner()
}
}

Expand Down
41 changes: 36 additions & 5 deletions packages/paymasters/src/TokenPaymasterInteractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ import BN from 'bn.js'

import { JsonRpcProvider, ExternalProvider } from '@ethersproject/providers'

import { Address, Contract, TruffleContract, wrapWeb3JsProvider } from '@opengsn/common'
import {
Address,
Contract,
LoggerInterface,
TruffleContract,
constants,
toBN,
wrapWeb3JsProvider
} from '@opengsn/common'
import { IERC20Instance } from '@opengsn/contracts'
import { IChainlinkOracleInstance, PermitERC20UniswapV3PaymasterInstance } from '../types/truffle-contracts'

Expand All @@ -26,6 +34,7 @@ export class TokenPaymasterInteractor {
private readonly PermitERC20UniswapV3Paymaster: Contract<PermitERC20UniswapV3PaymasterInstance>
private readonly ChainlinkOracle: Contract<IChainlinkOracleInstance>
private readonly paymasterAddress: Address
private readonly logger: LoggerInterface

tokenAddress?: Address
tokenSwapData?: TokenSwapData
Expand All @@ -35,9 +44,11 @@ export class TokenPaymasterInteractor {

constructor (
provider: JsonRpcProvider | ExternalProvider,
paymasterAddress: Address
paymasterAddress: Address,
logger: LoggerInterface
) {
this.paymasterAddress = paymasterAddress
this.logger = logger
this.provider = wrapWeb3JsProvider(provider)
this.ERC20Instance = TruffleContract({
contractName: 'ERC20Instance',
Expand Down Expand Up @@ -108,8 +119,28 @@ export class TokenPaymasterInteractor {
const tokenSwapData = await this.paymaster.getTokenSwapData(tokenAddress)
const chainlinkInstance = await this._createChainlinkOracleInstance(tokenSwapData.priceFeed)
const quote = await chainlinkInstance.latestAnswer()
const actualQuote = await this.paymaster.toActualQuote(quote.toString(), tokenSwapData.priceDivisor.toString())
const amountInWei = await this.paymaster.tokenToWei(tokenAmount.toString(), actualQuote.toString(), tokenSwapData.reverseQuote)
return { actualQuote, amountInWei }
const description = `(tokenAddress=${tokenAddress} tokenAmount=${tokenAmount.toString()} priceFeed=${tokenSwapData.priceFeed} quote=${quote.toString()} priceDivisor=${tokenSwapData.priceDivisor.toString()} reverseQuote=${tokenSwapData.reverseQuote})`
this.logger.debug(`Converting token balance to Ether quote ${description}`)
try {
const actualQuote = await this.paymaster.toActualQuote(quote.toString(), tokenSwapData.priceDivisor.toString())
this.logger.debug(`actualQuote=${actualQuote.toString()}`)
if (tokenAmount.gt(toBN(10).pow(toBN(30)))) {
this.logger.debug(`Amount to convert is > 1e30 which is infinity in most cases (tokenAddress=${tokenAddress})`)
return {
actualQuote: toBN(0),
amountInWei: constants.MAX_UINT256
}
}
const amountInWei = await this.paymaster.tokenToWei(tokenAmount.toString(), actualQuote.toString(), tokenSwapData.reverseQuote)
this.logger.debug(`amountInWei=${amountInWei.toString()}`)
return { actualQuote, amountInWei }
} catch (error: any) {
this.logger.error(`Failed to convert token balance to Ether quote ${description}`)
this.logger.error(error)
return {
actualQuote: toBN(0),
amountInWei: toBN(0)
}
}
}
}
2 changes: 1 addition & 1 deletion packages/paymasters/src/TokenPaymasterProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class TokenPaymasterProvider extends RelayProvider {
const chainId = this.origProvider.network.chainId

const paymasterAddress = getPaymasterAddressByTypeAndChain(this.config?.paymasterAddress, chainId, this.logger)
this.tokenPaymasterInteractor = new TokenPaymasterInteractor(this.origProvider, paymasterAddress as any)
this.tokenPaymasterInteractor = new TokenPaymasterInteractor(this.origProvider, paymasterAddress as any, this.logger)
await this.tokenPaymasterInteractor.init()
this.relayClient.dependencies.asyncPaymasterData = this._buildPaymasterData.bind(this)
if (permitERC20TokenForGas == null) {
Expand Down

0 comments on commit 0f42e4a

Please sign in to comment.