diff --git a/src/index.ts b/src/index.ts index 6ee52ce..bf56745 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,6 +36,9 @@ import { run as getAcrossBridgingId } from "./scripts/bridging/getAcrossBridging import { run as getEthFlowId } from "./scripts/ethflow/getEthFlowId"; import { run as minimalAppData } from "./scripts/app-data/minimalAppData"; import { run as collateralSwapAave } from "./scripts/flash-loans/collateralSwapAave"; +import { run as collateralSwapAavePoC } from "./scripts/flash-loans/collateralSwapAavePoC"; +import { run as repayDebtWithCollateralAavePoC } from "./scripts/flash-loans/repayDebtWithCollateralAavePoC"; +import { run as debtSwapAavePoC } from "./scripts/flash-loans/debtSwapAavePoC"; dotenv.config(); @@ -85,7 +88,9 @@ const JOBS: (() => Promise)[] = [ // getAcrossBridgingId, // getEthFlowId, // minimalAppData, - collateralSwapAave, + // collateralSwapAavePoC, + //repayDebtWithCollateralAavePoC, + debtSwapAavePoC, ]; async function main() { diff --git a/src/scripts/flash-loans/abi/AaveAdapterFactory.ts b/src/scripts/flash-loans/abi/AaveAdapterFactory.ts new file mode 100644 index 0000000..44c4e84 --- /dev/null +++ b/src/scripts/flash-loans/abi/AaveAdapterFactory.ts @@ -0,0 +1,626 @@ +export const aaveAdapterFactoryAbi = [ + { + type: "constructor", + inputs: [ + { + name: "aavePool_", + type: "address", + internalType: "address", + }, + { + name: "settlement_", + type: "address", + internalType: "address", + }, + { + name: "router_", + type: "address", + internalType: "address", + }, + { + name: "owner_", + type: "address", + internalType: "address", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "ADDRESSES_PROVIDER", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IPoolAddressesProvider", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "POOL", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IPool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "ROUTER", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IFlashLoanRouter", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "SETTLEMENT_CONTRACT", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract ICowSettlement", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "deployAndTransferFlashLoan", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "adapterImplementation", + type: "address", + internalType: "address", + }, + { + name: "hookAmounts", + type: "tuple", + internalType: "struct DataTypes.HookAmounts", + components: [ + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "sellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + name: "order", + type: "tuple", + internalType: "struct GPv2Order.Data", + components: [ + { + name: "sellToken", + type: "address", + internalType: "contract IERC20", + }, + { + name: "buyToken", + type: "address", + internalType: "contract IERC20", + }, + { + name: "receiver", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "validTo", + type: "uint32", + internalType: "uint32", + }, + { + name: "appData", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "feeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "partiallyFillable", + type: "bool", + internalType: "bool", + }, + { + name: "sellTokenBalance", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "buyTokenBalance", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "executeOperation", + inputs: [ + { + name: "", + type: "address[]", + internalType: "address[]", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "uint256[]", + internalType: "uint256[]", + }, + { + name: "", + type: "address", + internalType: "address", + }, + { + name: "callBackData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "flashLoanAndCallBack", + inputs: [ + { + name: "lender", + type: "address", + internalType: "address", + }, + { + name: "token", + type: "address", + internalType: "contract IERC20", + }, + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + { + name: "callBackData", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "getInstanceDeterministicAddress", + inputs: [ + { + name: "adapterImplementation", + type: "address", + internalType: "address", + }, + { + name: "hookData", + type: "tuple", + internalType: "struct DataTypes.HookOrderData", + components: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "sellAsset", + type: "address", + internalType: "address", + }, + { + name: "buyAsset", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "validTo", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookSellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookBuyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isAdapterImplementation", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "notifyRepayFlashLoan", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "openFlashLoans", + inputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "instanceActive", + type: "bool", + internalType: "bool", + }, + { + name: "asset", + type: "address", + internalType: "address", + }, + { + name: "totalAmount", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "recoverERC20", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "renounceOwnership", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setAdapterImplementation", + inputs: [ + { + name: "adapterImplementation", + type: "address", + internalType: "address", + }, + { + name: "active", + type: "bool", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "transferOwnership", + inputs: [ + { + name: "newOwner", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "OwnershipTransferred", + inputs: [ + { + name: "previousOwner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newOwner", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "SetAdapterImplementation", + inputs: [ + { + name: "adapterImplementation", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "active", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "AddressZero", + inputs: [], + }, + { + type: "error", + name: "AmountZero", + inputs: [], + }, + { + type: "error", + name: "BalanceInvalid", + inputs: [], + }, + { + type: "error", + name: "CallerNotAavePool", + inputs: [], + }, + { + type: "error", + name: "CallerNotInstance", + inputs: [], + }, + { + type: "error", + name: "CallerNotRouter", + inputs: [], + }, + { + type: "error", + name: "CallerNotSettlement", + inputs: [], + }, + { + type: "error", + name: "FailedDeployment", + inputs: [], + }, + { + type: "error", + name: "ImplementationAlreadyListed", + inputs: [], + }, + { + type: "error", + name: "ImplementationNotFound", + inputs: [], + }, + { + type: "error", + name: "InstanceAlreadyDeployed", + inputs: [], + }, + { + type: "error", + name: "InstanceNotInitialized", + inputs: [], + }, + { + type: "error", + name: "InsufficientBalance", + inputs: [ + { + name: "balance", + type: "uint256", + internalType: "uint256", + }, + { + name: "needed", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "LenderInvalid", + inputs: [], + }, + { + type: "error", + name: "NothingToRepay", + inputs: [], + }, + { + type: "error", + name: "OrderReceiverInvalid", + inputs: [], + }, + { + type: "error", + name: "OwnableInvalidOwner", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "OwnableUnauthorizedAccount", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "SafeERC20FailedOperation", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + ], + }, +] as const; \ No newline at end of file diff --git a/src/scripts/flash-loans/abi/CollateralSwapAdapterHook.ts b/src/scripts/flash-loans/abi/CollateralSwapAdapterHook.ts new file mode 100644 index 0000000..bbe0dc2 --- /dev/null +++ b/src/scripts/flash-loans/abi/CollateralSwapAdapterHook.ts @@ -0,0 +1,378 @@ +export const collateralSwapAdapterHookAbi = [ + { + type: "function", + name: "AAVE_POOL", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IPool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "FACTORY", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "SETTLEMENT_CONTRACT", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "collateralSwapWithFlashLoan", + inputs: [ + { + name: "erc20Permit", + type: "tuple", + internalType: "struct DataTypes.Permit", + components: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + { + name: "deadline", + type: "uint256", + internalType: "uint256", + }, + { + name: "v", + type: "uint8", + internalType: "uint8", + }, + { + name: "r", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "s", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "hookData", + inputs: [], + outputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "sellAsset", + type: "address", + internalType: "address", + }, + { + name: "buyAsset", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "validTo", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookSellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookBuyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "initialize", + inputs: [ + { + name: "aavePool_", + type: "address", + internalType: "address", + }, + { + name: "settlement_", + type: "address", + internalType: "address", + }, + { + name: "hookData_", + type: "tuple", + internalType: "struct DataTypes.HookOrderData", + components: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "sellAsset", + type: "address", + internalType: "address", + }, + { + name: "buyAsset", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "validTo", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookSellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookBuyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { + name: "_orderHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "_signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "recoverERC20", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "error", + name: "AddressZero", + inputs: [], + }, + { + type: "error", + name: "AmountInvalid", + inputs: [], + }, + { + type: "error", + name: "AmountZero", + inputs: [], + }, + { + type: "error", + name: "BalanceInvalid", + inputs: [], + }, + { + type: "error", + name: "BorrowInvalid", + inputs: [], + }, + { + type: "error", + name: "BuyAmountInvalid", + inputs: [], + }, + { + type: "error", + name: "BuyTokenInvalid", + inputs: [], + }, + { + type: "error", + name: "CallerNotHookTrampoline", + inputs: [], + }, + { + type: "error", + name: "CallerNotInstanceOwner", + inputs: [], + }, + { + type: "error", + name: "OrderExpired", + inputs: [], + }, + { + type: "error", + name: "OrderFeeNotNull", + inputs: [], + }, + { + type: "error", + name: "OrderHashMismatch", + inputs: [], + }, + { + type: "error", + name: "OrderKindInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderPartiallyFillable", + inputs: [], + }, + { + type: "error", + name: "OrderReceiverInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderSignatureInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderTokenBalanceInvalid", + inputs: [], + }, + { + type: "error", + name: "SafeERC20FailedOperation", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "SellAmountInvalid", + inputs: [], + }, + { + type: "error", + name: "SellTokenInvalid", + inputs: [], + }, + { + type: "error", + name: "WithdrawInvalid", + inputs: [], + }, +] as const; + \ No newline at end of file diff --git a/src/scripts/flash-loans/abi/DebtSwapAdapterHook.ts b/src/scripts/flash-loans/abi/DebtSwapAdapterHook.ts new file mode 100644 index 0000000..254b2fa --- /dev/null +++ b/src/scripts/flash-loans/abi/DebtSwapAdapterHook.ts @@ -0,0 +1,378 @@ +export const debtSwapAdapterHookAbi = [ + { + type: "function", + name: "AAVE_POOL", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IPool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "FACTORY", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "SETTLEMENT_CONTRACT", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "debtSwapWithFlashLoan", + inputs: [ + { + name: "creditDelegationSig", + type: "tuple", + internalType: "struct DataTypes.CreditDelegationSig", + components: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + { + name: "deadline", + type: "uint256", + internalType: "uint256", + }, + { + name: "v", + type: "uint8", + internalType: "uint8", + }, + { + name: "r", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "s", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "hookData", + inputs: [], + outputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "sellAsset", + type: "address", + internalType: "address", + }, + { + name: "buyAsset", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "validTo", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookSellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookBuyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "initialize", + inputs: [ + { + name: "aavePool_", + type: "address", + internalType: "address", + }, + { + name: "settlement_", + type: "address", + internalType: "address", + }, + { + name: "hookData_", + type: "tuple", + internalType: "struct DataTypes.HookOrderData", + components: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "sellAsset", + type: "address", + internalType: "address", + }, + { + name: "buyAsset", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "validTo", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookSellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookBuyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { + name: "_orderHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "_signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "recoverERC20", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "error", + name: "AddressZero", + inputs: [], + }, + { + type: "error", + name: "AmountInvalid", + inputs: [], + }, + { + type: "error", + name: "AmountZero", + inputs: [], + }, + { + type: "error", + name: "BalanceInvalid", + inputs: [], + }, + { + type: "error", + name: "BorrowInvalid", + inputs: [], + }, + { + type: "error", + name: "BuyAmountInvalid", + inputs: [], + }, + { + type: "error", + name: "BuyTokenInvalid", + inputs: [], + }, + { + type: "error", + name: "CallerNotHookTrampoline", + inputs: [], + }, + { + type: "error", + name: "CallerNotInstanceOwner", + inputs: [], + }, + { + type: "error", + name: "OrderExpired", + inputs: [], + }, + { + type: "error", + name: "OrderFeeNotNull", + inputs: [], + }, + { + type: "error", + name: "OrderHashMismatch", + inputs: [], + }, + { + type: "error", + name: "OrderKindInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderPartiallyFillable", + inputs: [], + }, + { + type: "error", + name: "OrderReceiverInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderSignatureInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderTokenBalanceInvalid", + inputs: [], + }, + { + type: "error", + name: "SafeERC20FailedOperation", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "SellAmountInvalid", + inputs: [], + }, + { + type: "error", + name: "SellTokenInvalid", + inputs: [], + }, + { + type: "error", + name: "WithdrawInvalid", + inputs: [], + }, +] as const; + \ No newline at end of file diff --git a/src/scripts/flash-loans/abi/RepayWithCollateralAdapterHook.ts b/src/scripts/flash-loans/abi/RepayWithCollateralAdapterHook.ts new file mode 100644 index 0000000..09fb954 --- /dev/null +++ b/src/scripts/flash-loans/abi/RepayWithCollateralAdapterHook.ts @@ -0,0 +1,378 @@ +export const repayWithCollateralAdapterHookAbi = [ + { + type: "function", + name: "AAVE_POOL", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IPool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "FACTORY", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "SETTLEMENT_CONTRACT", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "hookData", + inputs: [], + outputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "sellAsset", + type: "address", + internalType: "address", + }, + { + name: "buyAsset", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "validTo", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookSellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookBuyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "initialize", + inputs: [ + { + name: "aavePool_", + type: "address", + internalType: "address", + }, + { + name: "settlement_", + type: "address", + internalType: "address", + }, + { + name: "hookData_", + type: "tuple", + internalType: "struct DataTypes.HookOrderData", + components: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "sellAsset", + type: "address", + internalType: "address", + }, + { + name: "buyAsset", + type: "address", + internalType: "address", + }, + { + name: "sellAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "buyAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "kind", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "validTo", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "flashLoanFeeAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookSellAssetAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "hookBuyAssetAmount", + type: "uint256", + internalType: "uint256", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "isValidSignature", + inputs: [ + { + name: "_orderHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "_signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes4", + internalType: "bytes4", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "recoverERC20", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "repayDebtWithFlashLoan", + inputs: [ + { + name: "erc20Permit", + type: "tuple", + internalType: "struct DataTypes.Permit", + components: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + { + name: "deadline", + type: "uint256", + internalType: "uint256", + }, + { + name: "v", + type: "uint8", + internalType: "uint8", + }, + { + name: "r", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "s", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "error", + name: "AddressZero", + inputs: [], + }, + { + type: "error", + name: "AmountInvalid", + inputs: [], + }, + { + type: "error", + name: "AmountZero", + inputs: [], + }, + { + type: "error", + name: "BalanceInvalid", + inputs: [], + }, + { + type: "error", + name: "BorrowInvalid", + inputs: [], + }, + { + type: "error", + name: "BuyAmountInvalid", + inputs: [], + }, + { + type: "error", + name: "BuyTokenInvalid", + inputs: [], + }, + { + type: "error", + name: "CallerNotHookTrampoline", + inputs: [], + }, + { + type: "error", + name: "CallerNotInstanceOwner", + inputs: [], + }, + { + type: "error", + name: "OrderExpired", + inputs: [], + }, + { + type: "error", + name: "OrderFeeNotNull", + inputs: [], + }, + { + type: "error", + name: "OrderHashMismatch", + inputs: [], + }, + { + type: "error", + name: "OrderKindInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderPartiallyFillable", + inputs: [], + }, + { + type: "error", + name: "OrderReceiverInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderSignatureInvalid", + inputs: [], + }, + { + type: "error", + name: "OrderTokenBalanceInvalid", + inputs: [], + }, + { + type: "error", + name: "SafeERC20FailedOperation", + inputs: [ + { + name: "token", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "SellAmountInvalid", + inputs: [], + }, + { + type: "error", + name: "SellTokenInvalid", + inputs: [], + }, + { + type: "error", + name: "WithdrawInvalid", + inputs: [], + }, +] as const; + \ No newline at end of file diff --git a/src/scripts/flash-loans/abi/VariableDebt.ts b/src/scripts/flash-loans/abi/VariableDebt.ts new file mode 100644 index 0000000..1418962 --- /dev/null +++ b/src/scripts/flash-loans/abi/VariableDebt.ts @@ -0,0 +1,961 @@ +export const variableDebtAbi = [ + { + inputs: [ + { + internalType: "contract IPool", + name: "pool", + type: "address", + }, + { + internalType: "address", + name: "rewardsController", + type: "address", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "CallerMustBePool", + type: "error", + }, + { + inputs: [], + name: "ECDSAInvalidSignature", + type: "error", + }, + { + inputs: [ + { + internalType: "uint256", + name: "length", + type: "uint256", + }, + ], + name: "ECDSAInvalidSignatureLength", + type: "error", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "s", + type: "bytes32", + }, + ], + name: "ECDSAInvalidSignatureS", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "allowance", + type: "uint256", + }, + { + internalType: "uint256", + name: "needed", + type: "uint256", + }, + ], + name: "ERC20InsufficientAllowance", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "allowance", + type: "uint256", + }, + { + internalType: "uint256", + name: "needed", + type: "uint256", + }, + ], + name: "InsufficientBorrowAllowance", + type: "error", + }, + { + inputs: [], + name: "InvalidBurnAmount", + type: "error", + }, + { + inputs: [], + name: "InvalidExpiration", + type: "error", + }, + { + inputs: [], + name: "InvalidMintAmount", + type: "error", + }, + { + inputs: [], + name: "InvalidSignature", + type: "error", + }, + { + inputs: [], + name: "OperationNotSupported", + type: "error", + }, + { + inputs: [], + name: "PoolAddressesDoNotMatch", + type: "error", + }, + { + inputs: [ + { + internalType: "uint8", + name: "bits", + type: "uint8", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "SafeCastOverflowedUintDowncast", + type: "error", + }, + { + inputs: [], + name: "ZeroAddressNotValid", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "spender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "fromUser", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "toUser", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "asset", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "BorrowAllowanceDelegated", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "target", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "balanceIncrease", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "index", + type: "uint256", + }, + ], + name: "Burn", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "underlyingAsset", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "pool", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "incentivesController", + type: "address", + }, + { + indexed: false, + internalType: "uint8", + name: "debtTokenDecimals", + type: "uint8", + }, + { + indexed: false, + internalType: "string", + name: "debtTokenName", + type: "string", + }, + { + indexed: false, + internalType: "string", + name: "debtTokenSymbol", + type: "string", + }, + { + indexed: false, + internalType: "bytes", + name: "params", + type: "bytes", + }, + ], + name: "Initialized", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "caller", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "onBehalfOf", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "balanceIncrease", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "index", + type: "uint256", + }, + ], + name: "Mint", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Transfer", + type: "event", + }, + { + inputs: [], + name: "DEBT_TOKEN_REVISION", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "DELEGATION_WITH_SIG_TYPEHASH", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "DOMAIN_SEPARATOR", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "EIP712_REVISION", + outputs: [ + { + internalType: "bytes", + name: "", + type: "bytes", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "POOL", + outputs: [ + { + internalType: "contract IPool", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "REWARDS_CONTROLLER", + outputs: [ + { + internalType: "contract IAaveIncentivesController", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "UNDERLYING_ASSET_ADDRESS", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "address", + name: "", + type: "address", + }, + ], + name: "allowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "delegatee", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + ], + name: "approveDelegation", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "user", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "fromUser", + type: "address", + }, + { + internalType: "address", + name: "toUser", + type: "address", + }, + ], + name: "borrowAllowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "from", + type: "address", + }, + { + internalType: "uint256", + name: "scaledAmount", + type: "uint256", + }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + ], + name: "burn", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "decimals", + outputs: [ + { + internalType: "uint8", + name: "", + type: "uint8", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "decreaseAllowance", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "delegator", + type: "address", + }, + { + internalType: "address", + name: "delegatee", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + internalType: "uint256", + name: "deadline", + type: "uint256", + }, + { + internalType: "uint8", + name: "v", + type: "uint8", + }, + { + internalType: "bytes32", + name: "r", + type: "bytes32", + }, + { + internalType: "bytes32", + name: "s", + type: "bytes32", + }, + ], + name: "delegationWithSig", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "getIncentivesController", + outputs: [ + { + internalType: "contract IAaveIncentivesController", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "user", + type: "address", + }, + ], + name: "getPreviousIndex", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "user", + type: "address", + }, + ], + name: "getScaledUserBalanceAndSupply", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "increaseAllowance", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "contract IPool", + name: "initializingPool", + type: "address", + }, + { + internalType: "address", + name: "underlyingAsset", + type: "address", + }, + { + internalType: "uint8", + name: "debtTokenDecimals", + type: "uint8", + }, + { + internalType: "string", + name: "debtTokenName", + type: "string", + }, + { + internalType: "string", + name: "debtTokenSymbol", + type: "string", + }, + { + internalType: "bytes", + name: "params", + type: "bytes", + }, + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "user", + type: "address", + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "scaledAmount", + type: "uint256", + }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + ], + name: "mint", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "nonces", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "user", + type: "address", + }, + ], + name: "scaledBalanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "scaledTotalSupply", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalSupply", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "address", + name: "", + type: "address", + }, + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + name: "transferFrom", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, +] as const; diff --git a/src/scripts/flash-loans/collateralSwapAavePoC.ts b/src/scripts/flash-loans/collateralSwapAavePoC.ts new file mode 100644 index 0000000..a397d4b --- /dev/null +++ b/src/scripts/flash-loans/collateralSwapAavePoC.ts @@ -0,0 +1,305 @@ +import { + SupportedChainId, + OrderSigningUtils, + UnsignedOrder, + SellTokenSource +} from "@cowprotocol/cow-sdk"; +import { ethers } from "ethers"; +import { confirm, getWallet } from "../../utils"; +import { getErc20Contract } from "../../contracts/erc20"; +import { aaveAdapterFactoryAbi } from "./abi/AaveAdapterFactory"; +import { collateralSwapAdapterHookAbi } from "./abi/CollateralSwapAdapterHook"; +import { MetadataApi } from '@cowprotocol/app-data'; +import { utils } from 'ethers' +import { + OrderBalance, + OrderKind, + hashOrder, + type Order} from '@cowprotocol/contracts' +import { GPv2Settlement__factory } from "@cowprotocol/cow-sdk/dist/common/generated"; + + +const TOKENS = { + oldUnderlying: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d", // WXDAI + oldCollateral: "0xd0Dd6cEF72143E22cCED4867eb0d5F2328715533", // aGnoWXDAI + debt: "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb", // GNO + newUnderlying: "0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0", // USDC.e + newCollateral: "0xC0333cb85B59a788d8C7CAe5e1Fd6E229A3E5a65", // aUSDC.e +} as const; + +const AAVE_POOL_ADDRESS = "0xb50201558B00496A145fE76f7424749556E326D8"; // See https://search.onaave.com/?q=sepolia +const AAVE_ADAPTER_FACTORY = "0x1186B5ad42E3e6d6c6901FC53b4A367540E6EcFE"; +const AAVE_COLLATERAL_SWAP_ADAPTER_HOOK = "0xe80eE1e73f120b1106179Ae3D582CA4Fd768d517"; + +const DEFAULT_GAS_LIMIT = "1000000"; // FIXME: This should not be necessary, it should estimate correctly! +const VALID_FOR = 1758300000; +const CHAIN_ID = SupportedChainId.GNOSIS_CHAIN; +const FLASHLOAN_FEE = "10000000000000000"; // 0.05% of the flashloan amount +const OLD_COLLATERAL_AMOUNT = "20000000000000000000"; +const NEW_COLLATERAL_AMOUNT = "18000000"; +const KIND_SELL = "0xf3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775"; + +function getEmptyPermitSig() { + return { + amount: 0, + deadline: 0, + v: 0, + r: ethers.constants.HashZero, // bytes32(0) in Solidity + s: ethers.constants.HashZero // bytes32(0) in Solidity + }; +} + +export async function run() { + const wallet = await getWallet(CHAIN_ID); + const trader = wallet.address; + + const adapterFactory = new ethers.Contract( + AAVE_ADAPTER_FACTORY, + aaveAdapterFactoryAbi, + wallet + ); + + const flashLoanParams = { + borrower: AAVE_ADAPTER_FACTORY, + lender: AAVE_POOL_ADDRESS, + flashLoanAsset: TOKENS.oldUnderlying, + flashLoanAmount: OLD_COLLATERAL_AMOUNT, + flashLoanFee: FLASHLOAN_FEE + } + + const hookAmounts = { + flashLoanAmount: flashLoanParams.flashLoanAmount, + flashLoanFeeAmount: flashLoanParams.flashLoanFee, + sellAssetAmount: OLD_COLLATERAL_AMOUNT, + buyAssetAmount: NEW_COLLATERAL_AMOUNT + } + + let order = { + sellToken: TOKENS.oldUnderlying, + buyToken: TOKENS.newUnderlying, + receiver: "0", // to be updated later + feeAmount: "0", + sellAmount: "19990000000000000000", + buyAmount: NEW_COLLATERAL_AMOUNT, + validTo: VALID_FOR, + kind: KIND_SELL, + partiallyFillable: false, + appData: ethers.constants.HashZero, + sellTokenBalance: "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", + buyTokenBalance: "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", + } + + const hookOrderData = { + owner: trader, + sellAsset: order.sellToken, + buyAsset: order.buyToken, + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + kind: order.kind, + validTo: VALID_FOR, // max value for timestamp expiration for Order + flashLoanAmount: hookAmounts.flashLoanAmount, + flashLoanFeeAmount: hookAmounts.flashLoanFeeAmount, + hookSellAssetAmount: hookAmounts.sellAssetAmount, + hookBuyAssetAmount: hookAmounts.buyAssetAmount + } + + console.log("Get deterministic address params", AAVE_COLLATERAL_SWAP_ADAPTER_HOOK, hookOrderData); + + const expectedInstanceAddress: string = await adapterFactory.getInstanceDeterministicAddress( + AAVE_COLLATERAL_SWAP_ADAPTER_HOOK, + hookOrderData + ) + + console.log("expectedInstanceAddress", expectedInstanceAddress); + order.receiver = expectedInstanceAddress; + + const confirmed = await confirm( + `Do you want to approve token ${TOKENS.oldCollateral} to spender ${expectedInstanceAddress}?` + ); + if (confirmed) { + const oldCollateral = getErc20Contract(TOKENS.oldCollateral, wallet); + const tx = await oldCollateral.approve(expectedInstanceAddress, OLD_COLLATERAL_AMOUNT); + await tx.wait(); + console.log("approved: ", tx.hash); + } + + const appCode = 'aave-v3-flashloan' + const flashLoanHint = { + amount: OLD_COLLATERAL_AMOUNT, // this is actually in UNDERLYING but aave tokens are 1:1 + receiver: AAVE_ADAPTER_FACTORY, + liquidityProvider: AAVE_POOL_ADDRESS, + protocolAdapter: AAVE_ADAPTER_FACTORY, + token: TOKENS.oldUnderlying + }; + console.log("flashLoanHint", flashLoanHint); + + // Prepare deployment of the helper contract + const preHookCalldata = adapterFactory.interface.encodeFunctionData( + "deployAndTransferFlashLoan", + [ + trader, + AAVE_COLLATERAL_SWAP_ADAPTER_HOOK, + hookAmounts, + order, + ] + ); + + const adapterHookInstance = new ethers.Contract( + expectedInstanceAddress, + collateralSwapAdapterHookAbi + ); + + const postHookCalldata = adapterHookInstance.interface.encodeFunctionData( + "collateralSwapWithFlashLoan", + [getEmptyPermitSig()] + ); + + const stringify = require('json-stringify-deterministic'); + const appDataDoc = { + appCode: appCode, + metadata: { + flashloan: flashLoanHint, + hooks: { + pre: [ + { + target: AAVE_ADAPTER_FACTORY, + callData: preHookCalldata, + gasLimit: DEFAULT_GAS_LIMIT, + }, + ], + post: [ + { + target: expectedInstanceAddress, + callData: postHookCalldata, + gasLimit: DEFAULT_GAS_LIMIT, + } + ], + } + + } + } + + // TODO: Update the metadataApi dependency to avoid this hack + //const metadataApi = new MetadataApi(); + const fullAppData = stringify(appDataDoc); + console.log("fullAppData", fullAppData, "\n"); + + const module = await import('ethers/lib/utils') + const { keccak256, toUtf8Bytes } = module.default || module + const appDataHash = keccak256(toUtf8Bytes(fullAppData)) + console.log("appDataHash", appDataHash); + + const cowOrder: Order = { + sellToken: order.sellToken, + buyToken: order.buyToken, + receiver: order.receiver, + feeAmount: "0", + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + validTo: VALID_FOR, + appData: appDataHash, + kind: OrderKind.SELL, + partiallyFillable: false, + sellTokenBalance: OrderBalance.ERC20, + buyTokenBalance: OrderBalance.ERC20, + } + const orderHash = hashOrder(await OrderSigningUtils.getDomain(CHAIN_ID), cowOrder); + console.log("orderHash", orderHash, "\n"); + + // TODO: There should be an easier way to encode an order + const types = [ + "address", // sellToken + "address", // buyToken + "address", // receiver + "uint256", // sellAmount + "uint256", // buyAmount + "uint32", // validTo + "bytes32", // appData + "uint256", // feeAmount + "bytes32", // kind + "bool", // partiallyFillable + "bytes32", // sellTokenBalance + "bytes32", // buyTokenBalance + ]; + const encodedOrder = utils.defaultAbiCoder.encode(types, [ + cowOrder.sellToken, + cowOrder.buyToken, + cowOrder.receiver, + cowOrder.sellAmount, + cowOrder.buyAmount, + cowOrder.validTo, + cowOrder.appData, + cowOrder.feeAmount, + "0xf3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775", // order.kind + cowOrder.partiallyFillable, + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.sellTokenBalance + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.buyTokenBalance + ]); + console.log("encodedOrder", encodedOrder); + + + const signedOrder = await OrderSigningUtils.signOrder(cowOrder as UnsignedOrder, CHAIN_ID, wallet); + console.log("signedOrder", signedOrder.signature); + + // TODO: ugly. Find a better way to encode the order+signature + const fullSingature = utils.defaultAbiCoder.encode( + ["tuple(address sellToken, address buyToken, address receiver, uint256 sellAmount, uint256 buyAmount, uint32 validTo, bytes32 appData, uint256 feeAmount, bytes32 kind, bool partiallyFillable, bytes32 sellTokenBalance, bytes32 buyTokenBalance)", "bytes"], + [ + [ + cowOrder.sellToken, + cowOrder.buyToken, + cowOrder.receiver, + cowOrder.sellAmount, + cowOrder.buyAmount, + cowOrder.validTo, + cowOrder.appData, + cowOrder.feeAmount, + "0xf3b277728b3fee749481eb3e0b3b48980dbbab78658fc419025cb16eee346775", // order.kind + cowOrder.partiallyFillable, + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.sellTokenBalance + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9" + ], + signedOrder.signature + ]); + console.log("fullSignature", fullSingature, "\n"); + + + const data = { + "sellToken": cowOrder.sellToken, + "buyToken": cowOrder.buyToken, + "receiver": cowOrder.receiver, + "feeAmount": "0", + "sellAmount": cowOrder.sellAmount, + "buyAmount": cowOrder.buyAmount, + "validTo": cowOrder.validTo, + "kind": "sell", + "partiallyFillable": false, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + "signingScheme": "eip1271", + "signature": fullSingature, + "from": expectedInstanceAddress.toString(), + "quoteId": 0, + "appData": fullAppData, + } + + console.log("data", data); + console.log("json", JSON.stringify(data), "\n"); + + // Post the order + const response = await fetch("https://barn.api.cow.fi/xdai/api/v1/orders", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`); + } + + console.log("response", response); + +} diff --git a/src/scripts/flash-loans/debtSwapAavePoC.ts b/src/scripts/flash-loans/debtSwapAavePoC.ts new file mode 100644 index 0000000..0614815 --- /dev/null +++ b/src/scripts/flash-loans/debtSwapAavePoC.ts @@ -0,0 +1,309 @@ +import { + SupportedChainId, + OrderSigningUtils, + UnsignedOrder, + SellTokenSource +} from "@cowprotocol/cow-sdk"; +import { ethers } from "ethers"; +import { confirm, getWallet } from "../../utils"; +import { aaveAdapterFactoryAbi } from "./abi/AaveAdapterFactory"; +import { debtSwapAdapterHookAbi } from "./abi/DebtSwapAdapterHook"; +import { variableDebtAbi } from "./abi/VariableDebt"; +import { MetadataApi } from '@cowprotocol/app-data'; +import { utils } from 'ethers' +import { + OrderBalance, + OrderKind, + hashOrder, + type Order} from '@cowprotocol/contracts' +import { GPv2Settlement__factory } from "@cowprotocol/cow-sdk/dist/common/generated"; + + +const TOKENS = { + oldDebt: "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb", // GNO + newDebt: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d", // WXDAI + newDebtVariable: "0x281963D7471eCdC3A2Bd4503e24e89691cfe420D", // aWXDAI +} as const; + +const AAVE_POOL_ADDRESS = "0xb50201558B00496A145fE76f7424749556E326D8"; // See https://search.onaave.com/?q=sepolia +const AAVE_ADAPTER_FACTORY = "0x1186B5ad42E3e6d6c6901FC53b4A367540E6EcFE"; +const AAVE_DEBT_SWAP_ADAPTER_HOOK = "0xE7d37c2b8d30868781f5668B86a441074A06C6DD"; + +const DEFAULT_GAS_LIMIT = "1000000"; // FIXME: This should not be necessary, it should estimate correctly! +const VALID_FOR = 1758660000; +const CHAIN_ID = SupportedChainId.GNOSIS_CHAIN; +const FLASHLOAN_FEE = "1000000000000000"; // 0.05% of the flashloan amount +const FLASHLOAN_AMOUNT = "2000000000000000000"; +const SELL_AMOUNT = "1999000000000000000"; // Enough to cover the debt. ~ 1.9 WXDAI +const BUY_AMOUNT = "11000000000000000"; // 0.011 GNO +const KIND_BUY = "0x6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc"; + +function getEmptyPermitSig() { + return { + amount: 0, + deadline: 0, + v: 0, + r: ethers.constants.HashZero, // bytes32(0) in Solidity + s: ethers.constants.HashZero // bytes32(0) in Solidity + }; +} + +export async function run() { + const wallet = await getWallet(CHAIN_ID); + const trader = wallet.address; + + const adapterFactory = new ethers.Contract( + AAVE_ADAPTER_FACTORY, + aaveAdapterFactoryAbi, + wallet + ); + + const flashLoanParams = { + borrower: AAVE_ADAPTER_FACTORY, + lender: AAVE_POOL_ADDRESS, + flashLoanAsset: TOKENS.newDebt, + flashLoanAmount: FLASHLOAN_AMOUNT, + flashLoanFee: FLASHLOAN_FEE + } + + const hookAmounts = { + flashLoanAmount: flashLoanParams.flashLoanAmount, + flashLoanFeeAmount: flashLoanParams.flashLoanFee, + sellAssetAmount: FLASHLOAN_AMOUNT, + buyAssetAmount: BUY_AMOUNT + } + + let order = { + sellToken: TOKENS.newDebt, + buyToken: TOKENS.oldDebt, + receiver: "0", // to be updated later + feeAmount: "0", + sellAmount: SELL_AMOUNT, + buyAmount: BUY_AMOUNT, + validTo: VALID_FOR, + kind: KIND_BUY, + partiallyFillable: false, + appData: ethers.constants.HashZero, + sellTokenBalance: "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", + buyTokenBalance: "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", + } + + const hookOrderData = { + owner: trader, + sellAsset: order.sellToken, + buyAsset: order.buyToken, + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + kind: order.kind, + validTo: VALID_FOR, // max value for timestamp expiration for Order + flashLoanAmount: hookAmounts.flashLoanAmount, + flashLoanFeeAmount: hookAmounts.flashLoanFeeAmount, + hookSellAssetAmount: hookAmounts.sellAssetAmount, + hookBuyAssetAmount: hookAmounts.buyAssetAmount + } + + console.log("Get deterministic address params", AAVE_DEBT_SWAP_ADAPTER_HOOK, hookOrderData); + + const expectedInstanceAddress: string = await adapterFactory.getInstanceDeterministicAddress( + AAVE_DEBT_SWAP_ADAPTER_HOOK, + hookOrderData + ) + + console.log("expectedInstanceAddress", expectedInstanceAddress); + order.receiver = expectedInstanceAddress; + + const confirmed = await confirm( + `Do you want to approveDelegation to ${TOKENS.newDebtVariable} to spender ${expectedInstanceAddress}?` + ); + if (confirmed) { + const variableDebt = new ethers.Contract( + TOKENS.newDebtVariable, + variableDebtAbi, + wallet + ); + + const tx = await variableDebt.approveDelegation(expectedInstanceAddress, hookAmounts.sellAssetAmount); + await tx.wait(); + console.log("approved delegation: ", tx.hash); + } + + const appCode = 'aave-v3-flashloan' + const flashLoanHint = { + amount: FLASHLOAN_AMOUNT, // this is actually in UNDERLYING but aave tokens are 1:1 + receiver: AAVE_ADAPTER_FACTORY, + liquidityProvider: AAVE_POOL_ADDRESS, + protocolAdapter: AAVE_ADAPTER_FACTORY, + token: TOKENS.newDebt + }; + console.log("flashLoanHint", flashLoanHint); + + // Prepare deployment of the helper contract + const preHookCalldata = adapterFactory.interface.encodeFunctionData( + "deployAndTransferFlashLoan", + [ + trader, + AAVE_DEBT_SWAP_ADAPTER_HOOK, + hookAmounts, + order, + ] + ); + + const adapterHookInstance = new ethers.Contract( + expectedInstanceAddress, + debtSwapAdapterHookAbi + ); + + const postHookCalldata = adapterHookInstance.interface.encodeFunctionData( + "debtSwapWithFlashLoan", + [getEmptyPermitSig()] + ); + + const stringify = require('json-stringify-deterministic'); + const appDataDoc = { + appCode: appCode, + metadata: { + flashloan: flashLoanHint, + hooks: { + pre: [ + { + target: AAVE_ADAPTER_FACTORY, + callData: preHookCalldata, + gasLimit: DEFAULT_GAS_LIMIT, + }, + ], + post: [ + { + target: expectedInstanceAddress, + callData: postHookCalldata, + gasLimit: DEFAULT_GAS_LIMIT, + } + ], + } + + } + } + + // TODO: Update the metadataApi dependency to avoid this hack + //const metadataApi = new MetadataApi(); + const fullAppData = stringify(appDataDoc); + console.log("fullAppData", fullAppData, "\n"); + + const module = await import('ethers/lib/utils') + const { keccak256, toUtf8Bytes } = module.default || module + const appDataHash = keccak256(toUtf8Bytes(fullAppData)) + console.log("appDataHash", appDataHash); + + const cowOrder: Order = { + sellToken: order.sellToken, + buyToken: order.buyToken, + receiver: order.receiver, + feeAmount: "0", + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + validTo: VALID_FOR, + appData: appDataHash, + kind: OrderKind.BUY, + partiallyFillable: false, + sellTokenBalance: OrderBalance.ERC20, + buyTokenBalance: OrderBalance.ERC20, + } + const orderHash = hashOrder(await OrderSigningUtils.getDomain(CHAIN_ID), cowOrder); + console.log("orderHash", orderHash, "\n"); + + // TODO: There should be an easier way to encode an order + const types = [ + "address", // sellToken + "address", // buyToken + "address", // receiver + "uint256", // sellAmount + "uint256", // buyAmount + "uint32", // validTo + "bytes32", // appData + "uint256", // feeAmount + "bytes32", // kind + "bool", // partiallyFillable + "bytes32", // sellTokenBalance + "bytes32", // buyTokenBalance + ]; + const encodedOrder = utils.defaultAbiCoder.encode(types, [ + cowOrder.sellToken, + cowOrder.buyToken, + cowOrder.receiver, + cowOrder.sellAmount, + cowOrder.buyAmount, + cowOrder.validTo, + cowOrder.appData, + cowOrder.feeAmount, + KIND_BUY, + cowOrder.partiallyFillable, + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.sellTokenBalance + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.buyTokenBalance + ]); + console.log("encodedOrder", encodedOrder); + + + const signedOrder = await OrderSigningUtils.signOrder(cowOrder as UnsignedOrder, CHAIN_ID, wallet); + console.log("signedOrder", signedOrder.signature); + + // TODO: ugly. Find a better way to encode the order+signature + const fullSingature = utils.defaultAbiCoder.encode( + ["tuple(address sellToken, address buyToken, address receiver, uint256 sellAmount, uint256 buyAmount, uint32 validTo, bytes32 appData, uint256 feeAmount, bytes32 kind, bool partiallyFillable, bytes32 sellTokenBalance, bytes32 buyTokenBalance)", "bytes"], + [ + [ + cowOrder.sellToken, + cowOrder.buyToken, + cowOrder.receiver, + cowOrder.sellAmount, + cowOrder.buyAmount, + cowOrder.validTo, + cowOrder.appData, + cowOrder.feeAmount, + KIND_BUY, + cowOrder.partiallyFillable, + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.sellTokenBalance + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9" + ], + signedOrder.signature + ]); + console.log("fullSignature", fullSingature, "\n"); + + + const data = { + "sellToken": cowOrder.sellToken, + "buyToken": cowOrder.buyToken, + "receiver": cowOrder.receiver, + "feeAmount": "0", + "sellAmount": cowOrder.sellAmount, + "buyAmount": cowOrder.buyAmount, + "validTo": cowOrder.validTo, + "kind": "buy", + "partiallyFillable": false, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + "signingScheme": "eip1271", + "signature": fullSingature, + "from": expectedInstanceAddress.toString(), + "quoteId": 0, + "appData": fullAppData, + } + + console.log("data", data); + console.log("json", JSON.stringify(data), "\n"); + + // Post the order + const response = await fetch("https://barn.api.cow.fi/xdai/api/v1/orders", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`); + } + + console.log("response", response); + +} diff --git a/src/scripts/flash-loans/repayDebtWithCollateralAavePoC.ts b/src/scripts/flash-loans/repayDebtWithCollateralAavePoC.ts new file mode 100644 index 0000000..ad8c9c4 --- /dev/null +++ b/src/scripts/flash-loans/repayDebtWithCollateralAavePoC.ts @@ -0,0 +1,304 @@ +import { + SupportedChainId, + OrderSigningUtils, + UnsignedOrder, + SellTokenSource +} from "@cowprotocol/cow-sdk"; +import { ethers } from "ethers"; +import { confirm, getWallet } from "../../utils"; +import { getErc20Contract } from "../../contracts/erc20"; +import { aaveAdapterFactoryAbi } from "./abi/AaveAdapterFactory"; +import { repayWithCollateralAdapterHookAbi } from "./abi/RepayWithCollateralAdapterHook"; +import { MetadataApi } from '@cowprotocol/app-data'; +import { utils } from 'ethers' +import { + OrderBalance, + OrderKind, + hashOrder, + type Order} from '@cowprotocol/contracts' +import { GPv2Settlement__factory } from "@cowprotocol/cow-sdk/dist/common/generated"; + + +const TOKENS = { + underlying: "0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0", // USDC.e + collateral: "0xC0333cb85B59a788d8C7CAe5e1Fd6E229A3E5a65", // aUSDC.e + debt: "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb", // GNO +} as const; + +const AAVE_POOL_ADDRESS = "0xb50201558B00496A145fE76f7424749556E326D8"; // See https://search.onaave.com/?q=sepolia +const AAVE_ADAPTER_FACTORY = "0x1186B5ad42E3e6d6c6901FC53b4A367540E6EcFE"; +const AAVE_REPAY_DEBT_WITH_COLLATERAL_ADAPTER_HOOK = "0x9b80D127764c477165B31A9d1C49893Ece648A19"; + +const DEFAULT_GAS_LIMIT = "1000000"; // FIXME: This should not be necessary, it should estimate correctly! +const VALID_FOR = 1758320000; +const CHAIN_ID = SupportedChainId.GNOSIS_CHAIN; +const FLASHLOAN_FEE = "1000"; // 0.05% of the flashloan amount +const FLASHLOAN_AMOUNT = "2000000"; +const SELL_AMOUNT = "1999000"; // Enough to cover the debt. ~ 1.9 USDC.e +const BUY_AMOUNT = "11000000000000000"; // 0.011 GNO +const KIND_BUY = "0x6ed88e868af0a1983e3886d5f3e95a2fafbd6c3450bc229e27342283dc429ccc"; + +function getEmptyPermitSig() { + return { + amount: 0, + deadline: 0, + v: 0, + r: ethers.constants.HashZero, // bytes32(0) in Solidity + s: ethers.constants.HashZero // bytes32(0) in Solidity + }; +} + +export async function run() { + const wallet = await getWallet(CHAIN_ID); + const trader = wallet.address; + + const adapterFactory = new ethers.Contract( + AAVE_ADAPTER_FACTORY, + aaveAdapterFactoryAbi, + wallet + ); + + const flashLoanParams = { + borrower: AAVE_ADAPTER_FACTORY, + lender: AAVE_POOL_ADDRESS, + flashLoanAsset: TOKENS.underlying, + flashLoanAmount: FLASHLOAN_AMOUNT, + flashLoanFee: FLASHLOAN_FEE + } + + const hookAmounts = { + flashLoanAmount: flashLoanParams.flashLoanAmount, + flashLoanFeeAmount: flashLoanParams.flashLoanFee, + sellAssetAmount: FLASHLOAN_AMOUNT, + buyAssetAmount: BUY_AMOUNT + } + + let order = { + sellToken: TOKENS.underlying, + buyToken: TOKENS.debt, + receiver: "0", // to be updated later + feeAmount: "0", + sellAmount: SELL_AMOUNT, + buyAmount: BUY_AMOUNT, + validTo: VALID_FOR, + kind: KIND_BUY, + partiallyFillable: false, + appData: ethers.constants.HashZero, + sellTokenBalance: "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", + buyTokenBalance: "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", + } + + const hookOrderData = { + owner: trader, + sellAsset: order.sellToken, + buyAsset: order.buyToken, + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + kind: order.kind, + validTo: VALID_FOR, // max value for timestamp expiration for Order + flashLoanAmount: hookAmounts.flashLoanAmount, + flashLoanFeeAmount: hookAmounts.flashLoanFeeAmount, + hookSellAssetAmount: hookAmounts.sellAssetAmount, + hookBuyAssetAmount: hookAmounts.buyAssetAmount + } + + console.log("Get deterministic address params", AAVE_REPAY_DEBT_WITH_COLLATERAL_ADAPTER_HOOK, hookOrderData); + + const expectedInstanceAddress: string = await adapterFactory.getInstanceDeterministicAddress( + AAVE_REPAY_DEBT_WITH_COLLATERAL_ADAPTER_HOOK, + hookOrderData + ) + + console.log("expectedInstanceAddress", expectedInstanceAddress); + order.receiver = expectedInstanceAddress; + + const confirmed = await confirm( + `Do you want to approve token ${TOKENS.collateral} to spender ${expectedInstanceAddress}?` + ); + if (confirmed) { + const collateral = getErc20Contract(TOKENS.collateral, wallet); + const tx = await collateral.approve(expectedInstanceAddress, FLASHLOAN_AMOUNT); + await tx.wait(); + console.log("approved: ", tx.hash); + } + + const appCode = 'aave-v3-flashloan' + const flashLoanHint = { + amount: FLASHLOAN_AMOUNT, // this is actually in UNDERLYING but aave tokens are 1:1 + receiver: AAVE_ADAPTER_FACTORY, + liquidityProvider: AAVE_POOL_ADDRESS, + protocolAdapter: AAVE_ADAPTER_FACTORY, + token: TOKENS.underlying + }; + console.log("flashLoanHint", flashLoanHint); + + // Prepare deployment of the helper contract + const preHookCalldata = adapterFactory.interface.encodeFunctionData( + "deployAndTransferFlashLoan", + [ + trader, + AAVE_REPAY_DEBT_WITH_COLLATERAL_ADAPTER_HOOK, + hookAmounts, + order, + ] + ); + + const adapterHookInstance = new ethers.Contract( + expectedInstanceAddress, + repayWithCollateralAdapterHookAbi + ); + + const postHookCalldata = adapterHookInstance.interface.encodeFunctionData( + "repayDebtWithFlashLoan", + [getEmptyPermitSig()] + ); + + const stringify = require('json-stringify-deterministic'); + const appDataDoc = { + appCode: appCode, + metadata: { + flashloan: flashLoanHint, + hooks: { + pre: [ + { + target: AAVE_ADAPTER_FACTORY, + callData: preHookCalldata, + gasLimit: DEFAULT_GAS_LIMIT, + }, + ], + post: [ + { + target: expectedInstanceAddress, + callData: postHookCalldata, + gasLimit: DEFAULT_GAS_LIMIT, + } + ], + } + + } + } + + // TODO: Update the metadataApi dependency to avoid this hack + //const metadataApi = new MetadataApi(); + const fullAppData = stringify(appDataDoc); + console.log("fullAppData", fullAppData, "\n"); + + const module = await import('ethers/lib/utils') + const { keccak256, toUtf8Bytes } = module.default || module + const appDataHash = keccak256(toUtf8Bytes(fullAppData)) + console.log("appDataHash", appDataHash); + + const cowOrder: Order = { + sellToken: order.sellToken, + buyToken: order.buyToken, + receiver: order.receiver, + feeAmount: "0", + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + validTo: VALID_FOR, + appData: appDataHash, + kind: OrderKind.BUY, + partiallyFillable: false, + sellTokenBalance: OrderBalance.ERC20, + buyTokenBalance: OrderBalance.ERC20, + } + const orderHash = hashOrder(await OrderSigningUtils.getDomain(CHAIN_ID), cowOrder); + console.log("orderHash", orderHash, "\n"); + + // TODO: There should be an easier way to encode an order + const types = [ + "address", // sellToken + "address", // buyToken + "address", // receiver + "uint256", // sellAmount + "uint256", // buyAmount + "uint32", // validTo + "bytes32", // appData + "uint256", // feeAmount + "bytes32", // kind + "bool", // partiallyFillable + "bytes32", // sellTokenBalance + "bytes32", // buyTokenBalance + ]; + const encodedOrder = utils.defaultAbiCoder.encode(types, [ + cowOrder.sellToken, + cowOrder.buyToken, + cowOrder.receiver, + cowOrder.sellAmount, + cowOrder.buyAmount, + cowOrder.validTo, + cowOrder.appData, + cowOrder.feeAmount, + KIND_BUY, + cowOrder.partiallyFillable, + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.sellTokenBalance + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.buyTokenBalance + ]); + console.log("encodedOrder", encodedOrder); + + + const signedOrder = await OrderSigningUtils.signOrder(cowOrder as UnsignedOrder, CHAIN_ID, wallet); + console.log("signedOrder", signedOrder.signature); + + // TODO: ugly. Find a better way to encode the order+signature + const fullSingature = utils.defaultAbiCoder.encode( + ["tuple(address sellToken, address buyToken, address receiver, uint256 sellAmount, uint256 buyAmount, uint32 validTo, bytes32 appData, uint256 feeAmount, bytes32 kind, bool partiallyFillable, bytes32 sellTokenBalance, bytes32 buyTokenBalance)", "bytes"], + [ + [ + cowOrder.sellToken, + cowOrder.buyToken, + cowOrder.receiver, + cowOrder.sellAmount, + cowOrder.buyAmount, + cowOrder.validTo, + cowOrder.appData, + cowOrder.feeAmount, + KIND_BUY, + cowOrder.partiallyFillable, + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9", // order.sellTokenBalance + "0x5a28e9363bb942b639270062aa6bb295f434bcdfc42c97267bf003f272060dc9" + ], + signedOrder.signature + ]); + console.log("fullSignature", fullSingature, "\n"); + + + const data = { + "sellToken": cowOrder.sellToken, + "buyToken": cowOrder.buyToken, + "receiver": cowOrder.receiver, + "feeAmount": "0", + "sellAmount": cowOrder.sellAmount, + "buyAmount": cowOrder.buyAmount, + "validTo": cowOrder.validTo, + "kind": "buy", + "partiallyFillable": false, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + "signingScheme": "eip1271", + "signature": fullSingature, + "from": expectedInstanceAddress.toString(), + "quoteId": 0, + "appData": fullAppData, + } + + console.log("data", data); + console.log("json", JSON.stringify(data), "\n"); + + // Post the order + const response = await fetch("https://barn.api.cow.fi/xdai/api/v1/orders", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`HTTP error! status: ${response.status}, body: ${errorText}`); + } + + console.log("response", response); + +}