@lifi/sdk 3.6.0-beta.1 → 3.6.0-beta.3
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/package.json +1 -1
- package/src/_cjs/core/EVM/EVMStepExecutor.js +14 -5
- package/src/_cjs/core/EVM/EVMStepExecutor.js.map +1 -1
- package/src/_cjs/core/EVM/getNativePermit.js +127 -59
- package/src/_cjs/core/EVM/getNativePermit.js.map +1 -1
- package/src/_cjs/core/EVM/signPermitMessage.js +1 -7
- package/src/_cjs/core/EVM/signPermitMessage.js.map +1 -1
- package/src/_cjs/core/EVM/typeguards.js +1 -1
- package/src/_cjs/core/UTXO/getUTXOAPIPublicClient.js +3 -3
- package/src/_cjs/core/UTXO/getUTXOAPIPublicClient.js.map +1 -1
- package/src/_cjs/index.js +4 -2
- package/src/_cjs/index.js.map +1 -1
- package/src/_cjs/version.js +1 -1
- package/src/_esm/core/EVM/EVMStepExecutor.js +14 -5
- package/src/_esm/core/EVM/EVMStepExecutor.js.map +1 -1
- package/src/_esm/core/EVM/getNativePermit.js +147 -60
- package/src/_esm/core/EVM/getNativePermit.js.map +1 -1
- package/src/_esm/core/EVM/signPermitMessage.js +1 -7
- package/src/_esm/core/EVM/signPermitMessage.js.map +1 -1
- package/src/_esm/core/EVM/typeguards.js +1 -1
- package/src/_esm/core/UTXO/getUTXOAPIPublicClient.js +3 -3
- package/src/_esm/core/UTXO/getUTXOAPIPublicClient.js.map +1 -1
- package/src/_esm/index.js +1 -0
- package/src/_esm/index.js.map +1 -1
- package/src/_esm/version.js +1 -1
- package/src/_types/core/EVM/EVMStepExecutor.d.ts.map +1 -1
- package/src/_types/core/EVM/getNativePermit.d.ts +2 -0
- package/src/_types/core/EVM/getNativePermit.d.ts.map +1 -1
- package/src/_types/core/EVM/signPermitMessage.d.ts.map +1 -1
- package/src/_types/index.d.ts +1 -0
- package/src/_types/index.d.ts.map +1 -1
- package/src/_types/version.d.ts +1 -1
- package/src/core/EVM/EVMStepExecutor.ts +15 -6
- package/src/core/EVM/getNativePermit.ts +219 -66
- package/src/core/EVM/signPermitMessage.ts +1 -8
- package/src/core/EVM/typeguards.ts +1 -1
- package/src/core/UTXO/getUTXOAPIPublicClient.ts +3 -3
- package/src/index.ts +1 -0
- package/src/version.ts +1 -1
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { ExtendedChain } from '@lifi/types'
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
encodeAbiParameters,
|
|
4
|
+
keccak256,
|
|
5
|
+
pad,
|
|
6
|
+
parseAbiParameters,
|
|
7
|
+
toBytes,
|
|
8
|
+
toHex,
|
|
9
|
+
} from 'viem'
|
|
10
|
+
import type { Address, Client, Hex } from 'viem'
|
|
11
|
+
import type { TypedDataDomain } from 'viem'
|
|
3
12
|
import { multicall, readContract } from 'viem/actions'
|
|
4
13
|
import { eip2612Abi } from './abi.js'
|
|
5
14
|
import { getMulticallAddress } from './utils.js'
|
|
@@ -9,6 +18,136 @@ export type NativePermitData = {
|
|
|
9
18
|
version: string
|
|
10
19
|
nonce: bigint
|
|
11
20
|
supported: boolean
|
|
21
|
+
domain: TypedDataDomain
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* EIP-712 domain typehash with chainId
|
|
26
|
+
* @link https://eips.ethereum.org/EIPS/eip-712#specification
|
|
27
|
+
*
|
|
28
|
+
* keccak256(toBytes(
|
|
29
|
+
* 'EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'
|
|
30
|
+
* ))
|
|
31
|
+
*/
|
|
32
|
+
const EIP712_DOMAIN_TYPEHASH =
|
|
33
|
+
'0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f' as Hex
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* EIP-712 domain typehash with salt (e.g. USDC.e on Polygon)
|
|
37
|
+
* @link https://eips.ethereum.org/EIPS/eip-712#specification
|
|
38
|
+
*
|
|
39
|
+
* keccak256(toBytes(
|
|
40
|
+
* 'EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)'
|
|
41
|
+
* ))
|
|
42
|
+
*/
|
|
43
|
+
const EIP712_DOMAIN_TYPEHASH_WITH_SALT =
|
|
44
|
+
'0x36c25de3e541d5d970f66e4210d728721220fff5c077cc6cd008b3a0c62adab7' as Hex
|
|
45
|
+
|
|
46
|
+
function makeDomainSeparator({
|
|
47
|
+
name,
|
|
48
|
+
version,
|
|
49
|
+
chainId,
|
|
50
|
+
verifyingContract,
|
|
51
|
+
withSalt = false,
|
|
52
|
+
}: {
|
|
53
|
+
name: string
|
|
54
|
+
version: string
|
|
55
|
+
chainId: bigint
|
|
56
|
+
verifyingContract: Address
|
|
57
|
+
withSalt?: boolean
|
|
58
|
+
}): Hex {
|
|
59
|
+
const nameHash = keccak256(toBytes(name))
|
|
60
|
+
const versionHash = keccak256(toBytes(version))
|
|
61
|
+
|
|
62
|
+
const encoded = withSalt
|
|
63
|
+
? encodeAbiParameters(
|
|
64
|
+
parseAbiParameters('bytes32, bytes32, bytes32, address, bytes32'),
|
|
65
|
+
[
|
|
66
|
+
EIP712_DOMAIN_TYPEHASH_WITH_SALT,
|
|
67
|
+
nameHash,
|
|
68
|
+
versionHash,
|
|
69
|
+
verifyingContract,
|
|
70
|
+
pad(toHex(chainId), { size: 32 }),
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
: encodeAbiParameters(
|
|
74
|
+
parseAbiParameters('bytes32, bytes32, bytes32, uint256, address'),
|
|
75
|
+
[
|
|
76
|
+
EIP712_DOMAIN_TYPEHASH,
|
|
77
|
+
nameHash,
|
|
78
|
+
versionHash,
|
|
79
|
+
chainId,
|
|
80
|
+
verifyingContract,
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return keccak256(encoded)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// TODO: Add support for EIP-5267 when adoption increases
|
|
88
|
+
// This EIP provides a standard way to query domain separator and permit type hash
|
|
89
|
+
// via eip712Domain() function, which would simplify permit validation
|
|
90
|
+
// https://eips.ethereum.org/EIPS/eip-5267
|
|
91
|
+
function validateDomainSeparator({
|
|
92
|
+
name,
|
|
93
|
+
version,
|
|
94
|
+
chainId,
|
|
95
|
+
verifyingContract,
|
|
96
|
+
domainSeparator,
|
|
97
|
+
}: {
|
|
98
|
+
name: string
|
|
99
|
+
version: string
|
|
100
|
+
chainId: bigint
|
|
101
|
+
verifyingContract: Address
|
|
102
|
+
domainSeparator: Hex
|
|
103
|
+
}): { isValid: boolean; domain: TypedDataDomain } {
|
|
104
|
+
if (!name || !domainSeparator) {
|
|
105
|
+
return {
|
|
106
|
+
isValid: false,
|
|
107
|
+
domain: {},
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const withSalt of [false, true]) {
|
|
112
|
+
const computedDS = makeDomainSeparator({
|
|
113
|
+
name,
|
|
114
|
+
version,
|
|
115
|
+
chainId,
|
|
116
|
+
verifyingContract,
|
|
117
|
+
withSalt,
|
|
118
|
+
})
|
|
119
|
+
if (domainSeparator.toLowerCase() === computedDS.toLowerCase()) {
|
|
120
|
+
return {
|
|
121
|
+
isValid: true,
|
|
122
|
+
domain: withSalt
|
|
123
|
+
? {
|
|
124
|
+
name,
|
|
125
|
+
version,
|
|
126
|
+
verifyingContract,
|
|
127
|
+
salt: pad(toHex(chainId), { size: 32 }),
|
|
128
|
+
}
|
|
129
|
+
: {
|
|
130
|
+
name,
|
|
131
|
+
version,
|
|
132
|
+
chainId,
|
|
133
|
+
verifyingContract,
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
isValid: false,
|
|
141
|
+
domain: {},
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const defaultPermit: NativePermitData = {
|
|
146
|
+
name: '',
|
|
147
|
+
version: '1',
|
|
148
|
+
nonce: 0n,
|
|
149
|
+
supported: false,
|
|
150
|
+
domain: {},
|
|
12
151
|
}
|
|
13
152
|
|
|
14
153
|
/**
|
|
@@ -27,88 +166,102 @@ export const getNativePermit = async (
|
|
|
27
166
|
try {
|
|
28
167
|
const multicallAddress = await getMulticallAddress(chain.id)
|
|
29
168
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
await multicall(client, {
|
|
33
|
-
contracts: [
|
|
34
|
-
{
|
|
35
|
-
address: tokenAddress,
|
|
36
|
-
abi: eip2612Abi,
|
|
37
|
-
functionName: 'name',
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
address: tokenAddress,
|
|
41
|
-
abi: eip2612Abi,
|
|
42
|
-
functionName: 'DOMAIN_SEPARATOR',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
address: tokenAddress,
|
|
46
|
-
abi: eip2612Abi,
|
|
47
|
-
functionName: 'nonces',
|
|
48
|
-
args: [client.account!.address],
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
address: tokenAddress,
|
|
52
|
-
abi: eip2612Abi,
|
|
53
|
-
functionName: 'version',
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
multicallAddress,
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const supported =
|
|
60
|
-
nameResult.status === 'success' &&
|
|
61
|
-
domainSeparatorResult.status === 'success' &&
|
|
62
|
-
noncesResult.status === 'success' &&
|
|
63
|
-
!!nameResult.result &&
|
|
64
|
-
!!domainSeparatorResult.result &&
|
|
65
|
-
noncesResult.result !== undefined
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
name: nameResult.result!,
|
|
69
|
-
version: versionResult.result ?? '1',
|
|
70
|
-
nonce: noncesResult.result!,
|
|
71
|
-
supported,
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Fallback to individual calls
|
|
76
|
-
const [name, domainSeparator, nonce, version] = await Promise.all([
|
|
77
|
-
readContract(client, {
|
|
169
|
+
const contractCalls = [
|
|
170
|
+
{
|
|
78
171
|
address: tokenAddress,
|
|
79
172
|
abi: eip2612Abi,
|
|
80
173
|
functionName: 'name',
|
|
81
|
-
}
|
|
82
|
-
|
|
174
|
+
},
|
|
175
|
+
{
|
|
83
176
|
address: tokenAddress,
|
|
84
177
|
abi: eip2612Abi,
|
|
85
178
|
functionName: 'DOMAIN_SEPARATOR',
|
|
86
|
-
}
|
|
87
|
-
|
|
179
|
+
},
|
|
180
|
+
{
|
|
88
181
|
address: tokenAddress,
|
|
89
182
|
abi: eip2612Abi,
|
|
90
183
|
functionName: 'nonces',
|
|
91
184
|
args: [client.account!.address],
|
|
92
|
-
}
|
|
93
|
-
|
|
185
|
+
},
|
|
186
|
+
{
|
|
94
187
|
address: tokenAddress,
|
|
95
188
|
abi: eip2612Abi,
|
|
96
189
|
functionName: 'version',
|
|
97
|
-
}
|
|
98
|
-
]
|
|
190
|
+
},
|
|
191
|
+
] as const
|
|
192
|
+
|
|
193
|
+
if (multicallAddress) {
|
|
194
|
+
const [nameResult, domainSeparatorResult, noncesResult, versionResult] =
|
|
195
|
+
await multicall(client, {
|
|
196
|
+
contracts: contractCalls,
|
|
197
|
+
multicallAddress,
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
if (
|
|
201
|
+
nameResult.status !== 'success' ||
|
|
202
|
+
domainSeparatorResult.status !== 'success' ||
|
|
203
|
+
noncesResult.status !== 'success' ||
|
|
204
|
+
!nameResult.result ||
|
|
205
|
+
!domainSeparatorResult.result ||
|
|
206
|
+
noncesResult.result === undefined
|
|
207
|
+
) {
|
|
208
|
+
return defaultPermit
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const { isValid, domain } = validateDomainSeparator({
|
|
212
|
+
name: nameResult.result,
|
|
213
|
+
version: versionResult.result ?? '1',
|
|
214
|
+
chainId: BigInt(chain.id),
|
|
215
|
+
verifyingContract: tokenAddress,
|
|
216
|
+
domainSeparator: domainSeparatorResult.result,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
name: nameResult.result,
|
|
221
|
+
version: versionResult.result ?? '1',
|
|
222
|
+
nonce: noncesResult.result,
|
|
223
|
+
supported: isValid,
|
|
224
|
+
domain,
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const [nameResult, domainSeparatorResult, noncesResult, versionResult] =
|
|
229
|
+
(await Promise.allSettled(
|
|
230
|
+
contractCalls.map((call) => readContract(client, call))
|
|
231
|
+
)) as [
|
|
232
|
+
PromiseSettledResult<string>,
|
|
233
|
+
PromiseSettledResult<Hex>,
|
|
234
|
+
PromiseSettledResult<bigint>,
|
|
235
|
+
PromiseSettledResult<string>,
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
if (
|
|
239
|
+
nameResult.status !== 'fulfilled' ||
|
|
240
|
+
domainSeparatorResult.status !== 'fulfilled' ||
|
|
241
|
+
noncesResult.status !== 'fulfilled'
|
|
242
|
+
) {
|
|
243
|
+
return defaultPermit
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const name = nameResult.value
|
|
247
|
+
const version =
|
|
248
|
+
versionResult.status === 'fulfilled' ? versionResult.value : '1'
|
|
249
|
+
const { isValid, domain } = validateDomainSeparator({
|
|
250
|
+
name,
|
|
251
|
+
version,
|
|
252
|
+
chainId: BigInt(chain.id),
|
|
253
|
+
verifyingContract: tokenAddress,
|
|
254
|
+
domainSeparator: domainSeparatorResult.value,
|
|
255
|
+
})
|
|
99
256
|
|
|
100
257
|
return {
|
|
101
258
|
name,
|
|
102
|
-
version
|
|
103
|
-
nonce,
|
|
104
|
-
supported:
|
|
259
|
+
version,
|
|
260
|
+
nonce: noncesResult.value,
|
|
261
|
+
supported: isValid,
|
|
262
|
+
domain,
|
|
105
263
|
}
|
|
106
264
|
} catch {
|
|
107
|
-
return
|
|
108
|
-
name: '',
|
|
109
|
-
version: '1',
|
|
110
|
-
nonce: 0n,
|
|
111
|
-
supported: false,
|
|
112
|
-
}
|
|
265
|
+
return defaultPermit
|
|
113
266
|
}
|
|
114
267
|
}
|
|
@@ -28,13 +28,6 @@ export const signNativePermitMessage = async (
|
|
|
28
28
|
): Promise<PermitSignature> => {
|
|
29
29
|
const deadline = BigInt(Math.floor(Date.now() / 1000) + 30 * 60) // 30 minutes
|
|
30
30
|
|
|
31
|
-
const domain = {
|
|
32
|
-
name: nativePermit.name,
|
|
33
|
-
version: nativePermit.version,
|
|
34
|
-
chainId: chain.id,
|
|
35
|
-
verifyingContract: tokenAddress,
|
|
36
|
-
}
|
|
37
|
-
|
|
38
31
|
const message = {
|
|
39
32
|
owner: client.account!.address,
|
|
40
33
|
spender: chain.permit2Proxy as Address,
|
|
@@ -49,7 +42,7 @@ export const signNativePermitMessage = async (
|
|
|
49
42
|
'signTypedData'
|
|
50
43
|
)({
|
|
51
44
|
account: client.account!,
|
|
52
|
-
domain,
|
|
45
|
+
domain: nativePermit.domain,
|
|
53
46
|
types: eip2612Types,
|
|
54
47
|
primaryType: 'Permit',
|
|
55
48
|
message,
|
|
@@ -3,5 +3,5 @@ import type { EVMPermitStep } from './types.js'
|
|
|
3
3
|
|
|
4
4
|
export function isEVMPermitStep(step: LiFiStepExtended): step is EVMPermitStep {
|
|
5
5
|
const evmStep = step as EVMPermitStep
|
|
6
|
-
return 'permit' in evmStep
|
|
6
|
+
return 'permit' in evmStep && 'permitData' in evmStep && 'witness' in evmStep
|
|
7
7
|
}
|
|
@@ -49,15 +49,15 @@ export const getUTXOAPIPublicClient = async (chainId: number) => {
|
|
|
49
49
|
key: 'blockchair',
|
|
50
50
|
includeChainToURL: true,
|
|
51
51
|
}),
|
|
52
|
-
utxo('https://rpc.ankr.com/http/btc_blockbook/api/v2', {
|
|
53
|
-
key: 'ankr',
|
|
54
|
-
}),
|
|
55
52
|
utxo('https://api.blockcypher.com/v1/btc/main', {
|
|
56
53
|
key: 'blockcypher',
|
|
57
54
|
}),
|
|
58
55
|
utxo('https://mempool.space/api', {
|
|
59
56
|
key: 'mempool',
|
|
60
57
|
}),
|
|
58
|
+
utxo('https://rpc.ankr.com/http/btc_blockbook/api/v2', {
|
|
59
|
+
key: 'ankr',
|
|
60
|
+
}),
|
|
61
61
|
]),
|
|
62
62
|
}).extend(UTXOAPIActions)
|
|
63
63
|
publicAPIClients[chainId] = client
|
package/src/index.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/sdk'
|
|
2
|
-
export const version = '3.6.0-beta.
|
|
2
|
+
export const version = '3.6.0-beta.3'
|