diff --git a/uniswap/__init__.py b/uniswap/__init__.py index a1612f6..1f0e8ad 100644 --- a/uniswap/__init__.py +++ b/uniswap/__init__.py @@ -1,5 +1,7 @@ from . import exceptions from .cli import main from .uniswap import Uniswap, _str_to_addr +from .uniswap4 import Uniswap4 +from .v4pools import V4pools -__all__ = ["Uniswap", "exceptions", "_str_to_addr", "main"] +__all__ = ["Uniswap", "Uniswap4", "V4pools", "exceptions", "_str_to_addr", "main"] diff --git a/uniswap/assets/uniswap-v4/permit2.abi b/uniswap/assets/uniswap-v4/permit2.abi new file mode 100644 index 0000000..2f0281e --- /dev/null +++ b/uniswap/assets/uniswap-v4/permit2.abi @@ -0,0 +1,901 @@ +[ + { + "inputs": [ + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "AllowanceExpired", + "type": "error" + }, + { + "inputs": [], + "name": "ExcessiveInvalidation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "maxAmount", + "type": "uint256" + } + ], + "name": "InvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidContractSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidNonce", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSignatureLength", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSigner", + "type": "error" + }, + { + "inputs": [], + "name": "LengthMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + } + ], + "name": "SignatureExpired", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "Lockdown", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "newNonce", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "oldNonce", + "type": "uint48" + } + ], + "name": "NonceInvalidation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "indexed": false, + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "name": "Permit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "word", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "mask", + "type": "uint256" + } + ], + "name": "UnorderedNonceInvalidation", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint48", + "name": "newNonce", + "type": "uint48" + } + ], + "name": "invalidateNonces", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "wordPos", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "mask", + "type": "uint256" + } + ], + "name": "invalidateUnorderedNonces", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "internalType": "struct IAllowanceTransfer.TokenSpenderPair[]", + "name": "approvals", + "type": "tuple[]" + } + ], + "name": "lockdown", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "nonceBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "internalType": "struct IAllowanceTransfer.PermitDetails[]", + "name": "details", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sigDeadline", + "type": "uint256" + } + ], + "internalType": "struct IAllowanceTransfer.PermitBatch", + "name": "permitBatch", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "uint48", + "name": "expiration", + "type": "uint48" + }, + { + "internalType": "uint48", + "name": "nonce", + "type": "uint48" + } + ], + "internalType": "struct IAllowanceTransfer.PermitDetails", + "name": "details", + "type": "tuple" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sigDeadline", + "type": "uint256" + } + ], + "internalType": "struct IAllowanceTransfer.PermitSingle", + "name": "permitSingle", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions", + "name": "permitted", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails", + "name": "transferDetails", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions[]", + "name": "permitted", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions", + "name": "permitted", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails", + "name": "transferDetails", + "type": "tuple" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "witness", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "witnessTypeString", + "type": "string" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitWitnessTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.TokenPermissions[]", + "name": "permitted", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.PermitBatchTransferFrom", + "name": "permit", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "requestedAmount", + "type": "uint256" + } + ], + "internalType": "struct ISignatureTransfer.SignatureTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "witness", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "witnessTypeString", + "type": "string" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + } + ], + "name": "permitWitnessTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "internalType": "struct IAllowanceTransfer.AllowanceTransferDetails[]", + "name": "transferDetails", + "type": "tuple[]" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint160", + "name": "amount", + "type": "uint160" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/assets/uniswap-v4/poolmanager.abi b/uniswap/assets/uniswap-v4/poolmanager.abi new file mode 100644 index 0000000..a4ef1f8 --- /dev/null +++ b/uniswap/assets/uniswap-v4/poolmanager.abi @@ -0,0 +1,1366 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AlreadyUnlocked", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "currency0", + "type": "address" + }, + { + "internalType": "address", + "name": "currency1", + "type": "address" + } + ], + "name": "CurrenciesOutOfOrderOrEqual", + "type": "error" + }, + { + "inputs": [], + "name": "CurrencyNotSettled", + "type": "error" + }, + { + "inputs": [], + "name": "DelegateCallNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidCaller", + "type": "error" + }, + { + "inputs": [], + "name": "ManagerLocked", + "type": "error" + }, + { + "inputs": [], + "name": "MustClearExactPositiveDelta", + "type": "error" + }, + { + "inputs": [], + "name": "NonzeroNativeValue", + "type": "error" + }, + { + "inputs": [], + "name": "PoolNotInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "ProtocolFeeCurrencySynced", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "ProtocolFeeTooLarge", + "type": "error" + }, + { + "inputs": [], + "name": "SwapAmountCannotBeZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "TickSpacingTooLarge", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "TickSpacingTooSmall", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedDynamicLPFeeUpdate", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Donate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "int256", + "name": "liquidityDelta", + "type": "int256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "ModifyLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "OperatorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "protocolFeeController", + "type": "address" + } + ], + "name": "ProtocolFeeControllerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "protocolFee", + "type": "uint24" + } + ], + "name": "ProtocolFeeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "PoolId", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "int128", + "name": "amount0", + "type": "int128" + }, + { + "indexed": false, + "internalType": "int128", + "name": "amount1", + "type": "int128" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "clear", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "collectProtocolFees", + "outputs": [ + { + "internalType": "uint256", + "name": "amountCollected", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "donate", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "delta", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "startSlot", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "nSlots", + "type": "uint256" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "slots", + "type": "bytes32[]" + } + ], + "name": "extsload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "slots", + "type": "bytes32[]" + } + ], + "name": "exttload", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "slot", + "type": "bytes32" + } + ], + "name": "exttload", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [ + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isOperator", + "outputs": [ + { + "internalType": "bool", + "name": "isOperator", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "int256", + "name": "liquidityDelta", + "type": "int256" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "internalType": "struct IPoolManager.ModifyLiquidityParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "modifyLiquidity", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "callerDelta", + "type": "int256" + }, + { + "internalType": "BalanceDelta", + "name": "feesAccrued", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFeeController", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + } + ], + "name": "protocolFeesAccrued", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setOperator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint24", + "name": "newProtocolFee", + "type": "uint24" + } + ], + "name": "setProtocolFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "controller", + "type": "address" + } + ], + "name": "setProtocolFeeController", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "settle", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "settleFor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + } + ], + "internalType": "struct IPoolManager.SwapParams", + "name": "params", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "BalanceDelta", + "name": "swapDelta", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + } + ], + "name": "sync", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Currency", + "name": "currency", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "take", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "unlock", + "outputs": [ + { + "internalType": "bytes", + "name": "result", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "key", + "type": "tuple" + }, + { + "internalType": "uint24", + "name": "newDynamicLPFee", + "type": "uint24" + } + ], + "name": "updateDynamicLPFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/assets/uniswap-v4/quoter.abi b/uniswap/assets/uniswap-v4/quoter.abi new file mode 100644 index 0000000..f38100b --- /dev/null +++ b/uniswap/assets/uniswap-v4/quoter.abi @@ -0,0 +1,637 @@ +[ + { + "inputs": [ + { + "internalType": "contract IPoolManager", + "name": "_poolManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "NotEnoughLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "NotPoolManager", + "type": "error" + }, + { + "inputs": [], + "name": "NotSelf", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "QuoteSwap", + "type": "error" + }, + { + "inputs": [], + "name": "UnexpectedCallSuccess", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "revertData", + "type": "bytes" + } + ], + "name": "UnexpectedRevertBytes", + "type": "error" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactInput", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactInputSingle", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactOutput", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "_quoteExactOutputSingle", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "poolManager", + "outputs": [ + { + "internalType": "contract IPoolManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactInput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactInputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "Currency", + "name": "exactCurrency", + "type": "address" + }, + { + "components": [ + { + "internalType": "Currency", + "name": "intermediateCurrency", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct PathKey[]", + "name": "path", + "type": "tuple[]" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + } + ], + "internalType": "struct IV4Quoter.QuoteExactParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactOutput", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "Currency", + "name": "currency0", + "type": "address" + }, + { + "internalType": "Currency", + "name": "currency1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "contract IHooks", + "name": "hooks", + "type": "address" + } + ], + "internalType": "struct PoolKey", + "name": "poolKey", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "uint128", + "name": "exactAmount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "hookData", + "type": "bytes" + } + ], + "internalType": "struct IV4Quoter.QuoteExactSingleParams", + "name": "params", + "type": "tuple" + } + ], + "name": "quoteExactOutputSingle", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasEstimate", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "unlockCallback", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/assets/uniswap-v4/router.abi b/uniswap/assets/uniswap-v4/router.abi new file mode 100644 index 0000000..2474909 --- /dev/null +++ b/uniswap/assets/uniswap-v4/router.abi @@ -0,0 +1 @@ +[{"inputs":[{"components":[{"internalType":"address","name":"permit2","type":"address"},{"internalType":"address","name":"weth9","type":"address"},{"internalType":"address","name":"v2Factory","type":"address"},{"internalType":"address","name":"v3Factory","type":"address"},{"internalType":"bytes32","name":"pairInitCodeHash","type":"bytes32"},{"internalType":"bytes32","name":"poolInitCodeHash","type":"bytes32"},{"internalType":"address","name":"v4PoolManager","type":"address"},{"internalType":"address","name":"v3NFTPositionManager","type":"address"},{"internalType":"address","name":"v4PositionManager","type":"address"}],"internalType":"struct RouterParameters","name":"params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BalanceTooLow","type":"error"},{"inputs":[],"name":"ContractLocked","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotNegative","type":"error"},{"inputs":[{"internalType":"Currency","name":"currency","type":"address"}],"name":"DeltaNotPositive","type":"error"},{"inputs":[],"name":"ETHNotAccepted","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandIndex","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"ExecutionFailed","type":"error"},{"inputs":[],"name":"FromAddressIsNotOwner","type":"error"},{"inputs":[],"name":"InputLengthMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientETH","type":"error"},{"inputs":[],"name":"InsufficientToken","type":"error"},{"inputs":[{"internalType":"bytes4","name":"action","type":"bytes4"}],"name":"InvalidAction","type":"error"},{"inputs":[],"name":"InvalidBips","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[],"name":"InvalidEthSender","type":"error"},{"inputs":[],"name":"InvalidPath","type":"error"},{"inputs":[],"name":"InvalidReserves","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NotAuthorizedForToken","type":"error"},{"inputs":[],"name":"NotPoolManager","type":"error"},{"inputs":[],"name":"OnlyMintAllowed","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnsafeCast","type":"error"},{"inputs":[{"internalType":"uint256","name":"action","type":"uint256"}],"name":"UnsupportedAction","type":"error"},{"inputs":[],"name":"V2InvalidPath","type":"error"},{"inputs":[],"name":"V2TooLittleReceived","type":"error"},{"inputs":[],"name":"V2TooMuchRequested","type":"error"},{"inputs":[],"name":"V3InvalidAmountOut","type":"error"},{"inputs":[],"name":"V3InvalidCaller","type":"error"},{"inputs":[],"name":"V3InvalidSwap","type":"error"},{"inputs":[],"name":"V3TooLittleReceived","type":"error"},{"inputs":[],"name":"V3TooMuchRequested","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountOutReceived","type":"uint256"},{"internalType":"uint256","name":"amountReceived","type":"uint256"}],"name":"V4TooLittleReceived","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxAmountInRequested","type":"uint256"},{"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"V4TooMuchRequested","type":"error"},{"inputs":[],"name":"V3_POSITION_MANAGER","outputs":[{"internalType":"contract INonfungiblePositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"V4_POSITION_MANAGER","outputs":[{"internalType":"contract IPositionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"commands","type":"bytes"},{"internalType":"bytes[]","name":"inputs","type":"bytes[]"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"msgSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"contract IPoolManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"unlockCallback","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/uniswap/assets/uniswap-v4/stateview.abi b/uniswap/assets/uniswap-v4/stateview.abi new file mode 100644 index 0000000..19d5866 --- /dev/null +++ b/uniswap/assets/uniswap-v4/stateview.abi @@ -0,0 +1,365 @@ +[ + { + "inputs": [ + { + "internalType": "contract IPoolManager", + "name": "_poolManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getFeeGrowthGlobals", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthGlobal0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthGlobal1", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "getFeeGrowthInside", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthInside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "positionId", + "type": "bytes32" + } + ], + "name": "getPositionInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "getPositionInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "positionId", + "type": "bytes32" + } + ], + "name": "getPositionLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + } + ], + "name": "getSlot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint24", + "name": "protocolFee", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "lpFee", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int16", + "name": "tick", + "type": "int16" + } + ], + "name": "getTickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "tickBitmap", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickFeeGrowthOutside", + "outputs": [ + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickInfo", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "PoolId", + "name": "poolId", + "type": "bytes32" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "getTickLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolManager", + "outputs": [ + { + "internalType": "contract IPoolManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/uniswap/configs/chains.ini b/uniswap/configs/chains.ini new file mode 100644 index 0000000..4739b90 --- /dev/null +++ b/uniswap/configs/chains.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=chain name +[settings] +1=MAINNET +130=Unichain +10=Optimism +8453=Base +42161=Arbitrum +137=Polygon +81457=Blast +7777777=Zora +480=Worldchain +57073=Ink +1868=Soneium +43114=Avalanche +56=BNB diff --git a/uniswap/configs/permit2.ini b/uniswap/configs/permit2.ini new file mode 100644 index 0000000..c6ee0ed --- /dev/null +++ b/uniswap/configs/permit2.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x000000000022D473030F116dDEE9F6B43aC78BA3 +130=0x000000000022D473030F116dDEE9F6B43aC78BA3 +10=0x000000000022D473030F116dDEE9F6B43aC78BA3 +8453=0x000000000022D473030F116dDEE9F6B43aC78BA3 +42161=0x000000000022D473030F116dDEE9F6B43aC78BA3 +137=0x000000000022D473030F116dDEE9F6B43aC78BA3 +81457=0x000000000022D473030F116dDEE9F6B43aC78BA3 +7777777=0x000000000022D473030F116dDEE9F6B43aC78BA3 +480=0x000000000022D473030F116dDEE9F6B43aC78BA3 +57073=0x000000000022D473030F116dDEE9F6B43aC78BA3 +1868=0x000000000022D473030F116dDEE9F6B43aC78BA3 +43114=0x000000000022D473030F116dDEE9F6B43aC78BA3 +56=0x000000000022D473030F116dDEE9F6B43aC78BA3 diff --git a/uniswap/configs/poolmanager.ini b/uniswap/configs/poolmanager.ini new file mode 100644 index 0000000..96799a8 --- /dev/null +++ b/uniswap/configs/poolmanager.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=pool manager contract address +[settings] +1=0x000000000004444c5dc75cB358380D2e3dE08A90 +130=0x1f98400000000000000000000000000000000004 +10=0x9a13f98cb987694c9f086b1f5eb990eea8264ec3 +8453=0x498581ff718922c3f8e6a244956af099b2652b2b +42161=0x360e68faccca8ca495c1b759fd9eee466db9fb32 +137=0x67366782805870060151383f4bbff9dab53e5cd6 +81457=0x1631559198a9e474033433b2958dabc135ab6446 +7777777=0x0575338e4c17006ae181b47900a84404247ca30f +480=0xb1860d529182ac3bc1f51fa2abd56662b7d13f33 +57073=0x360e68faccca8ca495c1b759fd9eee466db9fb32 +1868=0x360e68faccca8ca495c1b759fd9eee466db9fb32 +43114=0x06380c0e0912312b5150364b9dc4542ba0dbbc85 +56= 0x28e2ea090877bf75740558f6bfb36a5ffee9e9df \ No newline at end of file diff --git a/uniswap/configs/quoter.ini b/uniswap/configs/quoter.ini new file mode 100644 index 0000000..f6f27dd --- /dev/null +++ b/uniswap/configs/quoter.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=quoter contract address +[settings] +1=0x52f0e24d1c21c8a0cb1e5a5dd6198556bd9e1203 +130=0x333e3c607b141b18ff6de9f258db6e77fe7491e0 +10=0x1f3131a13296fb91c90870043742c3cdbff1a8d7 +8453=0x0d5e0f971ed27fbff6c2837bf31316121532048d +42161=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +137=0xb3d5c3dfc3a7aebff71895a7191796bffc2c81b9 +81457=0x6f71cdcb0d119ff72c6eb501abceb576fbf62bcf +7777777=0x5edaccc0660e0a2c44b06e07ce8b915e625dc2c6 +480=0x55d235b3ff2daf7c3ede0defc9521f1d6fe6c5c0 +57073=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +1868=0x3972c00f7ed4885e145823eb7c655375d275a1c5 +43114=0xbe40675bb704506a3c2ccfb762dcfd1e979845c2 +56=0x9f75dd27d6664c475b90e105573e550ff69437b0 \ No newline at end of file diff --git a/uniswap/configs/router.ini b/uniswap/configs/router.ini new file mode 100644 index 0000000..0cb1f7d --- /dev/null +++ b/uniswap/configs/router.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x66a9893cc07d91d95644aedd05d03f95e1dba8af +130=0xef740bf23acae26f6492b10de645d6b98dc8eaf3 +10=0x851116d9223fabed8e56c0e6b8ad0c31d98b3507 +8453=0x6ff5693b99212da76ad316178a184ab56d299b43 +42161=0xa51afafe0263b40edaef0df8781ea9aa03e381a3 +137=0x1095692a6237d83c6a72f3f5efedb9a670c49223 +81457=0xeabbcb3e8e415306207ef514f660a3f820025be3 +7777777=0x3315ef7ca28db74abadc6c44570efdf06b04b020 +480=0x8ac7bee993bb44dab564ea4bc9ea67bf9eb5e743 +57073=0x112908dac86e20e7241b0927479ea3bf935d1fa0 +1868=0x4cded7edf52c8aa5259a54ec6a3ce7c6d2a455df +43114=0x94b75331ae8d42c1b61065089b7d48fe14aa73b7 +56=0x1906c1d672b88cd1b9ac7593301ca990f94eae07 diff --git a/uniswap/configs/stateview.ini b/uniswap/configs/stateview.ini new file mode 100644 index 0000000..7bb9d6b --- /dev/null +++ b/uniswap/configs/stateview.ini @@ -0,0 +1,16 @@ +#Format: +#chainID=router contract address +[settings] +1=0x7ffe42c4a5deea5b0fec41c94c136cf115597227 +130=0x86e8631a016f9068c3f085faf484ee3f5fdee8f2 +10=0xc18a3169788f4f75a170290584eca6395c75ecdb +8453=0xa3c0c9b65bad0b08107aa264b0f3db444b867a71 +42161=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +137=0x5ea1bd7974c8a611cbab0bdcafcb1d9cc9b3ba5a +81457=0x12a88ae16f46dce4e8b15368008ab3380885df30 +7777777=0x385785af07d63b50d0a0ea57c4ff89d06adf7328 +480=0x51d394718bc09297262e368c1a481217fdeb71eb +57073=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +1868=0x76fd297e2d437cd7f76d50f01afe6160f86e9990 +43114=0xc3c9e198c735a4b97e3e683f391ccbdd60b69286 +56=0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4 diff --git a/uniswap/uni4base.py b/uniswap/uni4base.py new file mode 100644 index 0000000..da89a37 --- /dev/null +++ b/uniswap/uni4base.py @@ -0,0 +1,13 @@ + +ZERO_HOOK = "0x0000000000000000000000000000000000000000" +ETH_ADDRESS = "0x0000000000000000000000000000000000000000" +WRAPPED_ETH_ADDRESS = "0xc207eb4dF2E25c180902257aF349d841022561E8" + + +class pool_key: + currency0 : str + currency1 : str + fee : int + tick_spacing : int + hooks : str + diff --git a/uniswap/uniswap4.py b/uniswap/uniswap4.py new file mode 100644 index 0000000..c5715d4 --- /dev/null +++ b/uniswap/uniswap4.py @@ -0,0 +1,576 @@ +from web3 import Web3 +import eth_abi.abi +from web3.contract import Contract +from web3.contract.contract import ContractFunction +from eth_typing import AnyAddress +from eth_abi.codec import ABICodec +from eth_abi import encode +from eth_abi.packed import encode_packed +import os +import fnmatch +import configparser +import logging +from decimal import Decimal +from typing import List, Any, Optional, Callable, Union, Tuple, Dict +from web3.types import ( + TxParams, + Wei, + Address, + ChecksumAddress, + ENS, + Nonce, + HexBytes, +) +import json +import ctypes +from .uni4base import * +from .token import ERC20Token +from .types import AddressLike +from .util import ( + _addr_to_str, + _load_contract, + _load_contract_erc20, + _load_abi, + _str_to_addr, + _validate_address, + chunks, + encode_sqrt_ratioX96, + is_same_address, + nearest_tick, + realised_fee_percentage, +) + + +_netid_to_name = {1000: "mainnet", 1001: "nile"} +with open(os.path.abspath(f"assets\\erc20.abi")) as f: + erc20_ABI : str = json.load(f) + + +class Uniswap4(): + def __init__(self, + address: Union[str, AddressLike], + private_key: str, + provider: str=None, + web3: Web3=None, + version: int=4, + max_slippage: float=0.1, + max_gas: float=250000.0, + max_gprice: float=1.80, + london_fork: int=1, + max_priorityfee: float=1.0,) -> None: + + self.address : AddressLike = _str_to_addr(address) if isinstance(address, str) else address + self.private_key = private_key + self.version = version + + self.max_slippage = max_slippage + + if web3: + self.w3 = web3 + else: + self.provider = provider or os.environ["PROVIDER"] + self.w3 = Web3(Web3.HTTPProvider(self.provider, request_kwargs={"timeout": 60})) + + self.last_nonce : Nonce = self.w3.eth.get_transaction_count(self.address) + + # This code automatically approves you for trading on the exchange. + # max_approval is to allow the contract to exchange on your behalf. + # max_approval_check checks that current approval is above a reasonable + # number + # The program cannot check for max_approval each time because it + # decreases + # with each trade. + self.max_approval_hex = f"0x{64 * 'f'}" + self.max_approval_int = int(self.max_approval_hex, 16) + self.max_approval_check_hex = f"0x{15 * '0'}{49 * 'f'}" + self.max_approval_check_int = int(self.max_approval_check_hex, 16) + self.gas_limit = max_gas + self.gas_price = max_gprice + self.london_style = london_fork + self.london_priorityfee = max_priorityfee + + chain_id = self.w3.net.version + config = configparser.ConfigParser() + config.read("configs\\quoter.ini") + quoter_address = config.get("settings",chain_id) + config.read("configs\\router.ini") + router_address = config.get("settings",chain_id) + config.read("configs\\stateview.ini") + stateview_address = config.get("settings",chain_id) + config.read("configs\\permit2.ini") + permit2_address = config.get("settings",chain_id) + + self.quoter_address = _str_to_addr(quoter_address) + self.router_address = _str_to_addr(router_address) + self.stateview_address = _str_to_addr(stateview_address) + self.permit2_address = _str_to_addr(permit2_address) + + self.quoter = _load_contract(self.w3, abi_name = "uniswap-v4/quoter", address = self.quoter_address) + self.router = _load_contract(self.w3, abi_name="uniswap-v4/router", address=self.router_address) + self.stateview = _load_contract(self.w3, abi_name="uniswap-v4/stateview", address=self.stateview_address) + self.permit2 = _load_contract(self.w3, abi_name="uniswap-v4/permit2", address=self.permit2_address) + return + + def load_contract_with_abi(self, + abi_name: str, + address: AddressLike) -> Contract: + return self.w3.eth.contract(address=address, abi=_load_abi(abi_name)) + + def erc20_contract(self, + token_addr: AddressLike) -> Contract: + return self.load_contract_with_abi(abi_name="erc20", address=token_addr) + + def approve(self, + token: AddressLike, + max_approval: Optional[int]=None) -> HexBytes: + """Give an PERMIT2 approval of a token.""" + if(token != ETH_ADDRESS): + max_approval = self.max_approval_int if not max_approval else max_approval + function = self.erc20_contract(token).functions.approve(_addr_to_str(self.permit2_address), max_approval) + print(f"Approving {_addr_to_str(token)} for PERMIT2...") + tx = self._build_and_send_tx(function) + time.sleep(7) + #Give an exchange/router max approval of a token. + max_approval :int = 2 ** 100 - 1 + expiration :int = int(10 ** 12) + print(f"Setting permit for {_addr_to_str(token)} at router contract...") + function = self.permit2.functions.approve(_str_to_addr(token), self.router_address, max_approval, expiration) + tx = self._build_and_send_tx(function) + + return tx + + def approval(self, + token: AddressLike): + #[0]=current allowance, [1]=allowance expiration [2]=current nonce + result = int(self.permit2.functions.allowance(self.address, token, self.router.address).call()[0]) + return result + + def _get_tx_params(self, + value: int=0 , + gas: int=250000) -> dict: + """Get generic transaction parameters.""" + if self.london_style == 0: + return { + "from": _addr_to_str(self.address), + "value": value, + "gas": int(self.gas_limit), + "gasPrice": Web3.to_wei(self.gas_price, 'gwei'), + "nonce": max(self.last_nonce, 0), + } + else: + return { + "from": _addr_to_str(self.address), + "gas": int(self.gas_limit), + "maxPriorityFeePerGas": Web3.to_wei(self.london_priorityfee, 'gwei'), + "maxFeePerGas": Web3.to_wei(self.gas_price, 'gwei'), + "type": 2, + "chainId": self.w3.eth.chain_id, + "value": value, + "nonce": max(self.last_nonce, 0), + } + #Gas customization + #Gas limit + def get_gas_limit(self) -> float: + return self.gas_limit + + def set_gas_limit(self, gas_limit: float): + self.gas_limit = gas_limit + + #Gas price in GWei + def get_gas_price(self) -> float: + return self.gas_price + + def set_gas_price(self, gas_price: float): + self.gas_price = gas_price + + #Priority fee in GWei + def get_gas_priorityfee(self) -> float: + return self.london_priorityfee + + def set_gas_priorityfee(self, gas_priorityfee: float): + self.london_priorityfee = gas_priorityfee + + #Tokens price functions + def get_token_token_spot_price(self, + token0: AddressLike, + token1: AddressLike, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK,) -> int: + """Current spot price for token to token trades.""" + if token0 > token1: + (token1, token0) = (token0, token1) + + pool = pool_key() + pool.currency0 = token0 + pool.currency1 = token1 + pool.fee = fee + pool.tick_spacing = tick_spacing + pool.hooks = hooks + pool_id = self.get_pool_id(pool) + + if self.version == 4: + price : int = self.stateview.functions.getSlot0(pool_id.hex()).call()[0] + else: + raise ValueError("Function is not supported for this version") + return price + + def get_quote_exact_input_single(self, + token0: AddressLike, + token1: AddressLike, + qty: int, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK, + hook_data:bytes=bytes()) -> int: + """Quote for token to token single hop trades with an exact input.""" + if self.version == 4: + if(token0 < token1): + zero_for_one = True + else: + zero_for_one = False + (token1, token0) = (token0, token1) + pool_key = (token0, + token1, + fee, + tick_spacing, + hooks) + #[0]=The output quote [1]=estimated gas units used for the swap + price : int = self.quoter.functions.quoteExactInputSingle((pool_key, zero_for_one,qty, hook_data)).call()[0] + else: + raise ValueError("Function is not supported for this version") + return price + + def get_pool_id(self, pool: pool_key): + pool_data = eth_abi.abi.encode(types=["address", "address", "uint24", "int24", "address"], + args=[pool.currency0, + pool.currency1, + pool.fee, + pool.tick_spacing, + pool.hooks,],) + pool_id = Web3.keccak(pool_data) + return pool_id + + + def get_token(self, + address: AddressLike, + abi_name: str="erc20") -> ERC20Token: + """ + Retrieves metadata from the ERC20 contract of a given token, like its name, symbol, and decimals. + """ + # FIXME: This function should always return the same output for the + # same input + # and would therefore benefit from caching + if address == "0x0000000000000000000000000000000000000000": + # This isn't exactly right, but for all intents and purposes, + # ETH is treated as a ERC20 by Uniswap. + return ERC20Token(address=address, + name="ETH", + symbol="ETH", + decimals=18,) + token_contract = _load_contract(self.w3, abi_name, address=address) + try: + _name = token_contract.functions.name().call() + _symbol = token_contract.functions.symbol().call() + decimals = token_contract.functions.decimals().call() + except Exception as e: + logger.warning(f"Exception occurred while trying to get token {_addr_to_str(address)}: {e}") + raise InvalidToken(address) + try: + name = _name.decode() + except Exception: + name = _name + try: + symbol = _symbol.decode() + except Exception: + symbol = _symbol + return ERC20Token(symbol, address, name, decimals) + + #Estimates slippage for the given amount of token0 + def estimate_price_impact(self, + token0: AddressLike, + token1: AddressLike, + qty: int, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK, + hook_data:bytes=bytes(), + route: Optional[List[AddressLike]]=None,) -> float: + """ + Returns the estimated price impact as a positive float (0.01 = 1%). + + NOTE: Work-in-progress. + + See ``examples/price_impact.py`` for an example which uses this. + """ + + try: + spot_price = self.get_token_token_spot_price(token0, + token1, + fee, + tick_spacing, + hooks) + except (ArithmeticError, BadFunctionCallOutput): + # ArithmeticError is raised when `token0` amount in the pool + # equals 0. + # BadFunctionCallOutput is raised when the pool for + # given `(token0, token1, fee)` doesn't exist + return 1 + + if spot_price == 0: + # Occurs when `token1` amount in the pool equals 0 + return 1 + try: + quote_amount = self.get_quote_exact_input_single(token0, + token1, + fee, + tick_spacing, + hooks, + hook_data) + except ContractLogicError: + # ContractLogicError is raised when the pool's contract for given + # `(token0, token1, fee)` hasn't been deployed. + return 1 + price = (quote_amount / (qty / (10 ** self.get_token(token0).decimals))) / 10 ** self.get_token(token1).decimals + + # calculate and subtract the realised fees from the price impact. See: + # https://github.com/uniswap-python/uniswap-python/issues/310 + price_impact_with_fees = float((spot_price - price) / spot_price) + fee_realised_percentage = realised_fee_percentage(fee, qty) + price_impact_real = price_impact_with_fees - fee_realised_percentage + + + def get_quote_exact_output_single(self, + token0: AddressLike, + token1: AddressLike, + qty: int, + fee: int=500, + tick_spacing: int=10, + hooks: AddressLike=ZERO_HOOK, + hook_data:bytes=bytes()) -> int: + """Quote for token to token single hop trades with an exact output.""" + if self.version == 4: + if(token0 < token1): + zero_for_one = True + else: + zero_for_one = False + (token1, token0) = (token0, token1) + + pool_key = (token0, + token1, + fee, + tick_spacing, + hooks,) + #[0]=The input quote [1]=estimated gas units used for the swap + price : int = self.quoter.functions.quoteExactOutputSingle((pool_key, zero_for_one,qty, hook_data)).call()[0] + else: + raise ValueError("Function is not supported for this version") + return price + + + def build_execute_params(self,): + """ + Generic parameters builder for universal router execute() call. + WIP + """ + pass + + #Swap functions + def _token_to_token_swap_input(self, + input_token: str, + qty: int, + qtycap: int, + output_token: str, + recipient: Optional[AddressLike], + fee: int, + tick_spacing: int, + hooks: str,) -> HexBytes: + if self.version == 4: + if recipient is None: + recipient = self.address + + min_tokens_bought = int((1 - self.max_slippage) * qtycap) + + ether_amount = 0 + if(input_token == ETH_ADDRESS): + ether_amount = qty + + #V4_SWAP // Encode swap actions + commands = encode_packed(["uint8"], args=[0x10],) + + #SWAP_EXACT_IN_SINGLE, SETTLE_ALL, TAKE_ALL + actions = encode_packed(["uint8","uint8","uint8"], [0x06, 0x0C, 0x0F],) + + #SETTING PARAMS + pool_key = (input_token, + output_token, + fee, + tick_spacing, + hooks) + if input_token < output_token: + zero_for_one = True + (token0, token1) = (input_token, output_token) + else: + zero_for_one = False + (token0, token1) = (output_token,input_token) + exact_input_single_params = encode(['((address,address,uint24,int24,address),bool,int128,uint128,bytes)'], + [((token0, token1, fee, tick_spacing, hooks), zero_for_one, qty, min_tokens_bought, bytes(0))],) + settle_all_params = encode(['address','uint128'], [input_token, qty],) + take_all_params = encode(['address','uint128'], [output_token, min_tokens_bought],) + + #ENCODING DATA + params = [exact_input_single_params, settle_all_params, take_all_params] + inputs = [] + inputs.append(encode(['bytes','bytes[]'], [actions, params],)) + + return self._build_and_send_tx(self.router.functions.execute(commands, inputs, self._deadline()), self._get_tx_params(value=ether_amount)) + else: + raise ValueError("Function is not supported for this version") + + + def _token_to_token_swap_output(self, + input_token: AddressLike, + qty: int, + qtycap: int, + output_token: AddressLike, + recipient: Optional[AddressLike], + fee: int, + tick_spacing, + hooks: AddressLike,) -> HexBytes: + if self.version == 4: + if recipient is None: + recipient = self.address + + amount_in_max = int((1 + self.max_slippage) * qtycap) + + ether_amount = 0 + if(input_token == ETH_ADDRESS): + ether_amount = qty + + #V4_SWAP // Encode swap actions + commands = encode_packed(["uint8"], + args=[0x10],) + + #SWAP_EXACT_OUT_SINGLE, SETTLE_ALL, TAKE_ALL + actions = encode_packed(["uint8","uint8","uint8"], + args=[0x09, 0x0C, 0x0F],) + #SETTING PARAMS + pool_key = (input_token, + output_token, + fee, + tick_spacing, + hooks,) + if input_token < output_token: + zero_for_one = True + (token0, token1) = (input_token, output_token) + else: + zero_for_one = False + (token0, token1) = (output_token,input_token) + exact_output_single_params = encode(['((address,address,uint24,int24,address),bool,int128,uint128,bytes)'], + [((token0, token1,fee,tick_spacing,hooks,), zero_for_one, qty, amount_in_max, bytes(0))],) + settle_all_params = encode(["address","uint128"], [input_token, amount_in_max],) + take_all_params = encode(["address","uint128"], [output_token, qty],) + + #ENCODING DATA + params = (exact_output_single_params, settle_all_params, take_all_params) + inputs = [] + inputs.append(encode(["bytes","bytes[]"], [actions, params],)) + + return self._build_and_send_tx(self.router.functions.execute(commands, inputs, self._deadline()), self._get_tx_params(value=ether_amount)) + else: + raise ValueError("Function is not supported for this version") + + + #Replaces pending transaction with zero-value ETH transfer + def drop_txn(self, + address_to: AddressLike, + gwei: float, + gasv: float, + priorityfee: int=10) -> HexBytes: + + #This one is for legacy transactions + signed_txn = self.w3.eth.account.sign_transaction(dict(chainId=int(self.w3.net.version), + nonce=self.last_nonce, + gasPrice = Web3.to_wei(self.gas_price, 'gwei'), + gas = int(self.gas_limit), + to = Web3.to_checksum_address(address_to), + value = Web3.to_wei(0,'wei')), self.private_key) + #This one is for post-Merge + signed_txn_london = self.w3.eth.account.sign_transaction(dict(chainId=int(self.w3.net.version), + type=2, + nonce=self.last_nonce, + maxFeePerGas = Web3.to_wei(int(gwei), 'gwei'), + maxPriorityFeePerGas = Web3.to_wei(priorityfee, 'gwei'), + gas = int(21000), + to = Web3.to_checksum_address(address_to), + value = Web3.to_wei(0,'wei')), self.private_key) + if self.london_style == 1: + return self.w3.eth.send_raw_transaction(signed_txn_london.rawTransaction) + else: + return self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) + + def make_swap_input(self, + input_token: AddressLike, + output_token: AddressLike, + qty: int, + qtycap: int, + swap_pool_key: pool_key, + recipient: AddressLike=None, + fee: int=3000) -> HexBytes: + + return self._token_to_token_swap_input(input_token, qty, qtycap, output_token, recipient, swap_pool_key.fee, swap_pool_key.tick_spacing, swap_pool_key.hooks) + + def make_swap_output(self, + input_token: AddressLike, + output_token: AddressLike, + qty: int, + qtycap: int, + swap_pool_key: pool_key, + recipient: AddressLike=None, + fee: int=3000) -> HexBytes: + + return self._token_to_token_swap_output(swap_pool_key.currency0, qty, qtycap, swap_pool_key.currency1, recipient, swap_pool_key.fee, swap_pool_key.tick_spacing, swap_pool_key.hooks) + + def get_token_balance(self, + erc20: AddressLike) -> Decimal: + + contract = _load_contract(self.w3, abi_name = "erc20", address = erc20) + decimals = contract.functions.decimals().call() + try: + balance = contract.functions.balanceOf(self.address).call() + except: + balance = 0 + balance = Decimal(balance) / (10 ** decimals) + return balance + + def get_balance(self) -> Decimal: + """Get the balance of ETH for your address.""" + try: + balance = self.w3.eth.get_balance(self.address) + except: + balance = 0 + return balance + + def _deadline(self) -> int: + """Get a predefined deadline. 10min by default.""" + return int(time.time()) + 10 * 60 + + def _build_and_send_tx(self, + function: ContractFunction, + tx_params: Optional[dict]=None) -> HexBytes: + """Build and send a transaction.""" + if not tx_params: + tx_params = self._get_tx_params() + transaction = function.build_transaction(tx_params) + signed_txn = self.w3.eth.account.sign_transaction(transaction, private_key=self.private_key) + # TODO: This needs to get more complicated if we want to support + # replacing a transaction + # FIXME: This does not play nice if transactions are sent from other + # places using the same wallet. + try: + return self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) + finally: + #logger.debug(f"nonce: {tx_params['nonce']}") + self.last_nonce = Nonce(tx_params["nonce"] + 1) + + diff --git a/uniswap/v4pools.py b/uniswap/v4pools.py new file mode 100644 index 0000000..ce29ea0 --- /dev/null +++ b/uniswap/v4pools.py @@ -0,0 +1,137 @@ +from typing import List, Any, Optional, Callable, Union, Tuple, Dict +from xml.etree import ElementTree +import configparser +from web3 import Web3 +import eth_abi.abi +from .uni4base import * +from .util import ( + _addr_to_str, + _load_contract, + _load_contract_erc20, + _str_to_addr, +) + + +class V4pools(): + """Uniswap V4 pools handler""" + def __init__(self, + web3: Web3,): + self.poolkeys_list = List() + self.web3 = web3 + self.last_block = 0 + + + def get_last_block(self,) -> int: + return self.last_block + + def set_last_block(self, value: int): + self.last_block = value + + def fetch_poolkey_data(self, first_block_number: int, chunk_size: int=500): + """ + :param pool_manager_contract_address PoolManager address on the network specified by w3 param + :param chunk_size defines amount of blocks per log request + """ + #Scans PoolManager contract initialize() logs in order to get list of + #all pools.See documentation for suggested starting blocks + # + #chunk_size default value should be suitable for public nodes; increase + #on private nodes for better performance + + chain_id = self.web3.net.version + config = configparser.ConfigParser() + config.read("configs\\poolmanager.ini") + pool_manager_contract_address = config.get("settings",chain_id) + pool_manager_contract = _load_contract(self.web3, "uniswap-v4/poolmanager", _str_to_addr(pool_manager_contract_address)) + last_block_number = this.web3.eth.get_block_number() + + chunks_amount = int((last_block_number - first_block_number) // chunk_size) + start_block = first_block_number + end_block = 0 + + print("Logs proccessing started.") + + for i in range(0, chunks_amount + 1): + #{ + print(f"Processing chunk {i}/{chunks_amount}",end='\r',flush=True) + if i < chunksAmount: + end_block = start_block + chunk_size + else: + end_block = this.web3.eth.get_block_number() + logs = pool_manager_contract.events.initialize().get_logs(fromBlock=start_block, toBlock=end_block,) + for log_item in logs: + txn = this.web3.eth.get_transaction_receipt(log_item["TransactionHash"]) + if txn["to"].lower() != pool_manager_contract_address.lower() or txn["status"] == 0: + continue + pool : pool_key = pool_key() + pool.currency0 = log_item["event"]["currency0"] + pool.currency1 = log_item["event"]["currency1"] + pool.fee = log_item["event"]["fee"] + pool.tick_spacing = log_item["event"]["tickSpacing"] + pool.hooks = log_item["event"]["hooks"] + self.poolkeys_list.append(pool) + start_block = start_block + chunk_size + + print(f"Logs proccessing completed. Last block processed {end_block}") + self.set_last_block(end_block) + return + + def save_poolkeys_list(self, poolkey_data_filename: str): + """Saves poolKey list to specified file (XML format supposed)""" + element_tree = ElementTree() + pool_data = element_tree.Element('PoolData') + + for item in self.poolkeys_list: + pool = element_tree.SubElement(pool_data, 'Pool') + + currency0 = element_tree.SubElement(pool, 'Currency0') + currency0.text = str(item[0]) + + currency1 = element_tree.SubElement(pool, 'Currency1') + currency1.text = str(item[1]) + + fee = element_tree.SubElement(pool, 'Fee') + fee.text = str(item[2]) + + tick_spacing = element_tree.SubElement(pool, 'TickSpacing') + tick_spacing.text = str(item[3]) + + hooks = element_tree.SubElement(pool, 'Hooks') + hooks.text = str(item[4]) + + final_data = element_tree.ElementTree(pool_data) + final_data.write(poolkey_data_filename) + return + + def load_poolkeys_list(self, poolkey_data_filename: str): + """Loads poolKey list from specified file (XML format supposed)""" + if(os.path.isfile(poolkey_data_filename)): + try: + element_tree = ElementTree() + tree = element_tree.parse(poolkey_data_filename) + except ParseError: + raise ValueError("Parse error, file seems to be corrupted (" + poolkey_data_filename + ")") + self.poolkeys_list.clear() + root = tree.getroot() + for item in root: + pool : pool_key = pool_key() + pool.currency0 = item[0].text + pool.currency1 = item[1].text + pool.fee = int(item[2].text) + pool.tick_spacing = int(item[3].text) + pool.hooks = item[4].text + self.poolkeys_list.append(pool) + else: + raise ValueError("Couldn't locate file " + poolkey_data_filename) + + + def get_poolkeys_sublist(self, currency0: str, currency1: str) -> List: + """Returns all pools for the (currency0, currency1) pair""" + if(currency0 < currency1): + (c0, c1) = (currency0, currency1) + else: + (c0, c1) = (currency1, currency0) + result_list = [x for x in self.poolkeys_list if c0 == x.currency0 and c1 == x.currency1] + return result_list + +