@cowprotocol/permit-utils 0.0.1-RC.0 → 0.0.1
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/README.md +153 -0
- package/index.cjs +1 -1
- package/index.d.ts +2 -1
- package/index.js +268 -197
- package/lib/checkIsCallDataAValidPermit.d.ts +2 -2
- package/package.json +1 -1
- package/types.d.ts +7 -8
- package/utils/PermitProviderConnector.d.ts +0 -1
- package/utils/getContract.d.ts +3 -0
- package/utils/getTokenName.d.ts +2 -0
- package/utils/isSupportedPermitInfo.d.ts +2 -0
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# @cowprotocol/permit-utils
|
|
2
|
+
|
|
3
|
+
Collection of utils for handling token permits.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
yarn add @cowprotocol/permit-utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### `getTokenPermitInfo`
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { getTokenPermitInfo } from "@cowprotocol/permit-utils"
|
|
17
|
+
|
|
18
|
+
const permitInfo = await getTokenPermitInfo({
|
|
19
|
+
spender, tokenAddress, tokenName, chainId, provider
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### `getPermitUtilsInstance`
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { getPermitUtilsInstance } from "@cowprotocol/permit-utils"
|
|
27
|
+
|
|
28
|
+
// Using the a static account defined in the library
|
|
29
|
+
const staticEip2612PermitUtils = getPermitUtilsInstance(chainId, provider)
|
|
30
|
+
|
|
31
|
+
// Using a provided account address
|
|
32
|
+
const accountEip2612PermitUtils = getPermitUtilsInstance(chainId, provider, account)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### `generatePermitHook`
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { generatePermitHook } from "@cowprotocol/permit-utils"
|
|
39
|
+
|
|
40
|
+
const hookData = await generatePermitHook({
|
|
41
|
+
chainId,
|
|
42
|
+
inputToken,
|
|
43
|
+
spender,
|
|
44
|
+
provider,
|
|
45
|
+
permitInfo,
|
|
46
|
+
eip2162Utils,
|
|
47
|
+
account,
|
|
48
|
+
nonce
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### `checkIsCallDataAValidPermit`
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { checkIsCallDataAValidPermit } from "@cowprotocol/permit-utils"
|
|
56
|
+
|
|
57
|
+
const isCallDataAValidPermit = await checkIsCallDataAValidPermit(
|
|
58
|
+
account,
|
|
59
|
+
chainId,
|
|
60
|
+
eip2612Utils,
|
|
61
|
+
tokenAddress,
|
|
62
|
+
tokenName,
|
|
63
|
+
callData,
|
|
64
|
+
permitInfo
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Full flow example**
|
|
69
|
+
|
|
70
|
+
To illustrate, we'll show the flow of placing an order with a permit hook using the utils in this lib.
|
|
71
|
+
|
|
72
|
+
1. Check whether a given token is permittable
|
|
73
|
+
2. If it is, generate a permit hook
|
|
74
|
+
3. Add permit hook to order's appData
|
|
75
|
+
4. Periodically check whether the permit is still valid
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { checkIsCallDataAValidPermit, generatePermitHook, getPermitUtilsInstance, getTokenPermitInfo } from '@cowprotocol/permit-utils'
|
|
79
|
+
import { stringifyDeterministic } from '@cowprotocol/app-data'
|
|
80
|
+
import { OrderBookApi } from '@cowprotocol/cow-sdk'
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
// Check whether token is permittable.
|
|
84
|
+
// No account info is necessary.
|
|
85
|
+
// `spender` could in theory be any address
|
|
86
|
+
// `tokenName` must always match the contract
|
|
87
|
+
const permitInfo = await getTokenPermitInfo({
|
|
88
|
+
spender, tokenAddress, tokenName, chainId, provider
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Not able to tell or not permittable
|
|
92
|
+
if (!permitInfo) {
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Pass in an account address as we'll need the user to sign the actual permit
|
|
97
|
+
const eip2612Utils = getPermitUtilsInstance(chainId, provider, account)
|
|
98
|
+
|
|
99
|
+
// Need to know what the current permit nonce is
|
|
100
|
+
const nonce = await eip2162Utils.getTokenNonce(inputToken.address, account)
|
|
101
|
+
|
|
102
|
+
// Calling this fn should trigger the signature in the user's wallet
|
|
103
|
+
const hookData = await generatePermitHook({
|
|
104
|
+
chainId,
|
|
105
|
+
inputToken,
|
|
106
|
+
spender, // Now `spender` must be the real one
|
|
107
|
+
provider,
|
|
108
|
+
permitInfo,
|
|
109
|
+
eip2612Utils,
|
|
110
|
+
account, // `account` should also be the user's
|
|
111
|
+
nonce
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// Add the hookData to the order's appData
|
|
115
|
+
// See the full reference on https://github.com/cowprotocol/app-data/
|
|
116
|
+
const appData = { version: '0.10.0', metadata: { hooks: { pre: [hookData] } } }
|
|
117
|
+
|
|
118
|
+
// The order expects the stringified JSON doc
|
|
119
|
+
const fullAppData = await stringifyDeterministic(appData)
|
|
120
|
+
|
|
121
|
+
// Build cow-sdk orderBookApi instance
|
|
122
|
+
const orderBookApi = new OrderBookApi()
|
|
123
|
+
|
|
124
|
+
// Note: price quoting is a required step before placing the order but has been left out of this example for brevity
|
|
125
|
+
|
|
126
|
+
// Place order
|
|
127
|
+
const orderId = await orderBookApi.sendOrder(
|
|
128
|
+
{
|
|
129
|
+
...orderParameters,
|
|
130
|
+
quoteId, // Must be fetched before hand, and also include the appData in the params
|
|
131
|
+
appData: fullAppData // <-- The order will be placed with the permit info
|
|
132
|
+
},
|
|
133
|
+
{ chainId }
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
// Now we check whether the permit included in the order is still valid
|
|
137
|
+
// It'll become invalid when another permit is executed
|
|
138
|
+
// This check does not consider whether the `address` has enough allowance, it purelly checks the permit validity.
|
|
139
|
+
|
|
140
|
+
// CallData is part of the hookData
|
|
141
|
+
const callData = hookData.callData
|
|
142
|
+
|
|
143
|
+
// All properties must match what was used when the hookData was generated
|
|
144
|
+
const isCallDataAValidPermit = await checkIsCallDataAValidPermit(
|
|
145
|
+
account,
|
|
146
|
+
chainId,
|
|
147
|
+
eip2612Utils,
|
|
148
|
+
tokenAddress,
|
|
149
|
+
tokenName,
|
|
150
|
+
callData,
|
|
151
|
+
permitInfo
|
|
152
|
+
)
|
|
153
|
+
```
|
package/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("@1inch/permit-signed-approvals-utils"),M=require("@ethersproject/constants"),U=require("@ethersproject/wallet"),w=require("@ethersproject/abi"),H=require("@ethersproject/bignumber"),O=require("@ethersproject/contracts"),F=require("@ethersproject/address");function A(t){return t==="USD//C"?"USD Coin":t}async function q(t,n,e,a,o,r,{version:i,type:c,name:l}){const d=l||o;if(c==="unsupported")return!1;if(!d)throw new Error(`No token name for ${a}`);const m={chainId:n,tokenName:A(d),tokenAddress:a,callData:r,version:i};let u;if(r.startsWith(p.EIP_2612_PERMIT_SELECTOR)?u=e.recoverPermitOwnerFromCallData({...m,callData:r.replace(p.EIP_2612_PERMIT_SELECTOR,"0x")}):r.startsWith(p.DAI_PERMIT_SELECTOR)&&(u=e.recoverDaiLikePermitOwnerFromCallData({...m,callData:r.replace(p.DAI_PERMIT_SELECTOR,"0x")})),!!u)try{return(await u).toLowerCase()===t.toLowerCase()}catch(s){return console.debug(`[checkHasValidPendingPermit] Failed to check permit validity for owner ${t} with callData ${r}`,s),!1}}const x="0xec10458cfaafb32533a4a27e18d9da345758094dde7052521b939a41c55dd1b0",h=new U.Wallet(x),G={1:55e3,100:55e3,5:36e3},I="80000",D=M.MaxUint256.toString(),K=157788e6,V=new Set(["0x6b175474e89094c44da98b954eedeac495271d0f"]);async function S({eip2162Utils:t,callDataParams:n}){const[e,a,o,...r]=n;return(await t.buildPermitCallData(e,a,A(o),...r)).replace("0x",p.EIP_2612_PERMIT_SELECTOR)}async function v({eip2162Utils:t,callDataParams:n}){return(await t.buildDaiLikePermitCallData(...n)).replace("0x",p.DAI_PERMIT_SELECTOR)}function T(){return Math.ceil((Date.now()+K)/1e3)}function L(t){return!!t&&t.type!=="unsupported"}const f={};async function B(t){const n=Q(t),e=f[n];if(e)try{return await e}catch(o){console.debug("[generatePermitHookWith] cached request failed",o),delete f[n]}const a=j(t).then(o=>(delete f[n],o));return f[n]=a,a}async function j(t){const{inputToken:n,spender:e,chainId:a,permitInfo:o,provider:r,account:i,eip2162Utils:c,nonce:l}=t,d=n.address,m=o.name||n.name;if(!L(o))throw new Error(`Trying to generate permit hook for unsupported token: ${d}`);if(!m)throw new Error(`No token name for token: ${d}`);const u=i||h.address,s=l===void 0?await c.getTokenNonce(d,u):l,P=T(),k=D,E=o.type==="eip-2612"?await S({eip2162Utils:c,callDataParams:[{owner:u,spender:e,value:k,nonce:s,deadline:P},a,m,d,o.version]}):await v({eip2162Utils:c,callDataParams:[{holder:u,spender:e,allowed:!0,value:k,nonce:s,expiry:P},a,m,d,o.version]}),N=await W(E,u,d,r,!!i);return{target:d,callData:E,gasLimit:N}}async function W(t,n,e,a,o){try{const r=await a.estimateGas({data:t,from:n,to:e}),i=o?r:r.add(r.div(10));return i.gt(I)?i.toString():I}catch(r){return console.debug("[calculatePermitGasLimit] Failed to estimateGas, using default",r),I}}function Q(t){const{inputToken:n,chainId:e,account:a}=t;return`${n.address.toLowerCase()}-${e}${a?`-${a.toLowerCase()}`:""}`}function R(t,n,e){return new O.Contract(t,n,e)}class Y{constructor(n,e){this.provider=n,this.walletSigner=e}contractEncodeABI(n,e,a,o){return R(e||"",n,this.provider).interface.encodeFunctionData(a,o)}signTypedData(n,e,a){const o=Object.keys(e.types).reduce((i,c)=>(c!=="EIP712Domain"&&(i[c]=e.types[c]),i),{});return(this.walletSigner||this.provider.getSigner())._signTypedData(e.domain,o,e.message)}ethCall(n,e){return this.provider.call({to:n,data:e})}decodeABIParameter(n,e){return w.defaultAbiCoder.decode([n],e)[0]}decodeABIParameters(n,e){const a=w.defaultAbiCoder.decode(n,e);if(a&&typeof a=="object"){const o={};return Object.keys(a).forEach(r=>{const i=a[r];H.BigNumber.isBigNumber(i)?o[r]=i.toHexString():o[r]=i}),o}return a}}const _=new Map,y=new Map;function $(t,n,e){const a=_.get(t);if(!e&&a)return a;const o=`${t}-${e}`,r=y.get(o);if(r)return r;const i=new Y(n,e?void 0:h),c=new p.Eip2612PermitUtils(i);return e?(console.log(`[getPermitUtilsInstance] Set cached provider utils for chain ${t}-${e}`,c),y.set(o,c)):(console.log(`[getPermitUtilsInstance] Set cached chain utils for chain ${t}`,c),_.set(t,c)),c}const z=[{constant:!0,inputs:[],name:"name",outputs:[{name:"",type:"string"}],payable:!1,stateMutability:"view",type:"function"}];async function J(t,n,e){const a=F.getAddress(t);return R(a,z,e).callStatic.name()}const X={value:D,nonce:0,deadline:T()},Z={allowed:!0,nonce:0,expiry:T()},C={},g={type:"unsupported"};async function ee(t){const{tokenAddress:n,chainId:e}=t,a=`${e}-${n.toLowerCase()}`,o=C[a];if(o)return o;const r=te(t);return C[a]=r,r}async function te(t){const{spender:n,tokenAddress:e,tokenName:a,chainId:o,provider:r}=t,i=$(o,r),c=h.address;let l=a;try{l=await J(e,o,r)}catch(s){if(/ETIMEDOUT/.test(s)&&!l)return{error:"Failed to fetch token name from contract. RPC connection error"};console.debug(`[checkTokenIsPermittable] Couldn't fetch token name from the contract for token ${e}, using provided '${l}'`,s)}if(!l)return{error:`Token name could not be determined for ${e}`};let d;try{d=await i.getTokenNonce(e,c)}catch(s){return s==="nonce not supported"||s.message==="nonce is NaN"?(console.debug(`[checkTokenIsPermittable] Not a permittable token ${e} - ${l}`,s?.message||s),{...g,name:l}):(console.debug(`[checkTokenIsPermittable] Failed to get nonce for ${e} - ${l}`,s),{error:s.message||s.toString()})}let m;if(!V.has(e))try{m=await i.getTokenVersion(e)}catch(s){console.debug(`[checkTokenIsPermittable] Failed to get version for ${e} - ${l}`,s)}const u={chainId:o,eip2612PermitUtils:i,nonce:d,spender:n,tokenAddress:e,tokenName:l,walletAddress:c,version:m};try{return await b({...u,type:"eip-2612",provider:r})}catch(s){try{return await re(e,i)?await b({...u,type:"dai-like",provider:r}):/invalid signature/.test(s)||s?.code==="UNPREDICTABLE_GAS_LIMIT"?(console.debug(`[checkTokenIsPermittable] Token ${e} - ${l} might be permittable, but it's not supported for now. Reason:`,s?.reason),{...g,name:l}):(console.debug(`[checkTokenIsPermittable] Failed to estimate eip-2612 permit for ${e} - ${l}`,s),{error:s.message||s.toString()})}catch(P){return console.debug(`[checkTokenIsPermittable] Failed to estimate dai-like permit for ${e} - ${l}`,P),{error:P.message||P.toString()}}}}async function b(t){const{provider:n,chainId:e,walletAddress:a,tokenAddress:o,tokenName:r,type:i,version:c}=t,d=await(i==="eip-2612"?ne:ae)(t);return d?(await n.estimateGas({data:d,from:a,to:o})).toNumber()>G[e]?{type:i,version:c,name:r}:{...g,name:r}:{...g,name:r}}async function ne(t){const{eip2612PermitUtils:n,walletAddress:e,spender:a,nonce:o,chainId:r,tokenName:i,tokenAddress:c,version:l}=t;return S({eip2162Utils:n,callDataParams:[{...X,owner:e,spender:a,nonce:o},+r,i,c,l]})}async function re(t,n){return await n.getPermitTypeHash(t)===p.DAI_LIKE_PERMIT_TYPEHASH}async function ae(t){const{eip2612PermitUtils:n,tokenAddress:e,walletAddress:a,spender:o,nonce:r,chainId:i,tokenName:c,version:l}=t;return await n.getPermitTypeHash(e)===p.DAI_LIKE_PERMIT_TYPEHASH?v({eip2162Utils:n,callDataParams:[{...Z,holder:a,spender:o,nonce:r},i,c,e,l]}):!1}exports.checkIsCallDataAValidPermit=q;exports.generatePermitHook=B;exports.getPermitUtilsInstance=$;exports.getTokenPermitInfo=ee;exports.isSupportedPermitInfo=L;
|
package/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { checkIsCallDataAValidPermit } from './lib/checkIsCallDataAValidPermit';
|
|
|
2
2
|
export { generatePermitHook } from './lib/generatePermitHook';
|
|
3
3
|
export { getPermitUtilsInstance } from './lib/getPermitUtilsInstance';
|
|
4
4
|
export { getTokenPermitInfo } from './lib/getTokenPermitInfo';
|
|
5
|
-
export
|
|
5
|
+
export { isSupportedPermitInfo } from './utils/isSupportedPermitInfo';
|
|
6
|
+
export type { PermitHookData, PermitHookParams, PermitInfo, PermitType, GetTokenPermitIntoResult } from './types';
|
package/index.js
CHANGED
|
@@ -1,311 +1,382 @@
|
|
|
1
|
-
import { EIP_2612_PERMIT_SELECTOR as
|
|
2
|
-
import { MaxUint256 as
|
|
3
|
-
import { Wallet as
|
|
4
|
-
import { defaultAbiCoder as
|
|
5
|
-
import { BigNumber as
|
|
6
|
-
import { Contract as
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { EIP_2612_PERMIT_SELECTOR as h, DAI_PERMIT_SELECTOR as k, Eip2612PermitUtils as U, DAI_LIKE_PERMIT_TYPEHASH as A } from "@1inch/permit-signed-approvals-utils";
|
|
2
|
+
import { MaxUint256 as M } from "@ethersproject/constants";
|
|
3
|
+
import { Wallet as H } from "@ethersproject/wallet";
|
|
4
|
+
import { defaultAbiCoder as y } from "@ethersproject/abi";
|
|
5
|
+
import { BigNumber as F } from "@ethersproject/bignumber";
|
|
6
|
+
import { Contract as O } from "@ethersproject/contracts";
|
|
7
|
+
import { getAddress as x } from "@ethersproject/address";
|
|
8
|
+
function v(t) {
|
|
9
|
+
return t === "USD//C" ? "USD Coin" : t;
|
|
9
10
|
}
|
|
10
|
-
async function
|
|
11
|
-
version: i
|
|
11
|
+
async function ue(t, n, e, o, a, r, {
|
|
12
|
+
version: i,
|
|
13
|
+
type: c,
|
|
14
|
+
name: l
|
|
12
15
|
}) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
const d = l || a;
|
|
17
|
+
if (c === "unsupported")
|
|
18
|
+
return !1;
|
|
19
|
+
if (!d)
|
|
20
|
+
throw new Error(`No token name for ${o}`);
|
|
21
|
+
const u = {
|
|
22
|
+
chainId: n,
|
|
23
|
+
tokenName: v(d),
|
|
24
|
+
tokenAddress: o,
|
|
17
25
|
callData: r,
|
|
18
26
|
version: i
|
|
19
27
|
};
|
|
20
|
-
let
|
|
21
|
-
if (r.startsWith(
|
|
22
|
-
...
|
|
28
|
+
let m;
|
|
29
|
+
if (r.startsWith(h) ? m = e.recoverPermitOwnerFromCallData({
|
|
30
|
+
...u,
|
|
23
31
|
// I don't know why this was removed, ok?
|
|
24
32
|
// We added it back on buildPermitCallData.ts
|
|
25
33
|
// But it looks like this is needed 🤷
|
|
26
34
|
// Check the test for this method https://github.com/1inch/permit-signed-approvals-utils/blob/master/src/eip-2612-permit.test.ts#L85-L106
|
|
27
|
-
callData: r.replace(
|
|
28
|
-
}) : r.startsWith(
|
|
29
|
-
...
|
|
30
|
-
callData: r.replace(
|
|
31
|
-
})), !!
|
|
35
|
+
callData: r.replace(h, "0x")
|
|
36
|
+
}) : r.startsWith(k) && (m = e.recoverDaiLikePermitOwnerFromCallData({
|
|
37
|
+
...u,
|
|
38
|
+
callData: r.replace(k, "0x")
|
|
39
|
+
})), !!m)
|
|
32
40
|
try {
|
|
33
|
-
return (await
|
|
34
|
-
} catch (
|
|
35
|
-
return console.debug(`[checkHasValidPendingPermit] Failed to check permit validity for owner ${
|
|
41
|
+
return (await m).toLowerCase() === t.toLowerCase();
|
|
42
|
+
} catch (s) {
|
|
43
|
+
return console.debug(`[checkHasValidPendingPermit] Failed to check permit validity for owner ${t} with callData ${r}`, s), !1;
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
|
-
const
|
|
46
|
+
const G = "0xec10458cfaafb32533a4a27e18d9da345758094dde7052521b939a41c55dd1b0", w = new H(G), K = {
|
|
39
47
|
1: 55e3,
|
|
40
48
|
100: 55e3,
|
|
41
49
|
5: 36e3
|
|
42
|
-
},
|
|
43
|
-
async function
|
|
44
|
-
eip2162Utils:
|
|
45
|
-
callDataParams:
|
|
50
|
+
}, P = "80000", S = M.toString(), V = 157788e6, B = /* @__PURE__ */ new Set(["0x6b175474e89094c44da98b954eedeac495271d0f"]);
|
|
51
|
+
async function L({
|
|
52
|
+
eip2162Utils: t,
|
|
53
|
+
callDataParams: n
|
|
46
54
|
}) {
|
|
47
|
-
const [e,
|
|
48
|
-
return (await
|
|
55
|
+
const [e, o, a, ...r] = n;
|
|
56
|
+
return (await t.buildPermitCallData(e, o, v(a), ...r)).replace("0x", h);
|
|
49
57
|
}
|
|
50
|
-
async function
|
|
51
|
-
eip2162Utils:
|
|
52
|
-
callDataParams:
|
|
58
|
+
async function $({
|
|
59
|
+
eip2162Utils: t,
|
|
60
|
+
callDataParams: n
|
|
53
61
|
}) {
|
|
54
|
-
return (await
|
|
62
|
+
return (await t.buildDaiLikePermitCallData(...n)).replace("0x", k);
|
|
55
63
|
}
|
|
56
|
-
function
|
|
57
|
-
return Math.ceil((Date.now() +
|
|
64
|
+
function T() {
|
|
65
|
+
return Math.ceil((Date.now() + V) / 1e3);
|
|
66
|
+
}
|
|
67
|
+
function q(t) {
|
|
68
|
+
return !!t && t.type !== "unsupported";
|
|
58
69
|
}
|
|
59
70
|
const f = {};
|
|
60
|
-
async function
|
|
61
|
-
const
|
|
71
|
+
async function pe(t) {
|
|
72
|
+
const n = Q(t), e = f[n];
|
|
62
73
|
if (e)
|
|
63
74
|
try {
|
|
64
75
|
return await e;
|
|
65
|
-
} catch (
|
|
66
|
-
console.debug("[generatePermitHookWith] cached request failed",
|
|
76
|
+
} catch (a) {
|
|
77
|
+
console.debug("[generatePermitHookWith] cached request failed", a), delete f[n];
|
|
67
78
|
}
|
|
68
|
-
const
|
|
69
|
-
return f[
|
|
79
|
+
const o = W(t).then((a) => (delete f[n], a));
|
|
80
|
+
return f[n] = o, o;
|
|
70
81
|
}
|
|
71
|
-
async function
|
|
82
|
+
async function W(t) {
|
|
72
83
|
const {
|
|
73
|
-
inputToken:
|
|
84
|
+
inputToken: n,
|
|
74
85
|
spender: e,
|
|
75
|
-
chainId:
|
|
76
|
-
permitInfo:
|
|
86
|
+
chainId: o,
|
|
87
|
+
permitInfo: a,
|
|
77
88
|
provider: r,
|
|
78
89
|
account: i,
|
|
79
|
-
eip2162Utils:
|
|
80
|
-
nonce:
|
|
81
|
-
} =
|
|
82
|
-
|
|
90
|
+
eip2162Utils: c,
|
|
91
|
+
nonce: l
|
|
92
|
+
} = t, d = n.address, u = a.name || n.name;
|
|
93
|
+
if (!q(a))
|
|
94
|
+
throw new Error(`Trying to generate permit hook for unsupported token: ${d}`);
|
|
95
|
+
if (!u)
|
|
96
|
+
throw new Error(`No token name for token: ${d}`);
|
|
97
|
+
const m = i || w.address, s = l === void 0 ? await c.getTokenNonce(d, m) : l, p = T(), I = S, E = a.type === "eip-2612" ? await L({
|
|
98
|
+
eip2162Utils: c,
|
|
83
99
|
callDataParams: [{
|
|
84
|
-
owner:
|
|
100
|
+
owner: m,
|
|
85
101
|
spender: e,
|
|
86
|
-
value:
|
|
87
|
-
nonce:
|
|
88
|
-
deadline:
|
|
89
|
-
},
|
|
90
|
-
}) : await
|
|
91
|
-
eip2162Utils:
|
|
102
|
+
value: I,
|
|
103
|
+
nonce: s,
|
|
104
|
+
deadline: p
|
|
105
|
+
}, o, u, d, a.version]
|
|
106
|
+
}) : await $({
|
|
107
|
+
eip2162Utils: c,
|
|
92
108
|
callDataParams: [{
|
|
93
|
-
holder:
|
|
109
|
+
holder: m,
|
|
94
110
|
spender: e,
|
|
95
111
|
allowed: !0,
|
|
96
|
-
value:
|
|
97
|
-
nonce:
|
|
98
|
-
expiry:
|
|
99
|
-
},
|
|
100
|
-
}),
|
|
112
|
+
value: I,
|
|
113
|
+
nonce: s,
|
|
114
|
+
expiry: p
|
|
115
|
+
}, o, u, d, a.version]
|
|
116
|
+
}), R = await j(E, m, d, r, !!i);
|
|
101
117
|
return {
|
|
102
118
|
target: d,
|
|
103
|
-
callData:
|
|
104
|
-
gasLimit:
|
|
119
|
+
callData: E,
|
|
120
|
+
gasLimit: R
|
|
105
121
|
};
|
|
106
122
|
}
|
|
107
|
-
async function
|
|
123
|
+
async function j(t, n, e, o, a) {
|
|
108
124
|
try {
|
|
109
|
-
const r = await
|
|
110
|
-
data:
|
|
111
|
-
from:
|
|
125
|
+
const r = await o.estimateGas({
|
|
126
|
+
data: t,
|
|
127
|
+
from: n,
|
|
112
128
|
to: e
|
|
113
|
-
}), i =
|
|
114
|
-
return i.gt(
|
|
129
|
+
}), i = a ? r : r.add(r.div(10));
|
|
130
|
+
return i.gt(P) ? i.toString() : P;
|
|
115
131
|
} catch (r) {
|
|
116
|
-
return console.debug("[calculatePermitGasLimit] Failed to estimateGas, using default", r),
|
|
132
|
+
return console.debug("[calculatePermitGasLimit] Failed to estimateGas, using default", r), P;
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
|
-
function
|
|
135
|
+
function Q(t) {
|
|
120
136
|
const {
|
|
121
|
-
inputToken:
|
|
137
|
+
inputToken: n,
|
|
122
138
|
chainId: e,
|
|
123
|
-
account:
|
|
124
|
-
} =
|
|
125
|
-
return `${
|
|
139
|
+
account: o
|
|
140
|
+
} = t;
|
|
141
|
+
return `${n.address.toLowerCase()}-${e}${o ? `-${o.toLowerCase()}` : ""}`;
|
|
126
142
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
function N(t, n, e) {
|
|
144
|
+
return new O(t, n, e);
|
|
145
|
+
}
|
|
146
|
+
class Y {
|
|
147
|
+
constructor(n, e) {
|
|
148
|
+
this.provider = n, this.walletSigner = e;
|
|
133
149
|
}
|
|
134
|
-
contractEncodeABI(
|
|
135
|
-
return
|
|
150
|
+
contractEncodeABI(n, e, o, a) {
|
|
151
|
+
return N(e || "", n, this.provider).interface.encodeFunctionData(o, a);
|
|
136
152
|
}
|
|
137
|
-
signTypedData(
|
|
138
|
-
const
|
|
139
|
-
return (this.walletSigner || this.provider.getSigner())._signTypedData(e.domain,
|
|
153
|
+
signTypedData(n, e, o) {
|
|
154
|
+
const a = Object.keys(e.types).reduce((i, c) => (c !== "EIP712Domain" && (i[c] = e.types[c]), i), {});
|
|
155
|
+
return (this.walletSigner || this.provider.getSigner())._signTypedData(e.domain, a, e.message);
|
|
140
156
|
}
|
|
141
|
-
ethCall(
|
|
157
|
+
ethCall(n, e) {
|
|
142
158
|
return this.provider.call({
|
|
143
|
-
to:
|
|
159
|
+
to: n,
|
|
144
160
|
data: e
|
|
145
161
|
});
|
|
146
162
|
}
|
|
147
|
-
decodeABIParameter(
|
|
148
|
-
return
|
|
163
|
+
decodeABIParameter(n, e) {
|
|
164
|
+
return y.decode([n], e)[0];
|
|
149
165
|
}
|
|
150
|
-
decodeABIParameters(
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
153
|
-
const
|
|
154
|
-
return Object.keys(
|
|
155
|
-
const i =
|
|
156
|
-
|
|
157
|
-
}),
|
|
166
|
+
decodeABIParameters(n, e) {
|
|
167
|
+
const o = y.decode(n, e);
|
|
168
|
+
if (o && typeof o == "object") {
|
|
169
|
+
const a = {};
|
|
170
|
+
return Object.keys(o).forEach((r) => {
|
|
171
|
+
const i = o[r];
|
|
172
|
+
F.isBigNumber(i) ? a[r] = i.toHexString() : a[r] = i;
|
|
173
|
+
}), a;
|
|
158
174
|
}
|
|
159
|
-
return
|
|
175
|
+
return o;
|
|
160
176
|
}
|
|
161
177
|
}
|
|
162
|
-
const C = /* @__PURE__ */ new Map(),
|
|
163
|
-
function
|
|
164
|
-
const
|
|
165
|
-
if (!e &&
|
|
166
|
-
return
|
|
167
|
-
const
|
|
178
|
+
const C = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Map();
|
|
179
|
+
function z(t, n, e) {
|
|
180
|
+
const o = C.get(t);
|
|
181
|
+
if (!e && o)
|
|
182
|
+
return o;
|
|
183
|
+
const a = `${t}-${e}`, r = b.get(a);
|
|
168
184
|
if (r)
|
|
169
185
|
return r;
|
|
170
|
-
const i = new
|
|
171
|
-
return e ? (console.log(`[getPermitUtilsInstance] Set cached provider utils for chain ${
|
|
186
|
+
const i = new Y(n, e ? void 0 : w), c = new U(i);
|
|
187
|
+
return e ? (console.log(`[getPermitUtilsInstance] Set cached provider utils for chain ${t}-${e}`, c), b.set(a, c)) : (console.log(`[getPermitUtilsInstance] Set cached chain utils for chain ${t}`, c), C.set(t, c)), c;
|
|
172
188
|
}
|
|
173
|
-
const
|
|
174
|
-
|
|
189
|
+
const J = [
|
|
190
|
+
{
|
|
191
|
+
constant: !0,
|
|
192
|
+
inputs: [],
|
|
193
|
+
name: "name",
|
|
194
|
+
outputs: [
|
|
195
|
+
{
|
|
196
|
+
name: "",
|
|
197
|
+
type: "string"
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
payable: !1,
|
|
201
|
+
stateMutability: "view",
|
|
202
|
+
type: "function"
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
async function X(t, n, e) {
|
|
206
|
+
const o = x(t);
|
|
207
|
+
return N(o, J, e).callStatic.name();
|
|
208
|
+
}
|
|
209
|
+
const Z = {
|
|
210
|
+
value: S,
|
|
175
211
|
nonce: 0,
|
|
176
|
-
deadline:
|
|
177
|
-
},
|
|
212
|
+
deadline: T()
|
|
213
|
+
}, ee = {
|
|
178
214
|
allowed: !0,
|
|
179
215
|
nonce: 0,
|
|
180
|
-
expiry:
|
|
181
|
-
},
|
|
182
|
-
|
|
216
|
+
expiry: T()
|
|
217
|
+
}, D = {}, g = {
|
|
218
|
+
type: "unsupported"
|
|
219
|
+
};
|
|
220
|
+
async function fe(t) {
|
|
183
221
|
const {
|
|
184
|
-
tokenAddress:
|
|
222
|
+
tokenAddress: n,
|
|
185
223
|
chainId: e
|
|
186
|
-
} =
|
|
187
|
-
if (
|
|
188
|
-
return
|
|
189
|
-
const r =
|
|
190
|
-
return
|
|
224
|
+
} = t, o = `${e}-${n.toLowerCase()}`, a = D[o];
|
|
225
|
+
if (a)
|
|
226
|
+
return a;
|
|
227
|
+
const r = te(t);
|
|
228
|
+
return D[o] = r, r;
|
|
191
229
|
}
|
|
192
|
-
async function
|
|
230
|
+
async function te(t) {
|
|
193
231
|
const {
|
|
194
|
-
spender:
|
|
232
|
+
spender: n,
|
|
195
233
|
tokenAddress: e,
|
|
196
|
-
tokenName:
|
|
197
|
-
chainId:
|
|
234
|
+
tokenName: o,
|
|
235
|
+
chainId: a,
|
|
198
236
|
provider: r
|
|
199
|
-
} =
|
|
200
|
-
let
|
|
237
|
+
} = t, i = z(a, r), c = w.address;
|
|
238
|
+
let l = o;
|
|
201
239
|
try {
|
|
202
|
-
|
|
203
|
-
} catch (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
240
|
+
l = await X(e, a, r);
|
|
241
|
+
} catch (s) {
|
|
242
|
+
if (/ETIMEDOUT/.test(s) && !l)
|
|
243
|
+
return {
|
|
244
|
+
error: "Failed to fetch token name from contract. RPC connection error"
|
|
245
|
+
};
|
|
246
|
+
console.debug(`[checkTokenIsPermittable] Couldn't fetch token name from the contract for token ${e}, using provided '${l}'`, s);
|
|
207
247
|
}
|
|
248
|
+
if (!l)
|
|
249
|
+
return {
|
|
250
|
+
error: `Token name could not be determined for ${e}`
|
|
251
|
+
};
|
|
208
252
|
let d;
|
|
209
|
-
|
|
253
|
+
try {
|
|
254
|
+
d = await i.getTokenNonce(e, c);
|
|
255
|
+
} catch (s) {
|
|
256
|
+
return s === "nonce not supported" || s.message === "nonce is NaN" ? (console.debug(`[checkTokenIsPermittable] Not a permittable token ${e} - ${l}`, s?.message || s), {
|
|
257
|
+
...g,
|
|
258
|
+
name: l
|
|
259
|
+
}) : (console.debug(`[checkTokenIsPermittable] Failed to get nonce for ${e} - ${l}`, s), {
|
|
260
|
+
error: s.message || s.toString()
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
let u;
|
|
264
|
+
if (!B.has(e))
|
|
210
265
|
try {
|
|
211
|
-
|
|
212
|
-
} catch (
|
|
213
|
-
console.debug(`[checkTokenIsPermittable] Failed to get version for ${e}`,
|
|
266
|
+
u = await i.getTokenVersion(e);
|
|
267
|
+
} catch (s) {
|
|
268
|
+
console.debug(`[checkTokenIsPermittable] Failed to get version for ${e} - ${l}`, s);
|
|
214
269
|
}
|
|
215
270
|
const m = {
|
|
216
|
-
chainId:
|
|
271
|
+
chainId: a,
|
|
217
272
|
eip2612PermitUtils: i,
|
|
218
|
-
nonce:
|
|
219
|
-
spender:
|
|
273
|
+
nonce: d,
|
|
274
|
+
spender: n,
|
|
220
275
|
tokenAddress: e,
|
|
221
|
-
tokenName:
|
|
222
|
-
walletAddress:
|
|
223
|
-
version:
|
|
276
|
+
tokenName: l,
|
|
277
|
+
walletAddress: c,
|
|
278
|
+
version: u
|
|
224
279
|
};
|
|
225
280
|
try {
|
|
226
|
-
return await
|
|
281
|
+
return await _({
|
|
227
282
|
...m,
|
|
228
283
|
type: "eip-2612",
|
|
229
284
|
provider: r
|
|
230
285
|
});
|
|
231
|
-
} catch (
|
|
232
|
-
console.debug(`[checkTokenIsPermittable] Failed to estimate eip-2612 permit for ${e}`, l);
|
|
286
|
+
} catch (s) {
|
|
233
287
|
try {
|
|
234
|
-
return await
|
|
288
|
+
return await re(e, i) ? await _({
|
|
235
289
|
...m,
|
|
236
290
|
type: "dai-like",
|
|
237
291
|
provider: r
|
|
292
|
+
}) : /invalid signature/.test(s) || s?.code === "UNPREDICTABLE_GAS_LIMIT" ? (console.debug(`[checkTokenIsPermittable] Token ${e} - ${l} might be permittable, but it's not supported for now. Reason:`, s?.reason), {
|
|
293
|
+
...g,
|
|
294
|
+
name: l
|
|
295
|
+
}) : (console.debug(`[checkTokenIsPermittable] Failed to estimate eip-2612 permit for ${e} - ${l}`, s), {
|
|
296
|
+
error: s.message || s.toString()
|
|
238
297
|
});
|
|
239
|
-
} catch (
|
|
240
|
-
return console.debug(`[checkTokenIsPermittable] Failed to estimate dai-like permit for ${e}`,
|
|
241
|
-
error:
|
|
298
|
+
} catch (p) {
|
|
299
|
+
return console.debug(`[checkTokenIsPermittable] Failed to estimate dai-like permit for ${e} - ${l}`, p), {
|
|
300
|
+
error: p.message || p.toString()
|
|
242
301
|
};
|
|
243
302
|
}
|
|
244
303
|
}
|
|
245
304
|
}
|
|
246
|
-
async function
|
|
305
|
+
async function _(t) {
|
|
247
306
|
const {
|
|
248
|
-
provider:
|
|
307
|
+
provider: n,
|
|
249
308
|
chainId: e,
|
|
250
|
-
walletAddress:
|
|
251
|
-
tokenAddress:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
309
|
+
walletAddress: o,
|
|
310
|
+
tokenAddress: a,
|
|
311
|
+
tokenName: r,
|
|
312
|
+
type: i,
|
|
313
|
+
version: c
|
|
314
|
+
} = t, d = await (i === "eip-2612" ? ne : oe)(t);
|
|
315
|
+
return d ? (await n.estimateGas({
|
|
316
|
+
data: d,
|
|
317
|
+
from: o,
|
|
318
|
+
to: a
|
|
319
|
+
})).toNumber() > K[e] ? {
|
|
320
|
+
type: i,
|
|
321
|
+
version: c,
|
|
322
|
+
name: r
|
|
323
|
+
} : {
|
|
324
|
+
...g,
|
|
325
|
+
name: r
|
|
326
|
+
} : {
|
|
327
|
+
...g,
|
|
328
|
+
name: r
|
|
329
|
+
};
|
|
263
330
|
}
|
|
264
|
-
async function
|
|
331
|
+
async function ne(t) {
|
|
265
332
|
const {
|
|
266
|
-
eip2612PermitUtils:
|
|
333
|
+
eip2612PermitUtils: n,
|
|
267
334
|
walletAddress: e,
|
|
268
|
-
spender:
|
|
269
|
-
nonce:
|
|
335
|
+
spender: o,
|
|
336
|
+
nonce: a,
|
|
270
337
|
chainId: r,
|
|
271
338
|
tokenName: i,
|
|
272
|
-
tokenAddress:
|
|
273
|
-
version:
|
|
274
|
-
} =
|
|
275
|
-
return
|
|
276
|
-
eip2162Utils:
|
|
339
|
+
tokenAddress: c,
|
|
340
|
+
version: l
|
|
341
|
+
} = t;
|
|
342
|
+
return L({
|
|
343
|
+
eip2162Utils: n,
|
|
277
344
|
callDataParams: [{
|
|
278
|
-
...
|
|
345
|
+
...Z,
|
|
279
346
|
owner: e,
|
|
280
|
-
spender:
|
|
281
|
-
nonce:
|
|
282
|
-
}, +r, i,
|
|
347
|
+
spender: o,
|
|
348
|
+
nonce: a
|
|
349
|
+
}, +r, i, c, l]
|
|
283
350
|
});
|
|
284
351
|
}
|
|
285
|
-
async function
|
|
352
|
+
async function re(t, n) {
|
|
353
|
+
return await n.getPermitTypeHash(t) === A;
|
|
354
|
+
}
|
|
355
|
+
async function oe(t) {
|
|
286
356
|
const {
|
|
287
|
-
eip2612PermitUtils:
|
|
357
|
+
eip2612PermitUtils: n,
|
|
288
358
|
tokenAddress: e,
|
|
289
|
-
walletAddress:
|
|
290
|
-
spender:
|
|
359
|
+
walletAddress: o,
|
|
360
|
+
spender: a,
|
|
291
361
|
nonce: r,
|
|
292
362
|
chainId: i,
|
|
293
|
-
tokenName:
|
|
294
|
-
version:
|
|
295
|
-
} =
|
|
296
|
-
return await
|
|
297
|
-
eip2162Utils:
|
|
363
|
+
tokenName: c,
|
|
364
|
+
version: l
|
|
365
|
+
} = t;
|
|
366
|
+
return await n.getPermitTypeHash(e) === A ? $({
|
|
367
|
+
eip2162Utils: n,
|
|
298
368
|
callDataParams: [{
|
|
299
|
-
...
|
|
300
|
-
holder:
|
|
301
|
-
spender:
|
|
369
|
+
...ee,
|
|
370
|
+
holder: o,
|
|
371
|
+
spender: a,
|
|
302
372
|
nonce: r
|
|
303
|
-
}, i,
|
|
373
|
+
}, i, c, e, l]
|
|
304
374
|
}) : !1;
|
|
305
375
|
}
|
|
306
376
|
export {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
377
|
+
ue as checkIsCallDataAValidPermit,
|
|
378
|
+
pe as generatePermitHook,
|
|
379
|
+
z as getPermitUtilsInstance,
|
|
380
|
+
fe as getTokenPermitInfo,
|
|
381
|
+
q as isSupportedPermitInfo
|
|
311
382
|
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils';
|
|
2
|
-
import {
|
|
3
|
-
export declare function checkIsCallDataAValidPermit(owner: string, chainId: number, eip2162Utils: Eip2612PermitUtils, tokenAddress: string,
|
|
2
|
+
import { PermitInfo } from '../types';
|
|
3
|
+
export declare function checkIsCallDataAValidPermit(owner: string, chainId: number, eip2162Utils: Eip2612PermitUtils, tokenAddress: string, _tokenName: string | undefined, callData: string, { version, type, name }: PermitInfo): Promise<boolean | undefined>;
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Eip2612PermitUtils } from '@1inch/permit-signed-approvals-utils';
|
|
2
2
|
import { latest } from '@cowprotocol/app-data';
|
|
3
3
|
import { JsonRpcProvider } from '@ethersproject/providers';
|
|
4
|
-
export type PermitType = 'dai-like' | 'eip-2612';
|
|
5
|
-
export type
|
|
4
|
+
export type PermitType = 'dai-like' | 'eip-2612' | 'unsupported';
|
|
5
|
+
export type PermitInfo = {
|
|
6
6
|
type: PermitType;
|
|
7
|
-
|
|
7
|
+
name?: string;
|
|
8
|
+
version?: string | undefined;
|
|
8
9
|
};
|
|
9
|
-
type UnsupportedPermitInfo = false;
|
|
10
|
-
export type PermitInfo = SupportedPermitInfo | UnsupportedPermitInfo;
|
|
11
10
|
type TokenInfo = {
|
|
12
11
|
address: string;
|
|
13
12
|
name: string | undefined;
|
|
@@ -16,7 +15,7 @@ export type PermitHookParams = {
|
|
|
16
15
|
inputToken: TokenInfo;
|
|
17
16
|
spender: string;
|
|
18
17
|
chainId: number;
|
|
19
|
-
permitInfo:
|
|
18
|
+
permitInfo: PermitInfo;
|
|
20
19
|
provider: JsonRpcProvider;
|
|
21
20
|
eip2162Utils: Eip2612PermitUtils;
|
|
22
21
|
account?: string | undefined;
|
|
@@ -26,7 +25,7 @@ export type PermitHookData = latest.CoWHook;
|
|
|
26
25
|
type FailedToIdentify = {
|
|
27
26
|
error: string;
|
|
28
27
|
};
|
|
29
|
-
export type GetTokenPermitIntoResult =
|
|
28
|
+
export type GetTokenPermitIntoResult = PermitInfo | FailedToIdentify;
|
|
30
29
|
type BasePermitCallDataParams = {
|
|
31
30
|
eip2162Utils: Eip2612PermitUtils;
|
|
32
31
|
};
|
|
@@ -39,7 +38,7 @@ export type BuildDaiLikePermitCallDataParams = BasePermitCallDataParams & {
|
|
|
39
38
|
export type GetTokenPermitInfoParams = {
|
|
40
39
|
spender: string;
|
|
41
40
|
tokenAddress: string;
|
|
42
|
-
tokenName
|
|
41
|
+
tokenName?: string | undefined;
|
|
43
42
|
chainId: number;
|
|
44
43
|
provider: JsonRpcProvider;
|
|
45
44
|
};
|
|
@@ -5,7 +5,6 @@ export declare class PermitProviderConnector implements ProviderConnector {
|
|
|
5
5
|
private provider;
|
|
6
6
|
private walletSigner?;
|
|
7
7
|
constructor(provider: JsonRpcProvider, walletSigner?: Wallet | undefined);
|
|
8
|
-
private getContract;
|
|
9
8
|
contractEncodeABI(abi: AbiItem[], address: string | null, methodName: string, methodParams: unknown[]): string;
|
|
10
9
|
signTypedData(_walletAddress: string, typedData: EIP712TypedData, _typedDataHash: string): Promise<string>;
|
|
11
10
|
ethCall(contractAddress: string, callData: string): Promise<string>;
|