@paraswap/sdk
Version:
274 lines (256 loc) • 7.33 kB
text/typescript
import type { ExtractAbiMethodNames } from '../../helpers/misc';
import type { ConstructProviderFetchInput, TxSendOverrides } from '../../types';
import type { OrderData } from './buildOrder';
import { constructGetSpender } from '../swap/spender';
import { sanitizeOrderData } from './helpers/misc';
import {
encodeDAIlikePermitFunctionInput,
encodeEIP_2612PermitFunctionInput,
} from '../common/orders/encoding';
export type FillOrderDirectlyFunctions<T> = {
fillOrderDirectly: FillOrderDirectly<T>;
};
type TakerPermitEncodedInputParams = {
encodedPermitParams: string;
};
type TakerPermit1Data = {
signature: string;
deadline: number | bigint | string;
amount?: string;
isDaiPermit?: false;
};
type TakerDaiPermitData = {
signature: string;
expiry: number | bigint | string;
nonce: number | bigint | string;
isDaiPermit: true;
};
type TakerPermitObject =
| TakerPermitEncodedInputParams
| TakerPermit1Data
| TakerDaiPermitData;
export type FillOrderDirectly<T> = (
orderFillData: {
order: OrderData;
signature: string;
/** @description Permit1 data or DAI Permit data or Token.parmit() input params encoded; Permit by taker for Taker Asset with AugustusRFQ as spender */
takerPermit?: TakerPermitObject;
},
overrides?: TxSendOverrides,
signal?: AbortSignal
) => Promise<T>;
// much smaller than the whole AugustusRFQ_ABI
const MinAugustusRFQAbi = [
{
inputs: [
{
components: [
{
internalType: 'uint256',
name: 'nonceAndMeta',
type: 'uint256',
},
{
internalType: 'uint128',
name: 'expiry',
type: 'uint128',
},
{
internalType: 'address',
name: 'makerAsset',
type: 'address',
},
{
internalType: 'address',
name: 'takerAsset',
type: 'address',
},
{
internalType: 'address',
name: 'maker',
type: 'address',
},
{
internalType: 'address',
name: 'taker',
type: 'address',
},
{
internalType: 'uint256',
name: 'makerAmount',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'takerAmount',
type: 'uint256',
},
],
internalType: 'struct AugustusRFQ.Order',
name: 'order',
type: 'tuple',
},
{
internalType: 'bytes',
name: 'signature',
type: 'bytes',
},
],
name: 'fillOrder',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
components: [
{
internalType: 'uint256',
name: 'nonceAndMeta',
type: 'uint256',
},
{
internalType: 'uint128',
name: 'expiry',
type: 'uint128',
},
{
internalType: 'address',
name: 'makerAsset',
type: 'address',
},
{
internalType: 'address',
name: 'takerAsset',
type: 'address',
},
{
internalType: 'address',
name: 'maker',
type: 'address',
},
{
internalType: 'address',
name: 'taker',
type: 'address',
},
{
internalType: 'uint256',
name: 'makerAmount',
type: 'uint256',
},
{
internalType: 'uint256',
name: 'takerAmount',
type: 'uint256',
},
],
internalType: 'struct AugustusRFQ.Order',
name: 'order',
type: 'tuple',
},
{
internalType: 'bytes',
name: 'signature',
type: 'bytes',
},
{
internalType: 'uint256',
name: 'takerTokenFillAmount',
type: 'uint256',
},
{
internalType: 'address',
name: 'target',
type: 'address',
},
{
internalType: 'bytes',
name: 'permitTakerAsset',
type: 'bytes',
},
{
internalType: 'bytes',
name: 'permitMakerAsset',
type: 'bytes',
},
],
name: 'partialFillOrderWithTargetPermit',
outputs: [
{
internalType: 'uint256',
name: 'makerTokenFilledAmount',
type: 'uint256',
},
],
stateMutability: 'nonpayable',
type: 'function',
},
] as const;
type FillOrderMethods = ExtractAbiMethodNames<typeof MinAugustusRFQAbi>;
// returns whatever `contractCaller` returns
// to allow for better versatility
export function constructFillOrderDirectly<T>(
options: ConstructProviderFetchInput<T, 'transactCall'>
): FillOrderDirectlyFunctions<T> {
// getAugustusRFQ is cached internally for the same instance of SDK
// so should persist across same apiUrl & network
const { getAugustusRFQ } = constructGetSpender(options);
const fillOrderDirectly: FillOrderDirectly<T> = async (
{ order, signature, takerPermit },
overrides = {},
signal
) => {
const AugustusRFQ = await getAugustusRFQ(signal);
const sanitizedOrder = sanitizeOrderData(order);
if (!takerPermit) {
const res = await options.contractCaller.transactCall<FillOrderMethods>({
address: AugustusRFQ,
abi: MinAugustusRFQAbi,
contractMethod: 'fillOrder',
args: [sanitizedOrder, signature],
overrides,
});
return res;
}
let permitTakerAsset: string;
if ('encodedPermitParams' in takerPermit) {
permitTakerAsset = takerPermit.encodedPermitParams;
} else if ('isDaiPermit' in takerPermit && takerPermit.isDaiPermit) {
// encoded DAI.permit() function params by taker with AugustusRFQ as spender
permitTakerAsset = encodeDAIlikePermitFunctionInput({
holder: order.taker,
spender: AugustusRFQ,
expiry: takerPermit.expiry,
nonce: takerPermit.nonce,
permitSignature: takerPermit.signature,
});
} else {
// encoded TakerAsset.permit() function params by taker with AugustusRFQ as spender, Permit1 only
permitTakerAsset = encodeEIP_2612PermitFunctionInput({
owner: order.taker,
spender: AugustusRFQ,
value: takerPermit.amount || order.takerAmount, // can use permit with a bigger value, fallback to exact Order takerAmount
deadline: takerPermit.deadline,
permitSignature: takerPermit.signature,
});
}
const res = await options.contractCaller.transactCall<FillOrderMethods>({
address: AugustusRFQ,
abi: MinAugustusRFQAbi,
contractMethod: 'partialFillOrderWithTargetPermit',
args: [
sanitizedOrder, // order
signature, // order.signature
order.takerAmount, // takerTokenFillAmount, can even partially fill
order.taker, // target
permitTakerAsset,
'0x', // permitMakerAsset, unused because hard to account for changing nonce for long running Orders
],
overrides,
});
return res;
};
return { fillOrderDirectly };
}