@cowprotocol/permit-utils 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/1inchPermitUtils-BDO_CqjL.cjs +1 -0
- package/1inchPermitUtils-CXu6m1-9.js +4 -0
- package/abi/versionAbis.d.ts +15 -0
- package/const.d.ts +24 -0
- package/consts/1inchPermitUtils.d.ts +25 -0
- package/{src/imports/1inchPermitUtils.ts → imports/1inchPermitUtils.d.ts} +1 -3
- package/index.cjs +1 -0
- package/{src/index.ts → index.d.ts} +8 -11
- package/index.js +547 -0
- package/lib/checkIsCallDataAValidPermit.d.ts +3 -0
- package/lib/generatePermitHook.d.ts +2 -0
- package/lib/getPermitUtilsInstance.d.ts +3 -0
- package/lib/getTokenPermitInfo.d.ts +2 -0
- package/package.json +19 -7
- package/types.d.ts +47 -0
- package/utils/PermitProviderConnector.d.ts +13 -0
- package/utils/buildPermitCallData.d.ts +3 -0
- package/utils/getContract.d.ts +3 -0
- package/utils/getEip712Domain.d.ts +13 -0
- package/utils/getPermitDeadline.d.ts +1 -0
- package/utils/getTokenName.d.ts +2 -0
- package/utils/getTokenPermitVersion.d.ts +2 -0
- package/utils/isSupportedPermitInfo.d.ts +2 -0
- package/CHANGELOG.md +0 -308
- package/jest.config.ts +0 -11
- package/project.json +0 -42
- package/src/abi/erc20.json +0 -59
- package/src/abi/versionAbis.ts +0 -37
- package/src/const.ts +0 -20
- package/src/consts/1inchPermitUtils.ts +0 -68
- package/src/lib/checkIsCallDataAValidPermit.ts +0 -64
- package/src/lib/generatePermitHook.ts +0 -133
- package/src/lib/getPermitUtilsInstance.ts +0 -51
- package/src/lib/getTokenPermitInfo.ts +0 -281
- package/src/types.ts +0 -61
- package/src/utils/PermitProviderConnector.ts +0 -75
- package/src/utils/buildPermitCallData.ts +0 -26
- package/src/utils/getContract.ts +0 -6
- package/src/utils/getEip712Domain.ts +0 -70
- package/src/utils/getPermitDeadline.ts +0 -7
- package/src/utils/getTokenName.ts +0 -13
- package/src/utils/getTokenPermitVersion.ts +0 -35
- package/src/utils/isSupportedPermitInfo.ts +0 -5
- package/tsconfig.json +0 -22
- package/tsconfig.lib.json +0 -10
- package/tsconfig.spec.json +0 -9
- package/vite.config.mts +0 -51
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @1inch/permit-signed-approvals-utils has a lot of extra variables and increases bundle size.
|
|
3
|
-
*
|
|
4
|
-
* At the same time it's repo is archived.
|
|
5
|
-
*
|
|
6
|
-
* Therefore we just copied needed constants, and when it's not possible, we should use import(...)
|
|
7
|
-
*
|
|
8
|
-
* You also can use `import type ... from '@1inch/permit-signed-approvals-utils'`
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export const DAI_EIP_2612_PERMIT_ABI = [
|
|
12
|
-
{
|
|
13
|
-
constant: false,
|
|
14
|
-
inputs: [
|
|
15
|
-
{
|
|
16
|
-
internalType: 'address',
|
|
17
|
-
name: 'holder',
|
|
18
|
-
type: 'address',
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
internalType: 'address',
|
|
22
|
-
name: 'spender',
|
|
23
|
-
type: 'address',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
internalType: 'uint256',
|
|
27
|
-
name: 'nonce',
|
|
28
|
-
type: 'uint256',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
internalType: 'uint256',
|
|
32
|
-
name: 'expiry',
|
|
33
|
-
type: 'uint256',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
internalType: 'bool',
|
|
37
|
-
name: 'allowed',
|
|
38
|
-
type: 'bool',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
internalType: 'uint8',
|
|
42
|
-
name: 'v',
|
|
43
|
-
type: 'uint8',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
internalType: 'bytes32',
|
|
47
|
-
name: 'r',
|
|
48
|
-
type: 'bytes32',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
internalType: 'bytes32',
|
|
52
|
-
name: 's',
|
|
53
|
-
type: 'bytes32',
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
name: 'permit',
|
|
57
|
-
outputs: [],
|
|
58
|
-
payable: false,
|
|
59
|
-
stateMutability: 'nonpayable',
|
|
60
|
-
type: 'function',
|
|
61
|
-
},
|
|
62
|
-
]
|
|
63
|
-
|
|
64
|
-
export const EIP_2612_PERMIT_SELECTOR = '0xd505accf'
|
|
65
|
-
|
|
66
|
-
export const DAI_PERMIT_SELECTOR = '0x8fcbaf0c'
|
|
67
|
-
|
|
68
|
-
export const DAI_LIKE_PERMIT_TYPEHASH = '0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb'
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { oneInchPermitUtilsConsts } from '..'
|
|
2
|
-
import { PermitInfo } from '../types'
|
|
3
|
-
|
|
4
|
-
import type { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils'
|
|
5
|
-
|
|
6
|
-
export async function checkIsCallDataAValidPermit(
|
|
7
|
-
owner: string,
|
|
8
|
-
chainId: number,
|
|
9
|
-
eip2612Utils: Eip2612PermitUtils,
|
|
10
|
-
tokenAddress: string,
|
|
11
|
-
_tokenName: string | undefined,
|
|
12
|
-
callData: string,
|
|
13
|
-
{ version, type, name }: PermitInfo,
|
|
14
|
-
): Promise<boolean | undefined> {
|
|
15
|
-
// TODO: take name only from PermitInfo
|
|
16
|
-
const tokenName = name || _tokenName
|
|
17
|
-
|
|
18
|
-
if (type === 'unsupported') {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!tokenName) {
|
|
23
|
-
throw new Error(`No token name for ${tokenAddress}`)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const params = { chainId, tokenName, tokenAddress, callData, version }
|
|
27
|
-
|
|
28
|
-
let recoverPermitOwnerPromise: Promise<string> | undefined = undefined
|
|
29
|
-
|
|
30
|
-
// If pre-hook doesn't start with either selector, it's not a permit
|
|
31
|
-
if (callData.startsWith(oneInchPermitUtilsConsts.EIP_2612_PERMIT_SELECTOR)) {
|
|
32
|
-
recoverPermitOwnerPromise = eip2612Utils.recoverPermitOwnerFromCallData({
|
|
33
|
-
...params,
|
|
34
|
-
// I don't know why this was removed, ok?
|
|
35
|
-
// We added it back on buildPermitCallData.ts
|
|
36
|
-
// But it looks like this is needed 🤷
|
|
37
|
-
// Check the test for this method https://github.com/1inch/permit-signed-approvals-utils/blob/master/src/eip-2612-permit.test.ts#L85-L106
|
|
38
|
-
callData: callData.replace(oneInchPermitUtilsConsts.EIP_2612_PERMIT_SELECTOR, '0x'),
|
|
39
|
-
})
|
|
40
|
-
} else if (callData.startsWith(oneInchPermitUtilsConsts.DAI_PERMIT_SELECTOR)) {
|
|
41
|
-
recoverPermitOwnerPromise = eip2612Utils.recoverDaiLikePermitOwnerFromCallData({
|
|
42
|
-
...params,
|
|
43
|
-
callData: callData.replace(oneInchPermitUtilsConsts.DAI_PERMIT_SELECTOR, '0x'),
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!recoverPermitOwnerPromise) {
|
|
48
|
-
// The callData doesn't match any known permit type
|
|
49
|
-
return undefined
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
const recoveredOwner = await recoverPermitOwnerPromise
|
|
54
|
-
|
|
55
|
-
// Permit is valid when recovered owner matches order owner
|
|
56
|
-
return recoveredOwner.toLowerCase() === owner.toLowerCase()
|
|
57
|
-
} catch (e) {
|
|
58
|
-
console.debug(
|
|
59
|
-
`[checkHasValidPendingPermit] Failed to check permit validity for owner ${owner} with callData ${callData}`,
|
|
60
|
-
e,
|
|
61
|
-
)
|
|
62
|
-
return false
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { PERMIT_HOOK_DAPP_ID } from '@cowprotocol/hook-dapp-lib'
|
|
2
|
-
import { JsonRpcProvider } from '@ethersproject/providers'
|
|
3
|
-
|
|
4
|
-
import { DEFAULT_PERMIT_GAS_LIMIT, DEFAULT_PERMIT_VALUE, PERMIT_SIGNER } from '../const'
|
|
5
|
-
import { PermitHookData, PermitHookParams } from '../types'
|
|
6
|
-
import { buildDaiLikePermitCallData, buildEip2612PermitCallData } from '../utils/buildPermitCallData'
|
|
7
|
-
import { getPermitDeadline } from '../utils/getPermitDeadline'
|
|
8
|
-
import { isSupportedPermitInfo } from '../utils/isSupportedPermitInfo'
|
|
9
|
-
|
|
10
|
-
const REQUESTS_CACHE: { [permitKey: string]: Promise<PermitHookData | undefined> } = {}
|
|
11
|
-
|
|
12
|
-
export async function generatePermitHook(params: PermitHookParams): Promise<PermitHookData | undefined> {
|
|
13
|
-
const permitKey = getCacheKey(params)
|
|
14
|
-
|
|
15
|
-
const cachedRequest = REQUESTS_CACHE[permitKey]
|
|
16
|
-
|
|
17
|
-
if (cachedRequest) {
|
|
18
|
-
return await cachedRequest
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const request = generatePermitHookRaw(params)
|
|
22
|
-
.catch((e) => {
|
|
23
|
-
console.debug(`[generatePermitHook] cached request failed`, e)
|
|
24
|
-
return undefined
|
|
25
|
-
})
|
|
26
|
-
.finally(() => {
|
|
27
|
-
// Remove consumed request to avoid stale data
|
|
28
|
-
delete REQUESTS_CACHE[permitKey]
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
REQUESTS_CACHE[permitKey] = request
|
|
32
|
-
|
|
33
|
-
return request
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function generatePermitHookRaw(params: PermitHookParams): Promise<PermitHookData> {
|
|
37
|
-
const { inputToken, spender, chainId, permitInfo, provider, account, eip2612Utils, nonce: preFetchedNonce } = params
|
|
38
|
-
|
|
39
|
-
const tokenAddress = inputToken.address
|
|
40
|
-
// TODO: remove the need for `name` from input token. Should come from permitInfo instead
|
|
41
|
-
const tokenName = permitInfo.name || inputToken.name
|
|
42
|
-
|
|
43
|
-
if (!isSupportedPermitInfo(permitInfo)) {
|
|
44
|
-
throw new Error(`Trying to generate permit hook for unsupported token: ${tokenAddress}`)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!tokenName) {
|
|
48
|
-
throw new Error(`No token name for token: ${tokenAddress}`)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const owner = account || PERMIT_SIGNER.address
|
|
52
|
-
|
|
53
|
-
// Only fetch the nonce in case it wasn't pre-fetched before
|
|
54
|
-
// That's the case for static account
|
|
55
|
-
const nonce = preFetchedNonce === undefined ? await eip2612Utils.getTokenNonce(tokenAddress, owner) : preFetchedNonce
|
|
56
|
-
|
|
57
|
-
const deadline = getPermitDeadline()
|
|
58
|
-
const value = params.amount || DEFAULT_PERMIT_VALUE
|
|
59
|
-
|
|
60
|
-
const callData =
|
|
61
|
-
permitInfo.type === 'eip-2612'
|
|
62
|
-
? await buildEip2612PermitCallData({
|
|
63
|
-
eip2612Utils,
|
|
64
|
-
callDataParams: [
|
|
65
|
-
{
|
|
66
|
-
owner,
|
|
67
|
-
spender,
|
|
68
|
-
value: value.toString(),
|
|
69
|
-
nonce,
|
|
70
|
-
deadline,
|
|
71
|
-
},
|
|
72
|
-
chainId as number,
|
|
73
|
-
tokenName,
|
|
74
|
-
tokenAddress,
|
|
75
|
-
permitInfo.version,
|
|
76
|
-
],
|
|
77
|
-
})
|
|
78
|
-
: await buildDaiLikePermitCallData({
|
|
79
|
-
eip2612Utils,
|
|
80
|
-
callDataParams: [
|
|
81
|
-
{
|
|
82
|
-
holder: owner,
|
|
83
|
-
spender,
|
|
84
|
-
allowed: true,
|
|
85
|
-
value: value.toString(),
|
|
86
|
-
nonce,
|
|
87
|
-
expiry: deadline,
|
|
88
|
-
},
|
|
89
|
-
chainId as number,
|
|
90
|
-
tokenName,
|
|
91
|
-
tokenAddress,
|
|
92
|
-
permitInfo.version,
|
|
93
|
-
],
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const gasLimit = await calculateGasLimit(callData, owner, tokenAddress, provider, !!account)
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
target: tokenAddress,
|
|
100
|
-
callData,
|
|
101
|
-
gasLimit,
|
|
102
|
-
dappId: PERMIT_HOOK_DAPP_ID,
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function calculateGasLimit(
|
|
107
|
-
data: string,
|
|
108
|
-
from: string,
|
|
109
|
-
to: string,
|
|
110
|
-
provider: JsonRpcProvider,
|
|
111
|
-
isUserAccount: boolean,
|
|
112
|
-
): Promise<string> {
|
|
113
|
-
try {
|
|
114
|
-
// Query the actual gas estimate
|
|
115
|
-
const actual = await provider.estimateGas({ data, from, to })
|
|
116
|
-
|
|
117
|
-
// Add 10% to actual value to account for minor differences with real account
|
|
118
|
-
// Do not add it if this is the real user's account
|
|
119
|
-
const gasLimit = !isUserAccount ? actual.add(actual.div(10)) : actual
|
|
120
|
-
|
|
121
|
-
// Pick the biggest between estimated and default
|
|
122
|
-
return gasLimit.gt(DEFAULT_PERMIT_GAS_LIMIT) ? gasLimit.toString() : DEFAULT_PERMIT_GAS_LIMIT
|
|
123
|
-
} catch (e) {
|
|
124
|
-
console.debug(`[calculatePermitGasLimit] Failed to estimateGas, using default`, e)
|
|
125
|
-
|
|
126
|
-
return DEFAULT_PERMIT_GAS_LIMIT
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function getCacheKey(params: PermitHookParams): string {
|
|
131
|
-
const { inputToken, chainId, account, amount } = params
|
|
132
|
-
return `${inputToken.address.toLowerCase()}-${chainId}${account ? `-${account.toLowerCase()}` : ''}${amount ? `-${amount.toString()}` : ''}`
|
|
133
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { JsonRpcProvider } from '@ethersproject/providers'
|
|
2
|
-
|
|
3
|
-
import { PERMIT_SIGNER } from '../const'
|
|
4
|
-
import { PermitProviderConnector } from '../utils/PermitProviderConnector'
|
|
5
|
-
|
|
6
|
-
import type { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Cache by network. Here we don't care about the provider as a static account will be used for the signature
|
|
10
|
-
*/
|
|
11
|
-
const CHAIN_UTILS_CACHE = new Map<number, Eip2612PermitUtils>()
|
|
12
|
-
/**
|
|
13
|
-
* Cache by provider. Here we cache per provider as each account should have its own instance
|
|
14
|
-
*/
|
|
15
|
-
const PROVIDER_UTILS_CACHE = new Map<string, Eip2612PermitUtils>()
|
|
16
|
-
|
|
17
|
-
export async function getPermitUtilsInstance(
|
|
18
|
-
chainId: number,
|
|
19
|
-
provider: JsonRpcProvider,
|
|
20
|
-
account?: string | undefined,
|
|
21
|
-
): Promise<Eip2612PermitUtils> {
|
|
22
|
-
const chainCache = CHAIN_UTILS_CACHE.get(chainId)
|
|
23
|
-
|
|
24
|
-
if (!account && chainCache) {
|
|
25
|
-
return chainCache
|
|
26
|
-
}
|
|
27
|
-
const providerCacheKey = `${chainId}-${account}`
|
|
28
|
-
const providerCache = PROVIDER_UTILS_CACHE.get(providerCacheKey)
|
|
29
|
-
|
|
30
|
-
if (providerCache) {
|
|
31
|
-
return providerCache
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// TODO: allow to receive the signer as a parameter
|
|
35
|
-
const web3ProviderConnector = new PermitProviderConnector(provider, account ? undefined : PERMIT_SIGNER)
|
|
36
|
-
const Eip2612PermitUtilsClass = await import('../imports/1inchPermitUtils').then((r) => r.Eip2612PermitUtils)
|
|
37
|
-
const eip2612PermitUtils = new Eip2612PermitUtilsClass(web3ProviderConnector, { enabledCheckSalt: true })
|
|
38
|
-
|
|
39
|
-
if (!account) {
|
|
40
|
-
console.log(`[getPermitUtilsInstance] Set cached chain utils for chain ${chainId}`, eip2612PermitUtils)
|
|
41
|
-
CHAIN_UTILS_CACHE.set(chainId, eip2612PermitUtils)
|
|
42
|
-
} else {
|
|
43
|
-
console.log(
|
|
44
|
-
`[getPermitUtilsInstance] Set cached provider utils for chain ${chainId}-${account}`,
|
|
45
|
-
eip2612PermitUtils,
|
|
46
|
-
)
|
|
47
|
-
PROVIDER_UTILS_CACHE.set(providerCacheKey, eip2612PermitUtils)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return eip2612PermitUtils
|
|
51
|
-
}
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
import { getTokenId } from '@cowprotocol/cow-sdk'
|
|
2
|
-
import type { JsonRpcProvider } from '@ethersproject/providers'
|
|
3
|
-
|
|
4
|
-
import { getPermitUtilsInstance } from './getPermitUtilsInstance'
|
|
5
|
-
|
|
6
|
-
import { oneInchPermitUtilsConsts } from '..'
|
|
7
|
-
import { DEFAULT_MIN_GAS_LIMIT, DEFAULT_PERMIT_VALUE, PERMIT_SIGNER } from '../const'
|
|
8
|
-
import { GetTokenPermitInfoParams, GetTokenPermitIntoResult, PermitInfo, PermitType } from '../types'
|
|
9
|
-
import { buildDaiLikePermitCallData, buildEip2612PermitCallData } from '../utils/buildPermitCallData'
|
|
10
|
-
import { Eip712Domain, getEip712Domain } from '../utils/getEip712Domain'
|
|
11
|
-
import { getPermitDeadline } from '../utils/getPermitDeadline'
|
|
12
|
-
import { getTokenName } from '../utils/getTokenName'
|
|
13
|
-
import { getTokenPermitVersion } from '../utils/getTokenPermitVersion'
|
|
14
|
-
|
|
15
|
-
import type { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils'
|
|
16
|
-
|
|
17
|
-
const EIP_2612_PERMIT_PARAMS = {
|
|
18
|
-
nonce: 0,
|
|
19
|
-
deadline: getPermitDeadline(),
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const DAI_LIKE_PERMIT_PARAMS = {
|
|
23
|
-
allowed: true,
|
|
24
|
-
nonce: 0,
|
|
25
|
-
expiry: getPermitDeadline(),
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const REQUESTS_CACHE: Record<string, Promise<GetTokenPermitIntoResult>> = {}
|
|
29
|
-
|
|
30
|
-
const UNSUPPORTED: PermitInfo = { type: 'unsupported' }
|
|
31
|
-
|
|
32
|
-
export async function getTokenPermitInfo(params: GetTokenPermitInfoParams): Promise<GetTokenPermitIntoResult> {
|
|
33
|
-
const { tokenAddress, chainId } = params
|
|
34
|
-
const key = getTokenId({ address: tokenAddress, chainId })
|
|
35
|
-
|
|
36
|
-
const cached = REQUESTS_CACHE[key]
|
|
37
|
-
|
|
38
|
-
if (cached) {
|
|
39
|
-
return cached
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const request = actuallyCheckTokenIsPermittable(params)
|
|
43
|
-
|
|
44
|
-
REQUESTS_CACHE[key] = request
|
|
45
|
-
|
|
46
|
-
return request
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// TODO: Break down this large function into smaller functions
|
|
50
|
-
// TODO: Reduce function complexity by extracting logic
|
|
51
|
-
// eslint-disable-next-line max-lines-per-function, complexity
|
|
52
|
-
async function actuallyCheckTokenIsPermittable(params: GetTokenPermitInfoParams): Promise<GetTokenPermitIntoResult> {
|
|
53
|
-
const { spender, tokenAddress, chainId, provider, minGasLimit, amount } = params
|
|
54
|
-
|
|
55
|
-
const eip2612PermitUtils = await getPermitUtilsInstance(chainId, provider)
|
|
56
|
-
|
|
57
|
-
const owner = PERMIT_SIGNER.address
|
|
58
|
-
|
|
59
|
-
let domain: Eip712Domain | undefined = undefined
|
|
60
|
-
// Try to get eip712domain, which contains most of the info we'll need here
|
|
61
|
-
try {
|
|
62
|
-
domain = await getEip712Domain(tokenAddress, chainId, provider)
|
|
63
|
-
} catch {
|
|
64
|
-
console.debug(`[checkTokenIsPermittable] Couldn't fetch eip712domain for token ${tokenAddress}`)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let tokenName = domain?.name
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
if (!tokenName) {
|
|
71
|
-
tokenName = await getTokenName(tokenAddress, chainId, provider)
|
|
72
|
-
}
|
|
73
|
-
} catch (e) {
|
|
74
|
-
if (/ETIMEDOUT/.test(e) && !tokenName) {
|
|
75
|
-
// Network issue or another temporary failure, return error
|
|
76
|
-
return { error: `Failed to fetch token name from contract. RPC connection error` }
|
|
77
|
-
}
|
|
78
|
-
console.debug(
|
|
79
|
-
`[checkTokenIsPermittable] Couldn't fetch token name from the contract for token ${tokenAddress}, using provided '${tokenName}'`,
|
|
80
|
-
e,
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!tokenName) {
|
|
85
|
-
const error = `Token name could not be determined for ${tokenAddress}`
|
|
86
|
-
return { error }
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
let nonce: number
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
nonce = await eip2612PermitUtils.getTokenNonce(tokenAddress, owner)
|
|
93
|
-
} catch (e) {
|
|
94
|
-
if (e === 'nonce not supported' || e.message === 'nonce is NaN') {
|
|
95
|
-
console.debug(`[checkTokenIsPermittable] Not a permittable token ${tokenAddress} - ${tokenName}`, e?.message || e)
|
|
96
|
-
// Here we know it's not supported, return unsupported
|
|
97
|
-
// See https://github.com/1inch/permit-signed-approvals-utils/blob/b190197a45c3289867ee4e6da93f10dea51ef276/src/eip-2612-permit.utils.ts#L309
|
|
98
|
-
// and https://github.com/1inch/permit-signed-approvals-utils/blob/b190197a45c3289867ee4e6da93f10dea51ef276/src/eip-2612-permit.utils.ts#L325
|
|
99
|
-
return { ...UNSUPPORTED, name: tokenName }
|
|
100
|
-
}
|
|
101
|
-
console.debug(`[checkTokenIsPermittable] Failed to get nonce for ${tokenAddress} - ${tokenName}`, e)
|
|
102
|
-
|
|
103
|
-
// Otherwise, it might have been a network issue or another temporary failure, return error
|
|
104
|
-
return { error: e.message || e.toString() }
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
let version: string | undefined = domain?.version
|
|
108
|
-
|
|
109
|
-
if (version === undefined) {
|
|
110
|
-
// If the token does not outright fails when calling with the `version` value
|
|
111
|
-
// returned by the contract, fetch it.
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
// Required by USDC-mainnet as its version is `2`.
|
|
115
|
-
// There might be other tokens that need this as well.
|
|
116
|
-
version = await getTokenPermitVersion(tokenAddress, provider)
|
|
117
|
-
} catch (e) {
|
|
118
|
-
// Not a problem, we can (try to) continue without it, and will default to `1` (part of the 1inch lib)
|
|
119
|
-
console.debug(`[checkTokenIsPermittable] Failed to get version for ${tokenAddress} - ${tokenName}`, e)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const baseParams: BaseParams = {
|
|
124
|
-
chainId,
|
|
125
|
-
eip2612PermitUtils,
|
|
126
|
-
nonce,
|
|
127
|
-
spender,
|
|
128
|
-
tokenAddress,
|
|
129
|
-
tokenName,
|
|
130
|
-
walletAddress: owner,
|
|
131
|
-
version,
|
|
132
|
-
minGasLimit,
|
|
133
|
-
amount,
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
// Try to estimate with eip-2612 first
|
|
138
|
-
return await estimateTokenPermit({ ...baseParams, type: 'eip-2612', provider })
|
|
139
|
-
} catch (e) {
|
|
140
|
-
// Not eip-2612, try dai-like
|
|
141
|
-
try {
|
|
142
|
-
const isDaiLike = await isDaiLikeTypeHash(tokenAddress, eip2612PermitUtils)
|
|
143
|
-
|
|
144
|
-
if (!isDaiLike) {
|
|
145
|
-
// These might be supported, as they have nonces, but we don't know why the permit call fails
|
|
146
|
-
// TODO: further investigate this kind of token
|
|
147
|
-
// For now mark them as unsupported and don't check it again
|
|
148
|
-
if (/invalid signature/.test(e) || e?.code === 'UNPREDICTABLE_GAS_LIMIT') {
|
|
149
|
-
console.debug(
|
|
150
|
-
`[checkTokenIsPermittable] Token ${tokenAddress} - ${tokenName} might be permittable, but it's not supported for now. Reason:`,
|
|
151
|
-
e?.reason,
|
|
152
|
-
)
|
|
153
|
-
return { ...UNSUPPORTED, name: tokenName }
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Maybe a temporary failure
|
|
157
|
-
console.debug(
|
|
158
|
-
`[checkTokenIsPermittable] Failed to estimate eip-2612 permit for ${tokenAddress} - ${tokenName}`,
|
|
159
|
-
e,
|
|
160
|
-
)
|
|
161
|
-
return { error: e.message || e.toString() }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return await estimateTokenPermit({ ...baseParams, type: 'dai-like', provider })
|
|
165
|
-
} catch (e) {
|
|
166
|
-
// Not dai-like either, return error
|
|
167
|
-
console.debug(
|
|
168
|
-
`[checkTokenIsPermittable] Failed to estimate dai-like permit for ${tokenAddress} - ${tokenName}`,
|
|
169
|
-
e,
|
|
170
|
-
)
|
|
171
|
-
return { error: e.message || e.toString() }
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
type BaseParams = {
|
|
177
|
-
tokenAddress: string
|
|
178
|
-
tokenName: string
|
|
179
|
-
chainId: number
|
|
180
|
-
walletAddress: string
|
|
181
|
-
spender: string
|
|
182
|
-
eip2612PermitUtils: Eip2612PermitUtils
|
|
183
|
-
nonce: number
|
|
184
|
-
version: string | undefined
|
|
185
|
-
minGasLimit?: number | undefined
|
|
186
|
-
amount?: bigint
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
type EstimateParams = BaseParams & {
|
|
190
|
-
type: PermitType
|
|
191
|
-
provider: JsonRpcProvider
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async function estimateTokenPermit(params: EstimateParams): Promise<GetTokenPermitIntoResult> {
|
|
195
|
-
const {
|
|
196
|
-
provider,
|
|
197
|
-
walletAddress,
|
|
198
|
-
tokenAddress,
|
|
199
|
-
tokenName,
|
|
200
|
-
type,
|
|
201
|
-
version,
|
|
202
|
-
minGasLimit = DEFAULT_MIN_GAS_LIMIT,
|
|
203
|
-
} = params
|
|
204
|
-
|
|
205
|
-
const getCallDataFn = type === 'eip-2612' ? getEip2612CallData : getDaiLikeCallData
|
|
206
|
-
|
|
207
|
-
const data = await getCallDataFn(params)
|
|
208
|
-
|
|
209
|
-
if (!data) {
|
|
210
|
-
return { ...UNSUPPORTED, name: tokenName }
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const estimatedGas = await provider.estimateGas({
|
|
214
|
-
data,
|
|
215
|
-
from: walletAddress,
|
|
216
|
-
to: tokenAddress,
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
const gasLimit = estimatedGas.toNumber()
|
|
220
|
-
|
|
221
|
-
return gasLimit > minGasLimit
|
|
222
|
-
? {
|
|
223
|
-
type,
|
|
224
|
-
version,
|
|
225
|
-
name: tokenName,
|
|
226
|
-
}
|
|
227
|
-
: { ...UNSUPPORTED, name: tokenName }
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
async function getEip2612CallData(params: BaseParams): Promise<string> {
|
|
231
|
-
const { eip2612PermitUtils, walletAddress, spender, nonce, chainId, tokenName, tokenAddress, version, amount } =
|
|
232
|
-
params
|
|
233
|
-
return buildEip2612PermitCallData({
|
|
234
|
-
eip2612Utils: eip2612PermitUtils,
|
|
235
|
-
callDataParams: [
|
|
236
|
-
{
|
|
237
|
-
...EIP_2612_PERMIT_PARAMS,
|
|
238
|
-
owner: walletAddress,
|
|
239
|
-
spender,
|
|
240
|
-
nonce,
|
|
241
|
-
value: amount ? amount.toString() : DEFAULT_PERMIT_VALUE.toString(),
|
|
242
|
-
},
|
|
243
|
-
+chainId,
|
|
244
|
-
tokenName,
|
|
245
|
-
tokenAddress,
|
|
246
|
-
version,
|
|
247
|
-
],
|
|
248
|
-
})
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
async function isDaiLikeTypeHash(tokenAddress: string, eip2612PermitUtils: Eip2612PermitUtils): Promise<boolean> {
|
|
252
|
-
const permitTypeHash = await eip2612PermitUtils.getPermitTypeHash(tokenAddress)
|
|
253
|
-
|
|
254
|
-
return permitTypeHash === oneInchPermitUtilsConsts.DAI_LIKE_PERMIT_TYPEHASH
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
async function getDaiLikeCallData(params: BaseParams): Promise<string | false> {
|
|
258
|
-
const { eip2612PermitUtils, tokenAddress, walletAddress, spender, nonce, chainId, tokenName, version } = params
|
|
259
|
-
|
|
260
|
-
const permitTypeHash = await eip2612PermitUtils.getPermitTypeHash(tokenAddress)
|
|
261
|
-
|
|
262
|
-
if (permitTypeHash === oneInchPermitUtilsConsts.DAI_LIKE_PERMIT_TYPEHASH) {
|
|
263
|
-
return buildDaiLikePermitCallData({
|
|
264
|
-
eip2612Utils: eip2612PermitUtils,
|
|
265
|
-
callDataParams: [
|
|
266
|
-
{
|
|
267
|
-
...DAI_LIKE_PERMIT_PARAMS,
|
|
268
|
-
holder: walletAddress,
|
|
269
|
-
spender,
|
|
270
|
-
nonce,
|
|
271
|
-
},
|
|
272
|
-
chainId as number,
|
|
273
|
-
tokenName,
|
|
274
|
-
tokenAddress,
|
|
275
|
-
version,
|
|
276
|
-
],
|
|
277
|
-
})
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return false
|
|
281
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { cowAppDataLatestScheme } from '@cowprotocol/cow-sdk'
|
|
2
|
-
import type { JsonRpcProvider } from '@ethersproject/providers'
|
|
3
|
-
|
|
4
|
-
import type { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils'
|
|
5
|
-
|
|
6
|
-
export type PermitType = 'dai-like' | 'eip-2612' | 'unsupported'
|
|
7
|
-
|
|
8
|
-
export type PermitInfo = {
|
|
9
|
-
type: PermitType
|
|
10
|
-
// TODO: make it not optional once token-lists is migrated
|
|
11
|
-
name?: string
|
|
12
|
-
version?: string | undefined // Some tokens have it different than `1`, and won't work without it
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Local TokenInfo definition to not depend on external libs just for this
|
|
16
|
-
type TokenInfo = {
|
|
17
|
-
address: string
|
|
18
|
-
// TODO: remove from token info
|
|
19
|
-
name: string | undefined
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export type PermitHookParams = {
|
|
23
|
-
inputToken: TokenInfo
|
|
24
|
-
spender: string
|
|
25
|
-
chainId: number
|
|
26
|
-
permitInfo: PermitInfo
|
|
27
|
-
provider: JsonRpcProvider
|
|
28
|
-
eip2612Utils: Eip2612PermitUtils
|
|
29
|
-
account?: string
|
|
30
|
-
nonce?: number
|
|
31
|
-
amount?: bigint
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type PermitHookData = cowAppDataLatestScheme.CoWHook
|
|
35
|
-
|
|
36
|
-
type FailedToIdentify = { error: string }
|
|
37
|
-
|
|
38
|
-
export type GetTokenPermitIntoResult =
|
|
39
|
-
// When it's a permittable token:
|
|
40
|
-
| PermitInfo
|
|
41
|
-
// When something failed:
|
|
42
|
-
| FailedToIdentify
|
|
43
|
-
|
|
44
|
-
type BasePermitCallDataParams = {
|
|
45
|
-
eip2612Utils: Eip2612PermitUtils
|
|
46
|
-
}
|
|
47
|
-
export type BuildEip2612PermitCallDataParams = BasePermitCallDataParams & {
|
|
48
|
-
callDataParams: Parameters<Eip2612PermitUtils['buildPermitCallData']>
|
|
49
|
-
}
|
|
50
|
-
export type BuildDaiLikePermitCallDataParams = BasePermitCallDataParams & {
|
|
51
|
-
callDataParams: Parameters<Eip2612PermitUtils['buildDaiLikePermitCallData']>
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type GetTokenPermitInfoParams = {
|
|
55
|
-
spender: string
|
|
56
|
-
tokenAddress: string
|
|
57
|
-
chainId: number
|
|
58
|
-
provider: JsonRpcProvider
|
|
59
|
-
minGasLimit?: number | undefined
|
|
60
|
-
amount?: bigint
|
|
61
|
-
}
|