@openocean.finance/widget-sdk
Version:
OpenOcean Any-to-Any Cross-Chain-Swap SDK
173 lines (150 loc) • 3.93 kB
text/typescript
import {
type Address,
type TypedData,
type TypedDataDomain,
hashTypedData,
} from 'viem'
import { MaxUint48, MaxUint160 } from '../../../constants.js'
import { invariant } from '../../../utils/invariant.js'
import { MaxSigDeadline } from './constants.js'
import { permit2Domain } from './domain.js'
export const MaxAllowanceTransferAmount = MaxUint160
export const MaxAllowanceExpiration = MaxUint48
export const MaxOrderedNonce = MaxUint48
export interface PermitDetails {
token: Address
amount: bigint
expiration: number
nonce: number
}
export interface PermitSingle {
details: PermitDetails
spender: Address
sigDeadline: bigint
}
export interface PermitBatch {
details: PermitDetails[]
spender: Address
sigDeadline: bigint
}
export type PermitSingleData = {
domain: TypedDataDomain
types: TypedData
message: PermitSingle
}
export type PermitBatchData = {
domain: TypedDataDomain
types: TypedData
message: PermitBatch
}
const PERMIT_DETAILS = [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint160' },
{ name: 'expiration', type: 'uint48' },
{ name: 'nonce', type: 'uint48' },
] as const
const PERMIT_TYPES = {
PermitDetails: PERMIT_DETAILS,
PermitSingle: [
{ name: 'details', type: 'PermitDetails' },
{ name: 'spender', type: 'address' },
{ name: 'sigDeadline', type: 'uint256' },
],
} as const
const PERMIT_BATCH_TYPES = {
PermitDetails: PERMIT_DETAILS,
PermitBatch: [
{ name: 'details', type: 'PermitDetails[]' },
{ name: 'spender', type: 'address' },
{ name: 'sigDeadline', type: 'uint256' },
],
} as const
function isPermit(permit: PermitSingle | PermitBatch): permit is PermitSingle {
return !Array.isArray(permit.details)
}
export function getPermitSingleData(
permit: PermitSingle,
permit2Address: Address,
chainId: number
) {
invariant(MaxSigDeadline >= permit.sigDeadline, 'SIG_DEADLINE_OUT_OF_RANGE')
const domain = permit2Domain(permit2Address, chainId)
validatePermitDetails(permit.details)
return {
domain,
message: permit,
}
}
export function getPermitBatchData(
permit: PermitBatch,
permit2Address: Address,
chainId: number
) {
invariant(MaxSigDeadline >= permit.sigDeadline, 'SIG_DEADLINE_OUT_OF_RANGE')
const domain = permit2Domain(permit2Address, chainId)
permit.details.forEach(validatePermitDetails)
return {
domain,
message: permit,
}
}
export function getPermitData(
permit: PermitSingle | PermitBatch,
permit2Address: Address,
chainId: number
): PermitSingleData | PermitBatchData {
invariant(MaxSigDeadline >= permit.sigDeadline, 'SIG_DEADLINE_OUT_OF_RANGE')
const domain = permit2Domain(permit2Address, chainId)
if (isPermit(permit)) {
validatePermitDetails(permit.details)
return {
domain,
types: PERMIT_TYPES,
message: permit,
}
}
permit.details.forEach(validatePermitDetails)
return {
domain,
types: PERMIT_BATCH_TYPES,
message: permit,
}
}
export function hash(
permit: PermitSingle | PermitBatch,
permit2Address: Address,
chainId: number
): string {
if (isPermit(permit)) {
const { domain, message } = getPermitSingleData(
permit,
permit2Address,
chainId
)
return hashTypedData({
domain,
types: PERMIT_TYPES,
primaryType: 'PermitSingle',
message: message,
})
}
const { domain, message } = getPermitBatchData(
permit,
permit2Address,
chainId
)
return hashTypedData({
domain,
types: PERMIT_BATCH_TYPES,
primaryType: 'PermitBatch',
message: message,
})
}
function validatePermitDetails(details: PermitDetails) {
invariant(MaxOrderedNonce >= details.nonce, 'NONCE_OUT_OF_RANGE')
invariant(MaxAllowanceTransferAmount >= details.amount, 'AMOUNT_OUT_OF_RANGE')
invariant(
MaxAllowanceExpiration >= details.expiration,
'EXPIRATION_OUT_OF_RANGE'
)
}