From 6f4dc5d97b93551fed7f5165e65746fd5857366a Mon Sep 17 00:00:00 2001 From: Velenir Date: Thu, 28 Nov 2024 18:49:48 +0100 Subject: [PATCH 01/17] constructFillOrderDirectly/allow for takerPermit --- src/methods/limitOrders/fillOrderDirectly.ts | 120 ++++++++++++++++++- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/src/methods/limitOrders/fillOrderDirectly.ts b/src/methods/limitOrders/fillOrderDirectly.ts index 9e3a4e00..8878a8b5 100644 --- a/src/methods/limitOrders/fillOrderDirectly.ts +++ b/src/methods/limitOrders/fillOrderDirectly.ts @@ -9,8 +9,12 @@ export type FillOrderDirectlyFunctions = { }; export type FillOrderDirectly = ( - order: OrderData, - signature: string, + orderFillData: { + order: OrderData; + signature: string; + /** @description Permit1 or DAI Permit by taker for Taker Asset with AugustusRFQ as spender */ + takerPermit?: string; + }, overrides?: TxSendOverrides, signal?: AbortSignal ) => Promise; @@ -77,6 +81,92 @@ const MinAugustusRFQAbi = [ 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; @@ -91,8 +181,7 @@ export function constructFillOrderDirectly( const { getAugustusRFQ } = constructGetSpender(options); const fillOrderDirectly: FillOrderDirectly = async ( - order, - signature, + { order, signature, takerPermit }, overrides = {}, signal ) => { @@ -100,11 +189,30 @@ export function constructFillOrderDirectly( const sanitizedOrder = sanitizeOrderData(order); + if (!takerPermit || takerPermit === '0x') { + const res = await options.contractCaller.transactCall({ + address: AugustusRFQ, + abi: MinAugustusRFQAbi, + contractMethod: 'fillOrder', + args: [sanitizedOrder, signature], + overrides, + }); + + return res; + } + const res = await options.contractCaller.transactCall({ address: AugustusRFQ, abi: MinAugustusRFQAbi, - contractMethod: 'fillOrder', - args: [sanitizedOrder, signature], + contractMethod: 'partialFillOrderWithTargetPermit', + args: [ + sanitizedOrder, // order + signature, // order.signature + order.takerAmount, // takerTokenFillAmount, can even partially fill + order.taker, // target + takerPermit, // encoded TakerAsset.permit() function params by taker with AugustusRFQ as spender, Permit1 and DAI Permit oonly + '0x', // permitMakerAsset, unused because hard to account for changing nonce for long running Orders + ], overrides, }); From fad6cf83e6cdb7ab23028008ee5c4f186707cf86 Mon Sep 17 00:00:00 2001 From: Velenir Date: Thu, 28 Nov 2024 18:50:00 +0100 Subject: [PATCH 02/17] constructApproveTokenForLimitOrder/approveTakerTokenForFillingP2POrderDirectly --- src/methods/limitOrders/approveForOrder.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/methods/limitOrders/approveForOrder.ts b/src/methods/limitOrders/approveForOrder.ts index c80650b0..f1da4ba6 100644 --- a/src/methods/limitOrders/approveForOrder.ts +++ b/src/methods/limitOrders/approveForOrder.ts @@ -6,6 +6,8 @@ import { constructGetSpender } from '../swap/spender'; export type ApproveTokenForLimitOrderFunctions = { /** @description approving AugustusRFQ as spender for makerAsset */ approveMakerTokenForLimitOrder: ApproveToken; + /** @description approving AugustusRFQ as spender for takerAsset to call SDK.fillOrderDirectly */ + approveTakerTokenForFillingP2POrderDirectly: ApproveToken; /** @description approving AugustusSwapper as spender for takerAsset for Limit Orders that will be executed through it */ approveTakerTokenForLimitOrder: ApproveToken; }; @@ -28,6 +30,7 @@ export const constructApproveTokenForLimitOrder = ( return { approveMakerTokenForLimitOrder, + approveTakerTokenForFillingP2POrderDirectly: approveMakerTokenForLimitOrder, approveTakerTokenForLimitOrder, }; }; From 9a41d41b9bcd2697bc7db155a79340d226d2d1de Mon Sep 17 00:00:00 2001 From: Velenir Date: Thu, 28 Nov 2024 18:50:19 +0100 Subject: [PATCH 03/17] test helpers/extends buyErc20TokenForEth --- tests/helpers/index.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/helpers/index.ts b/tests/helpers/index.ts index 66766636..3c1ba27a 100644 --- a/tests/helpers/index.ts +++ b/tests/helpers/index.ts @@ -19,6 +19,8 @@ type BuyErc20TokenForEthInput = { signer: ethers.Wallet; chainId: number; ethersProvider: ethers.providers.JsonRpcProvider; + srcDecimals?: number; + destDecimals?: number; }; type BuyErc20TokenForEthReturn = { @@ -32,6 +34,8 @@ export async function buyErc20TokenForEth({ signer, chainId, ethersProvider, + srcDecimals = 18, + destDecimals = 18, }: BuyErc20TokenForEthInput): Promise { const _paraSwap = constructSimpleSDK( { chainId, ...fetcherOptions }, @@ -39,8 +43,8 @@ export async function buyErc20TokenForEth({ ); const priceRoute = await _paraSwap.swap.getRate({ - srcDecimals: 18, - destDecimals: 18, + srcDecimals, + destDecimals, srcToken: ETH, destToken: tokenAddress, amount, @@ -52,8 +56,8 @@ export async function buyErc20TokenForEth({ const txParams = await _paraSwap.swap.buildTx( { - srcDecimals: 18, - destDecimals: 18, + srcDecimals, + destDecimals, srcToken: ETH, srcAmount, destToken: tokenAddress, From f64ffb292b03757f5ad1bf626a3626ecc4c9b1c5 Mon Sep 17 00:00:00 2001 From: Velenir Date: Thu, 28 Nov 2024 18:51:11 +0100 Subject: [PATCH 04/17] tests/fix comments --- tests/limitOrders.test.ts | 12 ++++++------ tests/nftOrders.test.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index 7b25d611..b832c45d 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -660,7 +660,7 @@ describe('Limit Orders', () => { ); // without SDK - // await WETH_Token.connect(maker).approve(Augustus.address, makerAmount); + // await WETH_Token.connect(maker).approve(AugustusRFQ.address, makerAmount); // withSDK const approveForMakerTx = await makerSDK.approveMakerTokenForLimitOrder( @@ -671,7 +671,7 @@ describe('Limit Orders', () => { await awaitTx(approveForMakerTx); // without SDK - // await BAT_Token.connect(taker).approve(Augustus.address, takerAmount); + // await BAT_Token.connect(taker).approve(AugustusSwapper.address, takerAmount); // withSDK const approveForTakerTx = await takerSDK.approveTakerTokenForLimitOrder( @@ -687,7 +687,7 @@ describe('Limit Orders', () => { signableOrderData.data.nonceAndMeta ); - // taker in nonceAndTaker = Zero + // taker in nonceAndTaker = p2pOrderInput.taker expect(metaAddress.toLowerCase()).toBe(taker.address.toLowerCase()); const { gas: payloadGas, ...LOPayloadTxParams } = @@ -893,7 +893,7 @@ describe('Limit Orders', () => { ); // without SDK - // await WETH_Token.connect(maker).approve(Augustus.address, makerAmount); + // await WETH_Token.connect(maker).approve(AugustusRFQ.address, makerAmount); // withSDK const approveForMakerTx = await makerSDK.approveMakerTokenForLimitOrder( @@ -904,7 +904,7 @@ describe('Limit Orders', () => { await awaitTx(approveForMakerTx); // without SDK - // await DAI_Token.connect(taker).approve(Augustus.address, takerAmount); + // await DAI_Token.connect(taker).approve(AugustusSwapper.address, takerAmount); // withSDK const approveForTakerTx = await takerSDK.approveTakerTokenForLimitOrder( @@ -920,7 +920,7 @@ describe('Limit Orders', () => { signableOrderData.data.nonceAndMeta ); - // taker in nonceAndTaker = Zero + // taker in nonceAndTaker = p2pOrderInput.taker expect(metaAddress.toLowerCase()).toBe(taker.address.toLowerCase()); const priceRoute = await takerSDK.getLimitOrdersRate( diff --git a/tests/nftOrders.test.ts b/tests/nftOrders.test.ts index 4e2459da..d402ef0f 100644 --- a/tests/nftOrders.test.ts +++ b/tests/nftOrders.test.ts @@ -787,7 +787,7 @@ describe('NFT Orders', () => { await COMP_Token.balanceOf(taker.address); // without SDK - // await NFT_Token.connect(maker).approve(Augustus.address, makerAmount); + // await NFT_Token.connect(maker).approve(AugustusRFQ.address, makerAmount); // withSDK const approveForMakerTx = await makerSDK.approveNFTForNFTOrder( @@ -797,7 +797,7 @@ describe('NFT Orders', () => { await awaitTx(approveForMakerTx); // without SDK - // await BAT_Token.connect(taker).approve(Augustus.address, takerAmount); + // await BAT_Token.connect(taker).approve(AugustusSwapper.address, takerAmount); // withSDK const approveForTakerTx = await takerSDK.approveERC20ForNFTOrder( @@ -1012,7 +1012,7 @@ describe('NFT Orders', () => { const AAVE_Token = erc20Token1.attach(AAVE); // without SDK - // await NFT_Token.connect(maker).approve(Augustus.address, makerAmount); + // await NFT_Token.connect(maker).approve(AugustusRFQ.address, makerAmount); // withSDK const approveForMakerTx = await makerSDK.approveNFTForNFTOrder( @@ -1044,9 +1044,6 @@ describe('NFT Orders', () => { [order] ); - // without SDK - // await DAI_Token.connect(taker).approve(Augustus.address, takerAmount); - //more to account for rogue Insufficient Balance errors const buyAmount = new BigNumber(priceRoute.srcAmount) .multipliedBy(2) @@ -1077,6 +1074,9 @@ describe('NFT Orders', () => { const taker3rdTokenInitBalance: BigNumberEthers = await AAVE_Token.balanceOf(taker.address); + // without SDK + // await DAI_Token.connect(taker).approve(AugustusSwapper.address, takerAmount); + // withSDK const approveForTakerTx = await takerSDK.approveERC20ForNFTOrder( buyAmount, From 6849b50c20dab0c7ab24569a77e950f9bfb59aeb Mon Sep 17 00:00:00 2001 From: Velenir Date: Thu, 28 Nov 2024 18:52:11 +0100 Subject: [PATCH 05/17] tests/LO/fill OTC directly --- tests/limitOrders.test.ts | 211 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index b832c45d..f8decbb3 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -36,6 +36,8 @@ import { constructBuildLimitOrderTx, BuildTxFunctions, constructBuildTx, + constructFillOrderDirectly, + FillOrderDirectlyFunctions, } from '../src'; import BigNumber from 'bignumber.js'; @@ -1261,6 +1263,215 @@ describe('Limit Orders', () => { // expect(newOrder).toMatchSnapshot('Order_from_API_Snapshot'); }); + + test(`fill OTC Order directly`, async () => { + // 0.01 WETH + const makerAmount = (0.01e18).toString(10); + // for 6 BAT + const takerAmount = (6e18).toString(10); + + // get some WETH onto maker wallet + const maker = new ethers.Wallet( + walletV5Stable.privateKey, + ethersV5Provider + ); + const { balance: wethBalance } = await buyErc20TokenForEth({ + fetcherOptions: { axios }, + tokenAddress: WETH, + amount: makerAmount, + signer: maker, + providerOptions: { + ethersProviderOrSigner: maker, + EthersContract: ethers.Contract, + account: maker.address, + }, + chainId, + ethersProvider: ethersV5Provider, + }); + + // for some reason BUY WETH may result into greater amount, unlike BUY other ERC20 + expect(new BigNumber(wethBalance).gt(makerAmount)).toBeTruthy(); + + // get some BAT onto the taker wallet + const taker = new ethers.Wallet( + walletV5Stable2.privateKey, + ethersV5Provider + ); + const { balance: batBalance } = await buyErc20TokenForEth({ + fetcherOptions: { axios }, + tokenAddress: BAT, + amount: takerAmount, + signer: taker, + providerOptions: { + ethersProviderOrSigner: taker, + EthersContract: ethers.Contract, + account: taker.address, + }, + chainId, + ethersProvider: ethersV5Provider, + }); + + expect(new BigNumber(batBalance).gte(takerAmount)).toBeTruthy(); + + const makerEthersContractCaller = constructEthersV5ContractCaller( + { + ethersProviderOrSigner: maker, + EthersContract: ethers.Contract, + }, + maker.address + ); + const takerEthersContractCaller = constructEthersV5ContractCaller( + { + ethersProviderOrSigner: taker, + EthersContract: ethers.Contract, + }, + taker.address + ); + + const makerSDK = constructPartialSDK( + { + chainId, + contractCaller: makerEthersContractCaller, + fetcher: axiosFetcher, + apiURL: process.env.API_URL, + version: '6.2', + }, + constructBuildLimitOrder, + constructSignLimitOrder, + constructApproveTokenForLimitOrder + ); + + const takerSDK = constructPartialSDK( + { + chainId, + contractCaller: takerEthersContractCaller, + fetcher: axiosFetcher, + apiURL: process.env.API_URL, + version: '6.2', // direct Order filling is supported on v6 only + }, + constructBuildLimitOrder, + constructSignLimitOrder, + constructApproveTokenForLimitOrder, + constructBuildLimitOrderTx, + constructFillOrderDirectly + ); + + const order = { + nonce: 9991, + expiry: orderExpiry, + maker: maker.address, + makerAsset: WETH, + makerAmount, + takerAsset: BAT, + takerAmount, + taker: taker.address, + }; + + const signableOrderData = await makerSDK.buildLimitOrder(order); + + const signature = await makerSDK.signLimitOrder(signableOrderData); + + const WETH_Token = ERC20MintableFactory.attach(WETH); + const BAT_Token = ERC20MintableFactory.attach(BAT); + + const makerToken1InitBalance: BigNumberEthers = await WETH_Token.balanceOf( + maker.address + ); + const takerToken1InitBalance: BigNumberEthers = await WETH_Token.balanceOf( + taker.address + ); + const makerToken2InitBalance: BigNumberEthers = await BAT_Token.balanceOf( + maker.address + ); + const takerToken2InitBalance: BigNumberEthers = await BAT_Token.balanceOf( + taker.address + ); + + // without SDK + // await WETH_Token.connect(maker).approve(AugustusRFQ.address, makerAmount); + + // withSDK + const approveForMakerTx = await makerSDK.approveMakerTokenForLimitOrder( + makerAmount, + WETH_Token.address + ); + + await awaitTx(approveForMakerTx); + + // without SDK + // await BAT_Token.connect(taker).approve(AugustusRFQ.address, takerAmount); + + // withSDK + const approveForTakerTx = + await takerSDK.approveTakerTokenForFillingP2POrderDirectly( + takerAmount, + BAT_Token.address + ); + await awaitTx(approveForTakerTx); + + const orderWithSignature = { ...signableOrderData.data, signature }; + + // taker address that would be checked as part of nonceAndMeta in Augustus + const metaAddress = deriveTakerFromNonceAndTaker( + signableOrderData.data.nonceAndMeta + ); + + // taker in nonceAndTaker = p2pOrderInput.taker + expect(metaAddress.toLowerCase()).toBe(taker.address.toLowerCase()); + // taker = p2pOrderInput.taker + expect(orderWithSignature.taker.toLowerCase()).toBe( + taker.address.toLowerCase() + ); + + const takerFillsOrderTx = await takerSDK.fillOrderDirectly({ + order: orderWithSignature, + signature: orderWithSignature.signature, + }); + + await awaitTx(takerFillsOrderTx); + + const makerToken1AfterBalance: BigNumberEthers = await WETH_Token.balanceOf( + maker.address + ); + const takerToken1AfterBalance: BigNumberEthers = await WETH_Token.balanceOf( + taker.address + ); + const makerToken2AfterBalance: BigNumberEthers = await BAT_Token.balanceOf( + maker.address + ); + const takerToken2AfterBalance: BigNumberEthers = await BAT_Token.balanceOf( + taker.address + ); + + expect( + new BigNumber(makerToken1AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(makerToken1InitBalance.toString()) + .minus(makerAmount) + .toString(10) + ); + expect( + new BigNumber(takerToken1AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(takerToken1InitBalance.toString()) + .plus(makerAmount) + .toString(10) + ); + expect( + new BigNumber(makerToken2AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(makerToken2InitBalance.toString()) + .plus(takerAmount) + .toString(10) + ); + expect( + new BigNumber(takerToken2AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(takerToken2InitBalance.toString()) + .minus(takerAmount) + .toString(10) + ); + }); }); function calculateOrderHash({ From bea142b4069908a218c6e2c6b4900393a61c46e6 Mon Sep 17 00:00:00 2001 From: Velenir Date: Thu, 28 Nov 2024 18:52:58 +0100 Subject: [PATCH 06/17] tests/LO/more helpers --- tests/limitOrders.test.ts | 188 +++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-) diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index f8decbb3..4bb4badf 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -3,7 +3,7 @@ import Web3 from 'web3'; import type { TransactionReceipt as Web3TransactionReceipt } from 'web3'; import { BigNumber as BigNumberEthers, Contract, ethers } from 'ethersV5'; import { ethers as ethersV6 } from 'ethers'; -import { hexValue, hexZeroPad } from '@ethersproject/bytes'; +import { hexValue, hexZeroPad, splitSignature } from '@ethersproject/bytes'; import axios from 'axios'; import { constructPartialSDK, @@ -1503,3 +1503,189 @@ function deriveTakerFromNonceAndTaker(nonceAndMeta: string): string { 20 ); } + +type SignPermit1Input = { + signer: ethers.providers.JsonRpcSigner | ethers.Wallet; + user: string; + spender: string; + tokenName: string; + tokenAddress: string; + version: string; + chainId: number; + amount: string; + nonce: number; + deadline: number; +}; + +async function signPermit1({ + signer, + user, + spender, + tokenAddress, + tokenName, + version, + chainId, + amount, + nonce, + deadline, +}: SignPermit1Input) { + // set the domain parameters + const domain = { + name: tokenName, + version, + chainId, + verifyingContract: tokenAddress, + }; + + const types = { + Permit: [ + { + name: 'owner', + type: 'address', + }, + { + name: 'spender', + type: 'address', + }, + { + name: 'value', + type: 'uint256', + }, + { + name: 'nonce', + type: 'uint256', + }, + { + name: 'deadline', + type: 'uint256', + }, + ], + }; + + // set the Permit type values + const values = { + owner: user, + spender, + value: amount, + nonce, + deadline, + }; + + // sign the Permit type data with the deployer's private key + const signature = await signer._signTypedData(domain, types, values); + + // split the signature into its components + const sig = ethers.utils.splitSignature(signature); + return signature; +} + +type ExecutePermit1Input = { + permitSignature: string; + provider: ethers.Signer | ethers.providers.Provider; + tokenAddress: string; + user: string; + spender: string; + amount: string; + deadline: number; +}; + +async function executePermit1({ + provider, + permitSignature, + tokenAddress, + user, + spender, + amount, + deadline, +}: ExecutePermit1Input) { + const TokenContract = new ethers.Contract( + tokenAddress, + EIP_2612_PERMIT_ABI, + provider + ); + + const { v, r, s } = splitSignature(permitSignature); + + const tx = await TokenContract.permit( + user, + spender, + amount, + deadline, + v, + r, + s + ); + + return tx; +} + +type EncodeEIP_2612PermitFunctionData = { + permitSignature: string; + user: string; + spender: string; + amount: string; + deadline: number; +}; + +function encodeEIP_2612PermitFunctionData({ + user, + spender, + amount, + deadline, + permitSignature, +}: EncodeEIP_2612PermitFunctionData) { + const { v, r, s } = splitSignature(permitSignature); + + const iface = new ethers.utils.Interface(EIP_2612_PERMIT_ABI); + const contractFunc = iface.getFunction('permit'); + return ethers.utils.defaultAbiCoder.encode(contractFunc.inputs, [ + user, + spender, + amount, + deadline, + v, + r, + s, + ]); +} + +const EIP_2612_PERMIT_ABI = [ + { + constant: false, + inputs: [ + { + name: 'owner', + type: 'address', + }, + { + name: 'spender', + type: 'address', + }, + { + name: 'value', + type: 'uint256', + }, + { + name: 'deadline', + type: 'uint256', + }, + { + name: 'v', + type: 'uint8', + }, + { + name: 'r', + type: 'bytes32', + }, + { + name: 's', + type: 'bytes32', + }, + ], + name: 'permit', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, +]; From 0d3f39e2aad3cbcb8bd90b2f45b4a880bf23eb4e Mon Sep 17 00:00:00 2001 From: Velenir Date: Thu, 28 Nov 2024 18:53:28 +0100 Subject: [PATCH 07/17] tests/LO/fill OTC directly with Permit --- tests/limitOrders.test.ts | 253 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index 4bb4badf..3353e9f4 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -62,6 +62,7 @@ const DAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; const HEX = '0x2b591e99afe9f32eaa6214f7b7629768c40eeb39'; const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; const BAT = '0x0d8775f648430679a709e98d2b0cb6250d2887ef'; +const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; // const DUMMY_ADDRESS_FOR_TESTING_ORDERS = // '0xb9A079479A7b0F4E7F398F7ED3946bE6d9a40E79'; @@ -1472,6 +1473,258 @@ describe('Limit Orders', () => { .toString(10) ); }); + + test.only(`fill OTC Order directly with Taker Permit`, async () => { + // 0.01 WETH + const makerAmount = (0.01e18).toString(10); + // for 60 USDC + const takerAmount = (60e6).toString(10); + + // get some WETH onto maker wallet + const maker = new ethers.Wallet( + walletV5Stable.privateKey, + ethersV5Provider + ); + const { balance: wethBalance } = await buyErc20TokenForEth({ + fetcherOptions: { axios }, + tokenAddress: WETH, + amount: makerAmount, + signer: maker, + providerOptions: { + ethersProviderOrSigner: maker, + EthersContract: ethers.Contract, + account: maker.address, + }, + chainId, + ethersProvider: ethersV5Provider, + }); + + // for some reason BUY WETH may result into greater amount, unlike BUY other ERC20 + expect(new BigNumber(wethBalance).gt(makerAmount)).toBeTruthy(); + + // get some USDC onto the taker wallet + const taker = new ethers.Wallet( + walletV5Stable2.privateKey, + ethersV5Provider + ); + const { balance: usdcBalance } = await buyErc20TokenForEth({ + fetcherOptions: { axios }, + tokenAddress: USDC, + destDecimals: 6, + amount: takerAmount, + signer: taker, + providerOptions: { + ethersProviderOrSigner: taker, + EthersContract: ethers.Contract, + account: taker.address, + }, + chainId, + ethersProvider: ethersV5Provider, + }); + + expect(new BigNumber(usdcBalance).gte(takerAmount)).toBeTruthy(); + + const makerEthersContractCaller = constructEthersV5ContractCaller( + { + ethersProviderOrSigner: maker, + EthersContract: ethers.Contract, + }, + maker.address + ); + const takerEthersContractCaller = constructEthersV5ContractCaller( + { + ethersProviderOrSigner: taker, + EthersContract: ethers.Contract, + }, + taker.address + ); + + const makerSDK = constructPartialSDK( + { + chainId, + contractCaller: makerEthersContractCaller, + fetcher: axiosFetcher, + apiURL: process.env.API_URL, + version: '6.2', + }, + constructBuildLimitOrder, + constructSignLimitOrder, + constructApproveTokenForLimitOrder + ); + + const takerSDK = constructPartialSDK( + { + chainId, + contractCaller: takerEthersContractCaller, + fetcher: axiosFetcher, + apiURL: process.env.API_URL, + version: '6.2', // direct Order filling is supported on v6 only + }, + constructBuildLimitOrder, + constructSignLimitOrder, + constructApproveTokenForLimitOrder, + constructBuildLimitOrderTx, + constructFillOrderDirectly + ); + + const order = { + nonce: 9992, + expiry: orderExpiry, + maker: maker.address, + makerAsset: WETH, + makerAmount, + takerAsset: USDC, + takerAmount, + taker: taker.address, + }; + + const signableOrderData = await makerSDK.buildLimitOrder(order); + + const signature = await makerSDK.signLimitOrder(signableOrderData); + + const WETH_Token = ERC20MintableFactory.attach(WETH); + const USDC_Token = ERC20MintableFactory.attach(USDC); + + const makerToken1InitBalance: BigNumberEthers = await WETH_Token.balanceOf( + maker.address + ); + const takerToken1InitBalance: BigNumberEthers = await WETH_Token.balanceOf( + taker.address + ); + const makerToken2InitBalance: BigNumberEthers = await USDC_Token.balanceOf( + maker.address + ); + const takerToken2InitBalance: BigNumberEthers = await USDC_Token.balanceOf( + taker.address + ); + + // without SDK + // await WETH_Token.connect(maker).approve(AugustusRFQ.address, makerAmount); + + // withSDK + const approveForMakerTx = await makerSDK.approveMakerTokenForLimitOrder( + makerAmount, + WETH_Token.address + ); + + await awaitTx(approveForMakerTx); + + // without SDK + // await BAT_Token.connect(taker).approve(AugustusRFQ.address, takerAmount); + + // withSDK + // const approveForTakerTx = + // await takerSDK.approveTakerTokenForFillingP2POrderDirectly( + // takerAmount, + // USDC_Token.address + // ); + // await awaitTx(approveForTakerTx); + + const orderWithSignature = { ...signableOrderData.data, signature }; + + // taker address that would be checked as part of nonceAndMeta in Augustus + const metaAddress = deriveTakerFromNonceAndTaker( + signableOrderData.data.nonceAndMeta + ); + + // taker in nonceAndTaker = p2pOrderInput.taker + expect(metaAddress.toLowerCase()).toBe(taker.address.toLowerCase()); + // taker = p2pOrderInput.taker + expect(orderWithSignature.taker.toLowerCase()).toBe( + taker.address.toLowerCase() + ); + + const AugustusRFQAddress = await paraSwap.getLimitOrdersContract(); + + const deadline = Math.ceil((Date.now() + 1000 * 60 * 60) / 1000); + + const takerPermitSignature = await signPermit1({ + signer: taker, + user: taker.address, + spender: AugustusRFQAddress, + tokenName: 'USD Coin', + tokenAddress: USDC, + version: '2', + chainId, + amount: takerAmount, + nonce: 0, + deadline, + }); + + console.log('🚀 ~ test.only ~ takerPermitSignature:', takerPermitSignature); + + // const permitTx = await executePermit1({ + // permitSignature: takerPermit, + // tokenAddress: USDC, + // user: taker.address, + // spender: AugustusRFQAddress, + // amount: takerAmount, + // deadline, + // provider: taker, + // }); + + // awaitTx(permitTx); + + const takerPermit = encodeEIP_2612PermitFunctionData({ + user: taker.address, + spender: AugustusRFQAddress, + amount: takerAmount, + deadline, + permitSignature: takerPermitSignature, + }); + + console.log('🚀 ~ test.only ~ takerPermit:', takerPermit); + + const takerFillsOrderTx = await takerSDK.fillOrderDirectly({ + order: orderWithSignature, + signature: orderWithSignature.signature, + takerPermit, + }); + + await awaitTx(takerFillsOrderTx); + + const makerToken1AfterBalance: BigNumberEthers = await WETH_Token.balanceOf( + maker.address + ); + const takerToken1AfterBalance: BigNumberEthers = await WETH_Token.balanceOf( + taker.address + ); + const makerToken2AfterBalance: BigNumberEthers = await USDC_Token.balanceOf( + maker.address + ); + const takerToken2AfterBalance: BigNumberEthers = await USDC_Token.balanceOf( + taker.address + ); + + expect( + new BigNumber(makerToken1AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(makerToken1InitBalance.toString()) + .minus(makerAmount) + .toString(10) + ); + expect( + new BigNumber(takerToken1AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(takerToken1InitBalance.toString()) + .plus(makerAmount) + .toString(10) + ); + expect( + new BigNumber(makerToken2AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(makerToken2InitBalance.toString()) + .plus(takerAmount) + .toString(10) + ); + expect( + new BigNumber(takerToken2AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(takerToken2InitBalance.toString()) + .minus(takerAmount) + .toString(10) + ); + }); }); function calculateOrderHash({ From ea6438bf8ddeb524f9af30c619bcd2266058400d Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 11:19:28 +0100 Subject: [PATCH 08/17] helpers/splitSignature --- src/methods/common/orders/signature.ts | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/methods/common/orders/signature.ts diff --git a/src/methods/common/orders/signature.ts b/src/methods/common/orders/signature.ts new file mode 100644 index 00000000..104246e8 --- /dev/null +++ b/src/methods/common/orders/signature.ts @@ -0,0 +1,68 @@ +type SplitSignatureResult = { + v: number; + r: string; + s: string; + compact: string; +}; + +export function splitSignature(signature: string): SplitSignatureResult { + // Remove "0x" prefix if present + if (signature.startsWith('0x')) { + signature = signature.slice(2); + } + + // Convert the hex string to a byte array + const bytes = new Uint8Array(signature.length / 2); + for (let i = 0; i < signature.length; i += 2) { + bytes[i / 2] = parseInt(signature.slice(i, i + 2), 16); + } + + // Validate the signature length (64 or 65 bytes) + if (bytes.length !== 64 && bytes.length !== 65) { + throw new Error('Invalid signature length: must be 64 or 65 bytes'); + } + + // Extract r and s components + const r = `0x${Array.from(bytes.slice(0, 32), (b) => + b.toString(16).padStart(2, '0') + ).join('')}`; + let s: string; + let v; + + // Handle 64-byte (EIP-2098 compact) and 65-byte signatures + if (bytes.length === 64) { + // Extract v from the highest bit of s and clear the bit in s + v = 27 + (bytes[32]! >> 7); + bytes[32]! &= 0x7f; // Clear the highest bit + s = `0x${Array.from(bytes.slice(32, 64)) + .map((b) => b.toString(16).padStart(2, '0')) + .join('')}`; + } else { + s = `0x${Array.from(bytes.slice(32, 64)) + .map((b) => b.toString(16).padStart(2, '0')) + .join('')}`; + + // Extract v directly for 65-byte signature + v = bytes[64]!; + + // Normalize v to canonical form (27 or 28) + if (v < 27) { + v += 27; + } + } + + // Compute yParityAndS (_vs) for the compact signature + const sBytes = Array.from(bytes.slice(32, 64)); + if (v === 28) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sBytes[0]! |= 0x80; // Set the highest bit if v is 28 + } + const yParityAndS = `0x${sBytes + .map((b) => b.toString(16).padStart(2, '0')) + .join('')}`; + + // Construct the compact signature by concatenating r and yParityAndS + const compactSignature = r + yParityAndS.slice(2); + + return { v, r, s, compact: compactSignature }; +} From 88c2f4397cab8ab1ebf63e2f4319b5665af3ce5d Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 11:22:38 +0100 Subject: [PATCH 09/17] helper/encodeEIP_2612PermitFunctionInput --- src/methods/common/orders/encoding.ts | 110 ++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/methods/common/orders/encoding.ts diff --git a/src/methods/common/orders/encoding.ts b/src/methods/common/orders/encoding.ts new file mode 100644 index 00000000..b6e91534 --- /dev/null +++ b/src/methods/common/orders/encoding.ts @@ -0,0 +1,110 @@ +import { splitSignature } from './signature'; + +type EncodeEIP_2612PermitFunctionInput = { + permitSignature: string; + owner: string; + spender: string; + value: string; + deadline: string | number | bigint; +}; + +// encoding params for Token.permit() Permit1 function +export function encodeEIP_2612PermitFunctionInput({ + owner, + spender, + value, + deadline, + permitSignature, +}: EncodeEIP_2612PermitFunctionInput): string { + const { v, r, s } = splitSignature(permitSignature); + + const encodedOwner = encodeAddress(owner); + const encodedSpender = encodeAddress(spender); + const encodedValue = encodeUint256(value); + const encodedDeadline = encodeUint256(deadline.toString()); + const encodedV = encodeUint8(v); + const encodedR = encodeBytes32(r); + const encodedS = encodeBytes32(s); + + // Concatenate all encoded values, stripping the "0x" prefix from each (except the first one) + return ( + '0x' + + [ + encodedOwner, + encodedSpender, + encodedValue, + encodedDeadline, + encodedV, + encodedR, + encodedS, + ] + .map((val) => val.slice(2)) // Remove "0x" prefix from each encoded value + .join('') // Concatenate the values + ); +} + +// encode an address (20 bytes) into 32 bytes +export function encodeAddress(address: string): string { + const strippedAddress = address.replace(/^0x/, ''); // Remove "0x" prefix + return '0x' + strippedAddress.toLowerCase().padStart(64, '0'); +} + +// encode a uint256 value +export function encodeUint256(value: string | bigint): string { + const bn = BigInt(value); + return '0x' + bn.toString(16).padStart(64, '0'); +} + +// encode a uint8 value +export function encodeUint8(value: number | bigint): string { + return '0x' + value.toString(16).padStart(64, '0'); +} + +// encode a bytes32 value +export function encodeBytes32(value: string): string { + const strippedValue = value.replace(/^0x/, ''); // Remove "0x" prefix + return '0x' + strippedValue.padStart(64, '0').toLowerCase(); +} + +/* +const EIP_2612_PERMIT_ABI = [ + { + constant: false, + inputs: [ + { + name: 'owner', + type: 'address', + }, + { + name: 'spender', + type: 'address', + }, + { + name: 'value', + type: 'uint256', + }, + { + name: 'deadline', + type: 'uint256', + }, + { + name: 'v', + type: 'uint8', + }, + { + name: 'r', + type: 'bytes32', + }, + { + name: 's', + type: 'bytes32', + }, + ], + name: 'permit', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, +]; +*/ From 542f7b17ef446c934be06c378340973a46cde7b7 Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 11:23:26 +0100 Subject: [PATCH 10/17] fillOrderDirectly/handle EIP2612 takerPermit --- src/methods/limitOrders/fillOrderDirectly.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/methods/limitOrders/fillOrderDirectly.ts b/src/methods/limitOrders/fillOrderDirectly.ts index 8878a8b5..ee383460 100644 --- a/src/methods/limitOrders/fillOrderDirectly.ts +++ b/src/methods/limitOrders/fillOrderDirectly.ts @@ -3,6 +3,7 @@ import type { ConstructProviderFetchInput, TxSendOverrides } from '../../types'; import type { OrderData } from './buildOrder'; import { constructGetSpender } from '../swap/spender'; import { sanitizeOrderData } from './helpers/misc'; +import { encodeEIP_2612PermitFunctionInput } from '../common/orders/encoding'; export type FillOrderDirectlyFunctions = { fillOrderDirectly: FillOrderDirectly; @@ -13,7 +14,11 @@ export type FillOrderDirectly = ( order: OrderData; signature: string; /** @description Permit1 or DAI Permit by taker for Taker Asset with AugustusRFQ as spender */ - takerPermit?: string; + takerPermit?: { + signature: string; + deadline: number; + value?: string; + }; }, overrides?: TxSendOverrides, signal?: AbortSignal @@ -189,7 +194,7 @@ export function constructFillOrderDirectly( const sanitizedOrder = sanitizeOrderData(order); - if (!takerPermit || takerPermit === '0x') { + if (!takerPermit || takerPermit.signature === '0x') { const res = await options.contractCaller.transactCall({ address: AugustusRFQ, abi: MinAugustusRFQAbi, @@ -201,6 +206,15 @@ export function constructFillOrderDirectly( return res; } + // encoded TakerAsset.permit() function params by taker with AugustusRFQ as spender, Permit1 and DAI Permit only + const permitTakerAsset = encodeEIP_2612PermitFunctionInput({ + owner: order.taker, + spender: AugustusRFQ, + value: takerPermit.value || 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({ address: AugustusRFQ, abi: MinAugustusRFQAbi, @@ -210,7 +224,7 @@ export function constructFillOrderDirectly( signature, // order.signature order.takerAmount, // takerTokenFillAmount, can even partially fill order.taker, // target - takerPermit, // encoded TakerAsset.permit() function params by taker with AugustusRFQ as spender, Permit1 and DAI Permit oonly + permitTakerAsset, '0x', // permitMakerAsset, unused because hard to account for changing nonce for long running Orders ], overrides, From 8d84278c1e7b9d0ccb22d486ac874ac2900080b7 Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 11:23:54 +0100 Subject: [PATCH 11/17] test/LO/OTC with Permit --- tests/limitOrders.test.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index 3353e9f4..e31fda39 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -40,6 +40,7 @@ import { FillOrderDirectlyFunctions, } from '../src'; import BigNumber from 'bignumber.js'; +import { encodeEIP_2612PermitFunctionInput } from '../src/methods/common/orders/encoding'; import ERC20MinableABI from './abi/ERC20Mintable.json'; import { bytecode as ERC20MintableBytecode } from './bytecode/ERC20Mintable.json'; @@ -1265,7 +1266,7 @@ describe('Limit Orders', () => { // expect(newOrder).toMatchSnapshot('Order_from_API_Snapshot'); }); - test(`fill OTC Order directly`, async () => { + test.only(`fill OTC Order directly`, async () => { // 0.01 WETH const makerAmount = (0.01e18).toString(10); // for 6 BAT @@ -1636,7 +1637,7 @@ describe('Limit Orders', () => { const AugustusRFQAddress = await paraSwap.getLimitOrdersContract(); - const deadline = Math.ceil((Date.now() + 1000 * 60 * 60) / 1000); + const permitDeadline = Math.ceil((Date.now() + 1000 * 60 * 60) / 1000); const takerPermitSignature = await signPermit1({ signer: taker, @@ -1648,11 +1649,9 @@ describe('Limit Orders', () => { chainId, amount: takerAmount, nonce: 0, - deadline, + deadline: permitDeadline, }); - console.log('🚀 ~ test.only ~ takerPermitSignature:', takerPermitSignature); - // const permitTx = await executePermit1({ // permitSignature: takerPermit, // tokenAddress: USDC, @@ -1665,20 +1664,21 @@ describe('Limit Orders', () => { // awaitTx(permitTx); - const takerPermit = encodeEIP_2612PermitFunctionData({ - user: taker.address, - spender: AugustusRFQAddress, - amount: takerAmount, - deadline, - permitSignature: takerPermitSignature, - }); - - console.log('🚀 ~ test.only ~ takerPermit:', takerPermit); + // const takerPermit = encodeEIP_2612PermitFunctionInput({ + // owner: taker.address, + // spender: AugustusRFQAddress, + // value: takerAmount, + // deadline, + // permitSignature: takerPermitSignature, + // }); const takerFillsOrderTx = await takerSDK.fillOrderDirectly({ order: orderWithSignature, signature: orderWithSignature.signature, - takerPermit, + takerPermit: { + signature: takerPermitSignature, + deadline: permitDeadline, + }, }); await awaitTx(takerFillsOrderTx); From e335a311ce3b89f6dd771bc2e57527b22304ee7c Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 15:19:01 +0100 Subject: [PATCH 12/17] helpers/encodeDAIlikePermitFunctionInput --- src/methods/common/orders/encoding.ts | 108 +++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/src/methods/common/orders/encoding.ts b/src/methods/common/orders/encoding.ts index b6e91534..5f0acbea 100644 --- a/src/methods/common/orders/encoding.ts +++ b/src/methods/common/orders/encoding.ts @@ -4,7 +4,7 @@ type EncodeEIP_2612PermitFunctionInput = { permitSignature: string; owner: string; spender: string; - value: string; + value: string | bigint; deadline: string | number | bigint; }; @@ -43,6 +43,50 @@ export function encodeEIP_2612PermitFunctionInput({ ); } +type EncodeDAIlikePermitFunctionInput = { + permitSignature: string; + holder: string; + spender: string; + nonce: number | bigint | string; + expiry: number | bigint | string; +}; + +// encoding params for DAIlike.permit() function +export function encodeDAIlikePermitFunctionInput({ + permitSignature, + holder, + spender, + nonce, + expiry, +}: EncodeDAIlikePermitFunctionInput): string { + const { v, r, s } = splitSignature(permitSignature); + + const encodedHolder = encodeAddress(holder); + const encodedSpender = encodeAddress(spender); + const encodedNonce = encodeUint256(nonce.toString()); + const encodedExpiry = encodeUint256(expiry.toString()); + const encodedV = encodeUint8(v); + const encodedR = encodeBytes32(r); + const encodedS = encodeBytes32(s); + + // Concatenate all encoded values, stripping the "0x" prefix from each (except the first one) + return ( + '0x' + + [ + encodedHolder, + encodedSpender, + encodedNonce, + encodedExpiry, + encodeBool(true), //allowed=true + encodedV, + encodedR, + encodedS, + ] + .map((val) => val.slice(2)) // Remove "0x" prefix from each encoded value + .join('') // Concatenate the values + ); +} + // encode an address (20 bytes) into 32 bytes export function encodeAddress(address: string): string { const strippedAddress = address.replace(/^0x/, ''); // Remove "0x" prefix @@ -66,6 +110,13 @@ export function encodeBytes32(value: string): string { return '0x' + strippedValue.padStart(64, '0').toLowerCase(); } +//encode a boolean +export function encodeBool(value: boolean): string { + const encodedValue = value ? '1' : '0'; + // padded to 32 bytes + return '0x' + encodedValue.padStart(64, '0'); +} + /* const EIP_2612_PERMIT_ABI = [ { @@ -108,3 +159,58 @@ const EIP_2612_PERMIT_ABI = [ }, ]; */ + +/* +const DAI_EIP_2612_PERMIT_ABI = [ + { + constant: false, + inputs: [ + { + internalType: 'address', + name: 'holder', + type: 'address', + }, + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'expiry', + type: 'uint256', + }, + { + internalType: 'bool', + name: 'allowed', + type: 'bool', + }, + { + internalType: 'uint8', + name: 'v', + type: 'uint8', + }, + { + internalType: 'bytes32', + name: 'r', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 's', + type: 'bytes32', + }, + ], + name: 'permit', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, +]; + */ From 899010ae144eaca854b967ad9235e2e3ed53dd82 Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 15:19:59 +0100 Subject: [PATCH 13/17] lOrder/fillOrderDirectly/handle DAI Permit and pre-encoded permit params --- src/methods/limitOrders/fillOrderDirectly.ts | 68 +++++++++++++++----- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/src/methods/limitOrders/fillOrderDirectly.ts b/src/methods/limitOrders/fillOrderDirectly.ts index ee383460..574c7d72 100644 --- a/src/methods/limitOrders/fillOrderDirectly.ts +++ b/src/methods/limitOrders/fillOrderDirectly.ts @@ -3,22 +3,44 @@ import type { ConstructProviderFetchInput, TxSendOverrides } from '../../types'; import type { OrderData } from './buildOrder'; import { constructGetSpender } from '../swap/spender'; import { sanitizeOrderData } from './helpers/misc'; -import { encodeEIP_2612PermitFunctionInput } from '../common/orders/encoding'; +import { + encodeDAIlikePermitFunctionInput, + encodeEIP_2612PermitFunctionInput, +} from '../common/orders/encoding'; export type FillOrderDirectlyFunctions = { fillOrderDirectly: FillOrderDirectly; }; +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 = ( orderFillData: { order: OrderData; signature: string; - /** @description Permit1 or DAI Permit by taker for Taker Asset with AugustusRFQ as spender */ - takerPermit?: { - signature: string; - deadline: number; - value?: 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 @@ -194,7 +216,7 @@ export function constructFillOrderDirectly( const sanitizedOrder = sanitizeOrderData(order); - if (!takerPermit || takerPermit.signature === '0x') { + if (!takerPermit) { const res = await options.contractCaller.transactCall({ address: AugustusRFQ, abi: MinAugustusRFQAbi, @@ -206,14 +228,28 @@ export function constructFillOrderDirectly( return res; } - // encoded TakerAsset.permit() function params by taker with AugustusRFQ as spender, Permit1 and DAI Permit only - const permitTakerAsset = encodeEIP_2612PermitFunctionInput({ - owner: order.taker, - spender: AugustusRFQ, - value: takerPermit.value || order.takerAmount, // can use permit with a bigger value, fallback to exact Order takerAmount - deadline: takerPermit.deadline, - permitSignature: takerPermit.signature, - }); + 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({ address: AugustusRFQ, From 9cb1319252cf44424fe2269358f0c85a57792c12 Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 15:20:33 +0100 Subject: [PATCH 14/17] test/LOrder/fill OTC with DAI Permit --- tests/limitOrders.test.ts | 427 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 405 insertions(+), 22 deletions(-) diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index e31fda39..5b50b4e8 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -59,7 +59,7 @@ jest.setTimeout(30 * 1000); const referrer = 'sdk-test'; -const DAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; +const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f'; const HEX = '0x2b591e99afe9f32eaa6214f7b7629768c40eeb39'; const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; const BAT = '0x0d8775f648430679a709e98d2b0cb6250d2887ef'; @@ -1266,7 +1266,7 @@ describe('Limit Orders', () => { // expect(newOrder).toMatchSnapshot('Order_from_API_Snapshot'); }); - test.only(`fill OTC Order directly`, async () => { + test(`fill OTC Order directly`, async () => { // 0.01 WETH const makerAmount = (0.01e18).toString(10); // for 6 BAT @@ -1475,7 +1475,7 @@ describe('Limit Orders', () => { ); }); - test.only(`fill OTC Order directly with Taker Permit`, async () => { + test(`fill OTC Order directly with Taker Permit1`, async () => { // 0.01 WETH const makerAmount = (0.01e18).toString(10); // for 60 USDC @@ -1610,17 +1610,6 @@ describe('Limit Orders', () => { await awaitTx(approveForMakerTx); - // without SDK - // await BAT_Token.connect(taker).approve(AugustusRFQ.address, takerAmount); - - // withSDK - // const approveForTakerTx = - // await takerSDK.approveTakerTokenForFillingP2POrderDirectly( - // takerAmount, - // USDC_Token.address - // ); - // await awaitTx(approveForTakerTx); - const orderWithSignature = { ...signableOrderData.data, signature }; // taker address that would be checked as part of nonceAndMeta in Augustus @@ -1652,6 +1641,240 @@ describe('Limit Orders', () => { deadline: permitDeadline, }); + const takerFillsOrderTx = await takerSDK.fillOrderDirectly({ + order: orderWithSignature, + signature: orderWithSignature.signature, + takerPermit: { + signature: takerPermitSignature, + deadline: permitDeadline, + }, + }); + + await awaitTx(takerFillsOrderTx); + + const makerToken1AfterBalance: BigNumberEthers = await WETH_Token.balanceOf( + maker.address + ); + const takerToken1AfterBalance: BigNumberEthers = await WETH_Token.balanceOf( + taker.address + ); + const makerToken2AfterBalance: BigNumberEthers = await USDC_Token.balanceOf( + maker.address + ); + const takerToken2AfterBalance: BigNumberEthers = await USDC_Token.balanceOf( + taker.address + ); + + expect( + new BigNumber(makerToken1AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(makerToken1InitBalance.toString()) + .minus(makerAmount) + .toString(10) + ); + expect( + new BigNumber(takerToken1AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(takerToken1InitBalance.toString()) + .plus(makerAmount) + .toString(10) + ); + expect( + new BigNumber(makerToken2AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(makerToken2InitBalance.toString()) + .plus(takerAmount) + .toString(10) + ); + expect( + new BigNumber(takerToken2AfterBalance.toString()).toString(10) + ).toEqual( + new BigNumber(takerToken2InitBalance.toString()) + .minus(takerAmount) + .toString(10) + ); + }); + + test.only(`fill OTC Order directly with Taker DAI Permit`, async () => { + // 0.01 WETH + const makerAmount = (0.01e18).toString(10); + // for 60 DAI + const takerAmount = (60e18).toString(10); + + // get some WETH onto maker wallet + const maker = new ethers.Wallet( + walletV5Stable.privateKey, + ethersV5Provider + ); + const { balance: wethBalance } = await buyErc20TokenForEth({ + fetcherOptions: { axios }, + tokenAddress: WETH, + amount: makerAmount, + signer: maker, + providerOptions: { + ethersProviderOrSigner: maker, + EthersContract: ethers.Contract, + account: maker.address, + }, + chainId, + ethersProvider: ethersV5Provider, + }); + + // for some reason BUY WETH may result into greater amount, unlike BUY other ERC20 + expect(new BigNumber(wethBalance).gt(makerAmount)).toBeTruthy(); + + // get some USDC onto the taker wallet + const taker = new ethers.Wallet( + walletV5Stable2.privateKey, + ethersV5Provider + ); + const { balance: daiBalance } = await buyErc20TokenForEth({ + fetcherOptions: { axios }, + tokenAddress: DAI, + amount: takerAmount, + signer: taker, + providerOptions: { + ethersProviderOrSigner: taker, + EthersContract: ethers.Contract, + account: taker.address, + }, + chainId, + ethersProvider: ethersV5Provider, + }); + + expect(new BigNumber(daiBalance).gte(takerAmount)).toBeTruthy(); + + const makerEthersContractCaller = constructEthersV5ContractCaller( + { + ethersProviderOrSigner: maker, + EthersContract: ethers.Contract, + }, + maker.address + ); + const takerEthersContractCaller = constructEthersV5ContractCaller( + { + ethersProviderOrSigner: taker, + EthersContract: ethers.Contract, + }, + taker.address + ); + + const makerSDK = constructPartialSDK( + { + chainId, + contractCaller: makerEthersContractCaller, + fetcher: axiosFetcher, + apiURL: process.env.API_URL, + version: '6.2', + }, + constructBuildLimitOrder, + constructSignLimitOrder, + constructApproveTokenForLimitOrder + ); + + const takerSDK = constructPartialSDK( + { + chainId, + contractCaller: takerEthersContractCaller, + fetcher: axiosFetcher, + apiURL: process.env.API_URL, + version: '6.2', // direct Order filling is supported on v6 only + }, + constructBuildLimitOrder, + constructSignLimitOrder, + constructApproveTokenForLimitOrder, + constructBuildLimitOrderTx, + constructFillOrderDirectly + ); + + const order = { + nonce: 9993, + expiry: orderExpiry, + maker: maker.address, + makerAsset: WETH, + makerAmount, + takerAsset: DAI, + takerAmount, + taker: taker.address, + }; + + const signableOrderData = await makerSDK.buildLimitOrder(order); + + const signature = await makerSDK.signLimitOrder(signableOrderData); + + const WETH_Token = ERC20MintableFactory.attach(WETH); + const DAI_Token = ERC20MintableFactory.attach(DAI); + + const makerToken1InitBalance: BigNumberEthers = await WETH_Token.balanceOf( + maker.address + ); + const takerToken1InitBalance: BigNumberEthers = await WETH_Token.balanceOf( + taker.address + ); + const makerToken2InitBalance: BigNumberEthers = await DAI_Token.balanceOf( + maker.address + ); + const takerToken2InitBalance: BigNumberEthers = await DAI_Token.balanceOf( + taker.address + ); + + // without SDK + // await WETH_Token.connect(maker).approve(AugustusRFQ.address, makerAmount); + + // withSDK + const approveForMakerTx = await makerSDK.approveMakerTokenForLimitOrder( + makerAmount, + WETH_Token.address + ); + + await awaitTx(approveForMakerTx); + + const orderWithSignature = { ...signableOrderData.data, signature }; + + // taker address that would be checked as part of nonceAndMeta in Augustus + const metaAddress = deriveTakerFromNonceAndTaker( + signableOrderData.data.nonceAndMeta + ); + + // taker in nonceAndTaker = p2pOrderInput.taker + expect(metaAddress.toLowerCase()).toBe(taker.address.toLowerCase()); + // taker = p2pOrderInput.taker + expect(orderWithSignature.taker.toLowerCase()).toBe( + taker.address.toLowerCase() + ); + + const AugustusRFQAddress = await paraSwap.getLimitOrdersContract(); + + const permitExpiry = Math.ceil((Date.now() + 1000 * 60 * 60) / 1000); + + const takerPermitSignature = await signDaiPermit({ + signer: taker, + user: taker.address, + spender: AugustusRFQAddress, + tokenName: 'Dai Stablecoin', + tokenAddress: DAI, + version: '1', + chainId, + amount: takerAmount, + nonce: 0, + expiry: permitExpiry, + }); + + console.log('🚀 ~ test.only ~ takerPermitSignature:', takerPermitSignature); + + // const permitTx = await executeDAIPermit({ + // permitSignature: takerPermitSignature, + // tokenAddress: DAI, + // user: taker.address, + // spender: AugustusRFQAddress, + // expiry: permitExpiry, + // nonce: 0, + // provider: taker, + // }); + + // awaitTx(permitTx); + // console.log('🚀 ~ test.only ~ permitTx:', permitTx.hash); + // const permitTx = await executePermit1({ // permitSignature: takerPermit, // tokenAddress: USDC, @@ -1676,8 +1899,10 @@ describe('Limit Orders', () => { order: orderWithSignature, signature: orderWithSignature.signature, takerPermit: { + isDaiPermit: true, signature: takerPermitSignature, - deadline: permitDeadline, + expiry: permitExpiry, + nonce: 0, }, }); @@ -1689,10 +1914,10 @@ describe('Limit Orders', () => { const takerToken1AfterBalance: BigNumberEthers = await WETH_Token.balanceOf( taker.address ); - const makerToken2AfterBalance: BigNumberEthers = await USDC_Token.balanceOf( + const makerToken2AfterBalance: BigNumberEthers = await DAI_Token.balanceOf( maker.address ); - const takerToken2AfterBalance: BigNumberEthers = await USDC_Token.balanceOf( + const takerToken2AfterBalance: BigNumberEthers = await DAI_Token.balanceOf( taker.address ); @@ -1816,7 +2041,7 @@ async function signPermit1({ }; // set the Permit type values - const values = { + const message = { owner: user, spender, value: amount, @@ -1824,11 +2049,75 @@ async function signPermit1({ deadline, }; - // sign the Permit type data with the deployer's private key - const signature = await signer._signTypedData(domain, types, values); + const signature = await signer._signTypedData(domain, types, message); + + return signature; +} + +type SignDAIPermitInput = { + signer: ethers.providers.JsonRpcSigner | ethers.Wallet; + user: string; + spender: string; + tokenName: string; + tokenAddress: string; + version: string; + chainId: number; + amount: string; + nonce: number; + expiry: number; +}; + +async function signDaiPermit({ + signer, + user, + spender, + tokenName, + tokenAddress, + version, + chainId, + nonce, + expiry, +}: SignDAIPermitInput) { + const domain = { + name: tokenName, + verifyingContract: tokenAddress, + chainId, + version, + }; + const types = { + Permit: [ + { + name: 'holder', + type: 'address', + }, + { + name: 'spender', + type: 'address', + }, + { + name: 'nonce', + type: 'uint256', + }, + { + name: 'expiry', + type: 'uint256', + }, + { + name: 'allowed', + type: 'bool', + }, + ], + }; + + const message = { + holder: user, + spender, + allowed: true, + nonce, + expiry, + }; + const signature = await signer._signTypedData(domain, types, message); - // split the signature into its components - const sig = ethers.utils.splitSignature(signature); return signature; } @@ -1872,6 +2161,47 @@ async function executePermit1({ return tx; } +type ExecuteDAIPermitInput = { + permitSignature: string; + provider: ethers.Signer | ethers.providers.Provider; + tokenAddress: string; + user: string; + spender: string; + nonce: number; + expiry: number; +}; + +async function executeDAIPermit({ + provider, + permitSignature, + tokenAddress, + user, + spender, + nonce, + expiry, +}: ExecuteDAIPermitInput) { + const TokenContract = new ethers.Contract( + tokenAddress, + DAI_EIP_2612_PERMIT_ABI, + provider + ); + + const { v, r, s } = splitSignature(permitSignature); + + const tx = await TokenContract.permit( + user, + spender, + nonce, + expiry, + true, + v, + r, + s + ); + + return tx; +} + type EncodeEIP_2612PermitFunctionData = { permitSignature: string; user: string; @@ -1942,3 +2272,56 @@ const EIP_2612_PERMIT_ABI = [ type: 'function', }, ]; + +const DAI_EIP_2612_PERMIT_ABI = [ + { + constant: false, + inputs: [ + { + internalType: 'address', + name: 'holder', + type: 'address', + }, + { + internalType: 'address', + name: 'spender', + type: 'address', + }, + { + internalType: 'uint256', + name: 'nonce', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'expiry', + type: 'uint256', + }, + { + internalType: 'bool', + name: 'allowed', + type: 'bool', + }, + { + internalType: 'uint8', + name: 'v', + type: 'uint8', + }, + { + internalType: 'bytes32', + name: 'r', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 's', + type: 'bytes32', + }, + ], + name: 'permit', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, +]; From d21b57c911da0658e5d381df1a03960c497b9efe Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 15:23:47 +0100 Subject: [PATCH 15/17] cleanup --- tests/limitOrders.test.ts | 246 +------------------------------------- 1 file changed, 1 insertion(+), 245 deletions(-) diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index 5b50b4e8..c583e8c7 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -3,7 +3,7 @@ import Web3 from 'web3'; import type { TransactionReceipt as Web3TransactionReceipt } from 'web3'; import { BigNumber as BigNumberEthers, Contract, ethers } from 'ethersV5'; import { ethers as ethersV6 } from 'ethers'; -import { hexValue, hexZeroPad, splitSignature } from '@ethersproject/bytes'; +import { hexValue, hexZeroPad } from '@ethersproject/bytes'; import axios from 'axios'; import { constructPartialSDK, @@ -37,10 +37,8 @@ import { BuildTxFunctions, constructBuildTx, constructFillOrderDirectly, - FillOrderDirectlyFunctions, } from '../src'; import BigNumber from 'bignumber.js'; -import { encodeEIP_2612PermitFunctionInput } from '../src/methods/common/orders/encoding'; import ERC20MinableABI from './abi/ERC20Mintable.json'; import { bytecode as ERC20MintableBytecode } from './bytecode/ERC20Mintable.json'; @@ -1860,41 +1858,6 @@ describe('Limit Orders', () => { expiry: permitExpiry, }); - console.log('🚀 ~ test.only ~ takerPermitSignature:', takerPermitSignature); - - // const permitTx = await executeDAIPermit({ - // permitSignature: takerPermitSignature, - // tokenAddress: DAI, - // user: taker.address, - // spender: AugustusRFQAddress, - // expiry: permitExpiry, - // nonce: 0, - // provider: taker, - // }); - - // awaitTx(permitTx); - // console.log('🚀 ~ test.only ~ permitTx:', permitTx.hash); - - // const permitTx = await executePermit1({ - // permitSignature: takerPermit, - // tokenAddress: USDC, - // user: taker.address, - // spender: AugustusRFQAddress, - // amount: takerAmount, - // deadline, - // provider: taker, - // }); - - // awaitTx(permitTx); - - // const takerPermit = encodeEIP_2612PermitFunctionInput({ - // owner: taker.address, - // spender: AugustusRFQAddress, - // value: takerAmount, - // deadline, - // permitSignature: takerPermitSignature, - // }); - const takerFillsOrderTx = await takerSDK.fillOrderDirectly({ order: orderWithSignature, signature: orderWithSignature.signature, @@ -2007,7 +1970,6 @@ async function signPermit1({ nonce, deadline, }: SignPermit1Input) { - // set the domain parameters const domain = { name: tokenName, version, @@ -2040,7 +2002,6 @@ async function signPermit1({ ], }; - // set the Permit type values const message = { owner: user, spender, @@ -2120,208 +2081,3 @@ async function signDaiPermit({ return signature; } - -type ExecutePermit1Input = { - permitSignature: string; - provider: ethers.Signer | ethers.providers.Provider; - tokenAddress: string; - user: string; - spender: string; - amount: string; - deadline: number; -}; - -async function executePermit1({ - provider, - permitSignature, - tokenAddress, - user, - spender, - amount, - deadline, -}: ExecutePermit1Input) { - const TokenContract = new ethers.Contract( - tokenAddress, - EIP_2612_PERMIT_ABI, - provider - ); - - const { v, r, s } = splitSignature(permitSignature); - - const tx = await TokenContract.permit( - user, - spender, - amount, - deadline, - v, - r, - s - ); - - return tx; -} - -type ExecuteDAIPermitInput = { - permitSignature: string; - provider: ethers.Signer | ethers.providers.Provider; - tokenAddress: string; - user: string; - spender: string; - nonce: number; - expiry: number; -}; - -async function executeDAIPermit({ - provider, - permitSignature, - tokenAddress, - user, - spender, - nonce, - expiry, -}: ExecuteDAIPermitInput) { - const TokenContract = new ethers.Contract( - tokenAddress, - DAI_EIP_2612_PERMIT_ABI, - provider - ); - - const { v, r, s } = splitSignature(permitSignature); - - const tx = await TokenContract.permit( - user, - spender, - nonce, - expiry, - true, - v, - r, - s - ); - - return tx; -} - -type EncodeEIP_2612PermitFunctionData = { - permitSignature: string; - user: string; - spender: string; - amount: string; - deadline: number; -}; - -function encodeEIP_2612PermitFunctionData({ - user, - spender, - amount, - deadline, - permitSignature, -}: EncodeEIP_2612PermitFunctionData) { - const { v, r, s } = splitSignature(permitSignature); - - const iface = new ethers.utils.Interface(EIP_2612_PERMIT_ABI); - const contractFunc = iface.getFunction('permit'); - return ethers.utils.defaultAbiCoder.encode(contractFunc.inputs, [ - user, - spender, - amount, - deadline, - v, - r, - s, - ]); -} - -const EIP_2612_PERMIT_ABI = [ - { - constant: false, - inputs: [ - { - name: 'owner', - type: 'address', - }, - { - name: 'spender', - type: 'address', - }, - { - name: 'value', - type: 'uint256', - }, - { - name: 'deadline', - type: 'uint256', - }, - { - name: 'v', - type: 'uint8', - }, - { - name: 'r', - type: 'bytes32', - }, - { - name: 's', - type: 'bytes32', - }, - ], - name: 'permit', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, -]; - -const DAI_EIP_2612_PERMIT_ABI = [ - { - constant: false, - inputs: [ - { - internalType: 'address', - name: 'holder', - type: 'address', - }, - { - internalType: 'address', - name: 'spender', - type: 'address', - }, - { - internalType: 'uint256', - name: 'nonce', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'expiry', - type: 'uint256', - }, - { - internalType: 'bool', - name: 'allowed', - type: 'bool', - }, - { - internalType: 'uint8', - name: 'v', - type: 'uint8', - }, - { - internalType: 'bytes32', - name: 'r', - type: 'bytes32', - }, - { - internalType: 'bytes32', - name: 's', - type: 'bytes32', - }, - ], - name: 'permit', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, -]; From 757c6a71047dc1ebd523b43f05d1a50dd9b93c9a Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 15:27:24 +0100 Subject: [PATCH 16/17] test/LO/reenable tests --- tests/__snapshots__/limitOrders.test.ts.snap | 4 ++-- tests/limitOrders.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/__snapshots__/limitOrders.test.ts.snap b/tests/__snapshots__/limitOrders.test.ts.snap index f04bb025..3a2b6607 100644 --- a/tests/__snapshots__/limitOrders.test.ts.snap +++ b/tests/__snapshots__/limitOrders.test.ts.snap @@ -74,7 +74,7 @@ exports[`Limit Orders buildLimitOrder p2p: P2P_Order_Data_Snapshot 1`] = ` "expiry": 1766188800, "maker": "0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9", "makerAmount": "1000000000000000000", - "makerAsset": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "makerAsset": "0x6b175474e89094c44da98b954eedeac495271d0f", "nonceAndMeta": "2693234308085732315020447542820174961101890254772", "taker": "0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57", "takerAmount": "8000000000000000000", @@ -131,7 +131,7 @@ exports[`Limit Orders buildLimitOrder: Order_Data_Snapshot 1`] = ` "expiry": 1766188800, "maker": "0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9", "makerAmount": "1000000000000000000", - "makerAsset": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "makerAsset": "0x6b175474e89094c44da98b954eedeac495271d0f", "nonceAndMeta": "1461501637330902918203684832716283019655932542976", "taker": "0x0000000000000000000000000000000000000000", "takerAmount": "8000000000000000000", diff --git a/tests/limitOrders.test.ts b/tests/limitOrders.test.ts index c583e8c7..4c702df3 100644 --- a/tests/limitOrders.test.ts +++ b/tests/limitOrders.test.ts @@ -1693,7 +1693,7 @@ describe('Limit Orders', () => { ); }); - test.only(`fill OTC Order directly with Taker DAI Permit`, async () => { + test(`fill OTC Order directly with Taker DAI Permit`, async () => { // 0.01 WETH const makerAmount = (0.01e18).toString(10); // for 60 DAI From 380390a1fdd6b62875b3e400c9255bc2087fed34 Mon Sep 17 00:00:00 2001 From: Velenir Date: Fri, 29 Nov 2024 15:48:56 +0100 Subject: [PATCH 17/17] update snapshots --- tests/nftOrders.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/nftOrders.test.ts b/tests/nftOrders.test.ts index d402ef0f..2b354434 100644 --- a/tests/nftOrders.test.ts +++ b/tests/nftOrders.test.ts @@ -825,12 +825,12 @@ describe('NFT Orders', () => { "expiry": 1766188800, "maker": "0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9", "makerAmount": "1", - "makerAsset": "4315429714524158815340545734471553671933189254538", + "makerAsset": "3020601824397600362935497100210020097557564572013", "makerAssetId": "0", "makerAssetType": 2, "nonce": 999, "nonceAndMeta": "1461271868364326844682297910593670628577722568144820", - "signature": "0x9360585f74eee3de2a12f6dc8c084b3c2646eaf62c3f1c829352bc2eb1e278d2542d36652fe864e3e717f26ed4b6c59554ba1b162ca5ed59a7b5bb32792409281b", + "signature": "0x227877ac7b3e39e602f35072aa1aebfd0dcba165f99cd5f53908312897c5c55b0eeac277396821631993fe3413f7562daabf6c68d26bb532685a2166bacfaabc1b", "taker": "0xDEF171Fe48CF0115B1d80b88dc8eAB59176FEe57", "takerAmount": "6000000000000000000", "takerAsset": "1096451400262405796991039590211805051831004063880",