bnpl
Version:
The smart contracts for bnpl
459 lines • 188 kB
JSON
{
"address": "0x2E363baC1401ca138E0992f9b24f3ba8CE72A024",
"abi": [
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "CallerIsNotNewPotentialOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "CallerIsNotOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "ChannelOutOfRange",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "ConduitAlreadyExists",
"type": "error"
},
{
"inputs": [],
"name": "InvalidCreator",
"type": "error"
},
{
"inputs": [],
"name": "InvalidInitialOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"internalType": "address",
"name": "newPotentialOwner",
"type": "address"
}
],
"name": "NewPotentialOwnerAlreadySet",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "NewPotentialOwnerIsZeroAddress",
"type": "error"
},
{
"inputs": [],
"name": "NoConduit",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "NoPotentialOwnerCurrentlySet",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "conduitKey",
"type": "bytes32"
}
],
"name": "NewConduit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "newPotentialOwner",
"type": "address"
}
],
"name": "PotentialOwnerUpdated",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "cancelOwnershipTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "conduitKey",
"type": "bytes32"
},
{
"internalType": "address",
"name": "initialOwner",
"type": "address"
}
],
"name": "createConduit",
"outputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"internalType": "uint256",
"name": "channelIndex",
"type": "uint256"
}
],
"name": "getChannel",
"outputs": [
{
"internalType": "address",
"name": "channel",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"internalType": "address",
"name": "channel",
"type": "address"
}
],
"name": "getChannelStatus",
"outputs": [
{
"internalType": "bool",
"name": "isOpen",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "getChannels",
"outputs": [
{
"internalType": "address[]",
"name": "channels",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "conduitKey",
"type": "bytes32"
}
],
"name": "getConduit",
"outputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"internalType": "bool",
"name": "exists",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getConduitCodeHashes",
"outputs": [
{
"internalType": "bytes32",
"name": "creationCodeHash",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "runtimeCodeHash",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "getKey",
"outputs": [
{
"internalType": "bytes32",
"name": "conduitKey",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "getPotentialOwner",
"outputs": [
{
"internalType": "address",
"name": "potentialOwner",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "getTotalChannels",
"outputs": [
{
"internalType": "uint256",
"name": "totalChannels",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
}
],
"name": "ownerOf",
"outputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"internalType": "address",
"name": "newPotentialOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "conduit",
"type": "address"
},
{
"internalType": "address",
"name": "channel",
"type": "address"
},
{
"internalType": "bool",
"name": "isOpen",
"type": "bool"
}
],
"name": "updateChannel",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"transactionHash": "0x4f57d6d18ea5bddd82bb2c98cceb8e7680dd1eef7a644067faf724fe4b8fb53d",
"receipt": {
"to": null,
"from": "0xf90E1475dbA948Fec94Be2b83661d8381C900d91",
"contractAddress": "0x2E363baC1401ca138E0992f9b24f3ba8CE72A024",
"transactionIndex": 0,
"gasUsed": "2669867",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"blockHash": "0x57023fac7c34b8ee31ee2c4cceb52345f47e7c05f8fa2c471b61582890dafa57",
"transactionHash": "0x4f57d6d18ea5bddd82bb2c98cceb8e7680dd1eef7a644067faf724fe4b8fb53d",
"logs": [],
"blockNumber": 1,
"cumulativeGasUsed": "2669867",
"status": 1,
"byzantium": true
},
"args": [],
"numDeployments": 1,
"solcInputHash": "302b5978d8e83675d9e89513cc8bfe9e",
"metadata": "{\"compiler\":{\"version\":\"0.8.14+commit.80d49f37\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"CallerIsNotNewPotentialOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"CallerIsNotOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"ChannelOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"ConduitAlreadyExists\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidCreator\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInitialOwner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"newPotentialOwner\",\"type\":\"address\"}],\"name\":\"NewPotentialOwnerAlreadySet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"NewPotentialOwnerIsZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoConduit\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"NoPotentialOwnerCurrentlySet\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"conduitKey\",\"type\":\"bytes32\"}],\"name\":\"NewConduit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newPotentialOwner\",\"type\":\"address\"}],\"name\":\"PotentialOwnerUpdated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"cancelOwnershipTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"conduitKey\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"}],\"name\":\"createConduit\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"channelIndex\",\"type\":\"uint256\"}],\"name\":\"getChannel\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"channel\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"channel\",\"type\":\"address\"}],\"name\":\"getChannelStatus\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"isOpen\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"getChannels\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"channels\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"conduitKey\",\"type\":\"bytes32\"}],\"name\":\"getConduit\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"exists\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConduitCodeHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"creationCodeHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"runtimeCodeHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"getKey\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"conduitKey\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"getPotentialOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"potentialOwner\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"getTotalChannels\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"totalChannels\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"newPotentialOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"conduit\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"channel\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"isOpen\",\"type\":\"bool\"}],\"name\":\"updateChannel\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"0age\",\"errors\":{\"CallerIsNotNewPotentialOwner(address)\":[{\"details\":\"Revert with an error when attempting to claim ownership of a conduit with a caller that is not the current potential owner for the conduit in question.\"}],\"CallerIsNotOwner(address)\":[{\"details\":\"Revert with an error when attempting to update channels or transfer ownership of a conduit when the caller is not the owner of the conduit in question.\"}],\"ChannelOutOfRange(address)\":[{\"details\":\"Revert with an error when attempting to retrieve a channel using an index that is out of range.\"}],\"ConduitAlreadyExists(address)\":[{\"details\":\"Revert with an error when attempting to create a conduit that already exists.\"}],\"InvalidCreator()\":[{\"details\":\"Revert with an error when attempting to create a new conduit using a conduit key where the first twenty bytes of the key do not match the address of the caller.\"}],\"InvalidInitialOwner()\":[{\"details\":\"Revert with an error when attempting to create a new conduit when no initial owner address is supplied.\"}],\"NewPotentialOwnerAlreadySet(address,address)\":[{\"details\":\"Revert with an error when attempting to set a new potential owner that is already set.\"}],\"NewPotentialOwnerIsZeroAddress(address)\":[{\"details\":\"Revert with an error when attempting to register a new potential owner and supplying the null address.\"}],\"NoConduit()\":[{\"details\":\"Revert with an error when attempting to interact with a conduit that does not yet exist.\"}],\"NoPotentialOwnerCurrentlySet(address)\":[{\"details\":\"Revert with an error when attempting to cancel ownership transfer when no new potential owner is currently set.\"}]},\"kind\":\"dev\",\"methods\":{\"acceptOwnership(address)\":{\"params\":{\"conduit\":\"The conduit for which to accept ownership.\"}},\"cancelOwnershipTransfer(address)\":{\"params\":{\"conduit\":\"The conduit for which to cancel ownership transfer.\"}},\"constructor\":{\"details\":\"Initialize contract by deploying a conduit and setting the creation code and runtime code hashes as immutable arguments.\"},\"createConduit(bytes32,address)\":{\"params\":{\"conduitKey\":\"The conduit key used to deploy the conduit. Note that the first twenty bytes of the conduit key must match the caller of this contract.\",\"initialOwner\":\"The initial owner to set for the new conduit.\"},\"returns\":{\"conduit\":\"The address of the newly deployed conduit.\"}},\"getChannel(address,uint256)\":{\"params\":{\"channelIndex\":\"The index of the channel in question.\",\"conduit\":\"The conduit for which to retrieve the open channel.\"},\"returns\":{\"channel\":\"The open channel, if any, at the specified channel index.\"}},\"getChannelStatus(address,address)\":{\"params\":{\"channel\":\"The channel for which to retrieve the status.\",\"conduit\":\"The conduit for which to retrieve the channel status.\"},\"returns\":{\"isOpen\":\"The status of the channel on the given conduit.\"}},\"getChannels(address)\":{\"params\":{\"conduit\":\"The conduit for which to retrieve open channels.\"},\"returns\":{\"channels\":\"An array of open channels on the given conduit.\"}},\"getConduit(bytes32)\":{\"params\":{\"conduitKey\":\"The conduit key used to derive the conduit.\"},\"returns\":{\"conduit\":\"The derived address of the conduit.\",\"exists\":\" A boolean indicating whether the derived conduit has been deployed or not.\"}},\"getConduitCodeHashes()\":{\"details\":\"Retrieve the conduit creation code and runtime code hashes.\"},\"getKey(address)\":{\"params\":{\"conduit\":\"The conduit for which to retrieve the associated conduit key.\"},\"returns\":{\"conduitKey\":\"The conduit key used to deploy the supplied conduit.\"}},\"getPotentialOwner(address)\":{\"params\":{\"conduit\":\"The conduit for which to retrieve the potential owner.\"},\"returns\":{\"potentialOwner\":\"The potential owner, if any, for the conduit.\"}},\"getTotalChannels(address)\":{\"params\":{\"conduit\":\"The conduit for which to retrieve the total channel count.\"},\"returns\":{\"totalChannels\":\"The total number of open channels for the conduit.\"}},\"ownerOf(address)\":{\"params\":{\"conduit\":\"The conduit for which to retrieve the associated owner.\"},\"returns\":{\"owner\":\"The owner of the supplied conduit.\"}},\"transferOwnership(address,address)\":{\"params\":{\"conduit\":\"The conduit for which to initiate ownership transfer.\",\"newPotentialOwner\":\"The new potential owner of the conduit.\"}},\"updateChannel(address,address,bool)\":{\"params\":{\"channel\":\"The channel to open or close on the conduit.\",\"conduit\":\"The conduit for which to open or close the channel.\",\"isOpen\":\"A boolean indicating whether to open or close the channel.\"}}},\"title\":\"ConduitController\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"acceptOwnership(address)\":{\"notice\":\"Accept ownership of a supplied conduit. Only accounts that the current owner has set as the new potential owner may call this function.\"},\"cancelOwnershipTransfer(address)\":{\"notice\":\"Clear the currently set potential owner, if any, from a conduit. Only the owner of the conduit in question may call this function.\"},\"createConduit(bytes32,address)\":{\"notice\":\"Deploy a new conduit using a supplied conduit key and assigning an initial owner for the deployed conduit. Note that the first twenty bytes of the supplied conduit key must match the caller and that a new conduit cannot be created if one has already been deployed using the same conduit key.\"},\"getChannel(address,uint256)\":{\"notice\":\"Retrieve an open channel at a specific index for a given conduit. Note that the index of a channel can change as a result of other channels being closed on the conduit.\"},\"getChannelStatus(address,address)\":{\"notice\":\"Retrieve the status (either open or closed) of a given channel on a conduit.\"},\"getChannels(address)\":{\"notice\":\"Retrieve all open channels for a given conduit. Note that calling this function for a conduit with many channels will revert with an out-of-gas error.\"},\"getConduit(bytes32)\":{\"notice\":\"Derive the conduit associated with a given conduit key and determine whether that conduit exists (i.e. whether it has been deployed).\"},\"getKey(address)\":{\"notice\":\"Retrieve the conduit key for a deployed conduit via reverse lookup.\"},\"getPotentialOwner(address)\":{\"notice\":\"Retrieve the potential owner, if any, for a given conduit. The current owner may set a new potential owner via `transferOwnership` and that owner may then accept ownership of the conduit in question via `acceptOwnership`.\"},\"getTotalChannels(address)\":{\"notice\":\"Retrieve the total number of open channels for a given conduit.\"},\"ownerOf(address)\":{\"notice\":\"Retrieve the current owner of a deployed conduit.\"},\"transferOwnership(address,address)\":{\"notice\":\"Initiate conduit ownership transfer by assigning a new potential owner for the given conduit. Once set, the new potential owner may call `acceptOwnership` to claim ownership of the conduit. Only the owner of the conduit in question may call this function.\"},\"updateChannel(address,address,bool)\":{\"notice\":\"Open or close a channel on a given conduit, thereby allowing the specified account to execute transfers against that conduit. Extreme care must be taken when updating channels, as malicious or vulnerable channels can transfer any ERC20, ERC721 and ERC1155 tokens where the token holder has granted the conduit approval. Only the owner of the conduit in question may call this function.\"}},\"notice\":\"ConduitController enables deploying and managing new conduits, or contracts that allow registered callers (or open \\\"channels\\\") to transfer approved ERC20/721/1155 tokens on their behalf.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/conduit/ConduitController.sol\":\"ConduitController\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/conduit/Conduit.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.7;\\n\\nimport { ConduitInterface } from \\\"../interfaces/ConduitInterface.sol\\\";\\n\\nimport { ConduitItemType } from \\\"./lib/ConduitEnums.sol\\\";\\n\\nimport { TokenTransferrer } from \\\"../lib/TokenTransferrer.sol\\\";\\n\\nimport {\\n ConduitTransfer,\\n ConduitBatch1155Transfer\\n} from \\\"./lib/ConduitStructs.sol\\\";\\n\\nimport \\\"./lib/ConduitConstants.sol\\\";\\n\\n/**\\n * @title Conduit\\n * @author 0age\\n * @notice This contract serves as an originator for \\\"proxied\\\" transfers. Each\\n * conduit is deployed and controlled by a \\\"conduit controller\\\" that can\\n * add and remove \\\"channels\\\" or contracts that can instruct the conduit\\n * to transfer approved ERC20/721/1155 tokens. *IMPORTANT NOTE: each\\n * conduit has an owner that can arbitrarily add or remove channels, and\\n * a malicious or negligent owner can add a channel that allows for any\\n * approved ERC20/721/1155 tokens to be taken immediately \\u2014 be extremely\\n * cautious with what conduits you give token approvals to!*\\n */\\ncontract Conduit is ConduitInterface, TokenTransferrer {\\n // Set deployer as an immutable controller that can update channel statuses.\\n address private immutable _controller;\\n\\n // Track the status of each channel.\\n mapping(address => bool) private _channels;\\n\\n /**\\n * @notice Ensure that the caller is currently registered as an open channel\\n * on the conduit.\\n */\\n modifier onlyOpenChannel() {\\n // Utilize assembly to access channel storage mapping directly.\\n assembly {\\n // Write the caller to scratch space.\\n mstore(ChannelKey_channel_ptr, caller())\\n\\n // Write the storage slot for _channels to scratch space.\\n mstore(ChannelKey_slot_ptr, _channels.slot)\\n\\n // Derive the position in storage of _channels[msg.sender]\\n // and check if the stored value is zero.\\n if iszero(\\n sload(keccak256(ChannelKey_channel_ptr, ChannelKey_length))\\n ) {\\n // The caller is not an open channel; revert with\\n // ChannelClosed(caller). First, set error signature in memory.\\n mstore(ChannelClosed_error_ptr, ChannelClosed_error_signature)\\n\\n // Next, set the caller as the argument.\\n mstore(ChannelClosed_channel_ptr, caller())\\n\\n // Finally, revert, returning full custom error with argument.\\n revert(ChannelClosed_error_ptr, ChannelClosed_error_length)\\n }\\n }\\n\\n // Continue with function execution.\\n _;\\n }\\n\\n /**\\n * @notice In the constructor, set the deployer as the controller.\\n */\\n constructor() {\\n // Set the deployer as the controller.\\n _controller = msg.sender;\\n }\\n\\n /**\\n * @notice Execute a sequence of ERC20/721/1155 transfers. Only a caller\\n * with an open channel can call this function. Note that channels\\n * are expected to implement reentrancy protection if desired, and\\n * that cross-channel reentrancy may be possible if the conduit has\\n * multiple open channels at once. Also note that channels are\\n * expected to implement checks against transferring any zero-amount\\n * items if that constraint is desired.\\n *\\n * @param transfers The ERC20/721/1155 transfers to perform.\\n *\\n * @return magicValue A magic value indicating that the transfers were\\n * performed successfully.\\n */\\n function execute(ConduitTransfer[] calldata transfers)\\n external\\n override\\n onlyOpenChannel\\n returns (bytes4 magicValue)\\n {\\n // Retrieve the total number of transfers and place on the stack.\\n uint256 totalStandardTransfers = transfers.length;\\n\\n // Iterate over each transfer.\\n for (uint256 i = 0; i < totalStandardTransfers; ) {\\n // Retrieve the transfer in question and perform the transfer.\\n _transfer(transfers[i]);\\n\\n // Skip overflow check as for loop is indexed starting at zero.\\n unchecked {\\n ++i;\\n }\\n }\\n\\n // Return a magic value indicating that the transfers were performed.\\n magicValue = this.execute.selector;\\n }\\n\\n /**\\n * @notice Execute a sequence of batch 1155 item transfers. Only a caller\\n * with an open channel can call this function. Note that channels\\n * are expected to implement reentrancy protection if desired, and\\n * that cross-channel reentrancy may be possible if the conduit has\\n * multiple open channels at once. Also note that channels are\\n * expected to implement checks against transferring any zero-amount\\n * items if that constraint is desired.\\n *\\n * @param batchTransfers The 1155 batch item transfers to perform.\\n *\\n * @return magicValue A magic value indicating that the item transfers were\\n * performed successfully.\\n */\\n function executeBatch1155(\\n ConduitBatch1155Transfer[] calldata batchTransfers\\n ) external override onlyOpenChannel returns (bytes4 magicValue) {\\n // Perform 1155 batch transfers. Note that memory should be considered\\n // entirely corrupted from this point forward.\\n _performERC1155BatchTransfers(batchTransfers);\\n\\n // Return a magic value indicating that the transfers were performed.\\n magicValue = this.executeBatch1155.selector;\\n }\\n\\n /**\\n * @notice Execute a sequence of transfers, both single ERC20/721/1155 item\\n * transfers as well as batch 1155 item transfers. Only a caller\\n * with an open channel can call this function. Note that channels\\n * are expected to implement reentrancy protection if desired, and\\n * that cross-channel reentrancy may be possible if the conduit has\\n * multiple open channels at once. Also note that channels are\\n * expected to implement checks against transferring any zero-amount\\n * items if that constraint is desired.\\n *\\n * @param standardTransfers The ERC20/721/1155 item transfers to perform.\\n * @param batchTransfers The 1155 batch item transfers to perform.\\n *\\n * @return magicValue A magic value indicating that the item transfers were\\n * performed successfully.\\n */\\n function executeWithBatch1155(\\n ConduitTransfer[] calldata standardTransfers,\\n ConduitBatch1155Transfer[] calldata batchTransfers\\n ) external override onlyOpenChannel returns (bytes4 magicValue) {\\n // Retrieve the total number of transfers and place on the stack.\\n uint256 totalStandardTransfers = standardTransfers.length;\\n\\n // Iterate over each standard transfer.\\n for (uint256 i = 0; i < totalStandardTransfers; ) {\\n // Retrieve the transfer in question and perform the transfer.\\n _transfer(standardTransfers[i]);\\n\\n // Skip overflow check as for loop is indexed starting at zero.\\n unchecked {\\n ++i;\\n }\\n }\\n\\n // Perform 1155 batch transfers. Note that memory should be considered\\n // entirely corrupted from this point forward aside from the free memory\\n // pointer having the default value.\\n _performERC1155BatchTransfers(batchTransfers);\\n\\n // Return a magic value indicating that the transfers were performed.\\n magicValue = this.executeWithBatch1155.selector;\\n }\\n\\n /**\\n * @notice Open or close a given channel. Only callable by the controller.\\n *\\n * @param channel The channel to open or close.\\n * @param isOpen The status of the channel (either open or closed).\\n */\\n function updateChannel(address channel, bool isOpen) external override {\\n // Ensure that the caller is the controller of this contract.\\n if (msg.sender != _controller) {\\n revert InvalidController();\\n }\\n\\n // Ensure that the channel does not already have the indicated status.\\n if (_channels[channel] == isOpen) {\\n revert ChannelStatusAlreadySet(channel, isOpen);\\n }\\n\\n // Update the status of the channel.\\n _channels[channel] = isOpen;\\n\\n // Emit a corresponding event.\\n emit ChannelUpdated(channel, isOpen);\\n }\\n\\n /**\\n * @dev Internal function to transfer a given ERC20/721/1155 item. Note that\\n * channels are expected to implement checks against transferring any\\n * zero-amount items if that constraint is desired.\\n *\\n * @param item The ERC20/721/1155 item to transfer.\\n */\\n function _transfer(ConduitTransfer calldata item) internal {\\n // Determine the transfer method based on the respective item type.\\n if (item.itemType == ConduitItemType.ERC20) {\\n // Transfer ERC20 token. Note that item.identifier is ignored and\\n // therefore ERC20 transfer items are potentially malleable \\u2014 this\\n // check should be performed by the calling channel if a constraint\\n // on item malleability is desired.\\n _performERC20Transfer(item.token, item.from, item.to, item.amount);\\n } else if (item.itemType == ConduitItemType.ERC721) {\\n // Ensure that exactly one 721 item is being transferred.\\n if (item.amount != 1) {\\n revert InvalidERC721TransferAmount();\\n }\\n\\n // Transfer ERC721 token.\\n _performERC721Transfer(\\n item.token,\\n item.from,\\n item.to,\\n item.identifier\\n );\\n } else if (item.itemType == ConduitItemType.ERC1155) {\\n // Transfer ERC1155 token.\\n _performERC1155Transfer(\\n item.token,\\n item.from,\\n item.to,\\n item.identifier,\\n item.amount\\n );\\n } else {\\n // Throw with an error.\\n revert InvalidItemType();\\n }\\n }\\n}\\n\",\"keccak256\":\"0x180267c5f93666446ffc93c9798dc52339e94d101b2e67a3eab229d366c873d6\",\"license\":\"MIT\"},\"contracts/conduit/ConduitController.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.7;\\n\\nimport {\\n ConduitControllerInterface\\n} from \\\"../interfaces/ConduitControllerInterface.sol\\\";\\n\\nimport { ConduitInterface } from \\\"../interfaces/ConduitInterface.sol\\\";\\n\\nimport { Conduit } from \\\"./Conduit.sol\\\";\\n\\n/**\\n * @title ConduitController\\n * @author 0age\\n * @notice ConduitController enables deploying and managing new conduits, or\\n * contracts that allow registered callers (or open \\\"channels\\\") to\\n * transfer approved ERC20/721/1155 tokens on their behalf.\\n */\\ncontract ConduitController is ConduitControllerInterface {\\n // Register keys, owners, new potential owners, and channels by conduit.\\n mapping(address => ConduitProperties) internal _conduits;\\n\\n // Set conduit creation code and runtime code hashes as immutable arguments.\\n bytes32 internal immutable _CONDUIT_CREATION_CODE_HASH;\\n bytes32 internal immutable _CONDUIT_RUNTIME_CODE_HASH;\\n\\n /**\\n * @dev Initialize contract by deploying a conduit and setting the creation\\n * code and runtime code hashes as immutable arguments.\\n */\\n constructor() {\\n // Derive the conduit creation code hash and set it as an immutable.\\n _CONDUIT_CREATION_CODE_HASH = keccak256(type(Conduit).creationCode);\\n\\n // Deploy a conduit with the zero hash as the salt.\\n Conduit zeroConduit = new Conduit{ salt: bytes32(0) }();\\n\\n // Retrieve the conduit runtime code hash and set it as an immutable.\\n _CONDUIT_RUNTIME_CODE_HASH = address(zeroConduit).codehash;\\n }\\n\\n /**\\n * @notice Deploy a new conduit using a supplied conduit key and assigning\\n * an initial owner for the deployed conduit. Note that the first\\n * twenty bytes of the supplied conduit key must match the caller\\n * and that a new conduit cannot be created if one has already been\\n * deployed using the same conduit key.\\n *\\n * @param conduitKey The conduit key used to deploy the conduit. Note that\\n * the first twenty bytes of the conduit key must match\\n * the caller of this contract.\\n * @param initialOwner The initial owner to set for the new conduit.\\n *\\n * @return conduit The address of the newly deployed conduit.\\n */\\n function createConduit(bytes32 conduitKey, address initialOwner)\\n external\\n override\\n returns (address conduit)\\n {\\n // Ensure that an initial owner has been supplied.\\n if (initialOwner == address(0)) {\\n revert InvalidInitialOwner();\\n }\\n\\n // If the first 20 bytes of the conduit key do not match the caller...\\n if (address(uint160(bytes20(conduitKey))) != msg.sender) {\\n // Revert with an error indicating that the creator is invalid.\\n revert InvalidCreator();\\n }\\n\\n // Derive address from deployer, conduit key and creation code hash.\\n conduit = address(\\n uint160(\\n uint256(\\n keccak256(\\n abi.encodePacked(\\n bytes1(0xff),\\n address(this),\\n conduitKey,\\n _CONDUIT_CREATION_CODE_HASH\\n )\\n )\\n )\\n )\\n );\\n\\n // If derived conduit exists, as evidenced by comparing runtime code...\\n if (conduit.codehash == _CONDUIT_RUNTIME_CODE_HASH) {\\n // Revert with an error indicating that the conduit already exists.\\n revert ConduitAlreadyExists(conduit);\\n }\\n\\n // Deploy the conduit via CREATE2 using the conduit key as the salt.\\n new Conduit{ salt: conduitKey }();\\n\\n // Initialize storage variable referencing conduit properties.\\n ConduitProperties storage conduitProperties = _conduits[conduit];\\n\\n // Set the supplied initial owner as the owner of the conduit.\\n conduitProperties.owner = initialOwner;\\n\\n // Set conduit key used to deploy the conduit to enable reverse lookup.\\n conduitProperties.key = conduitKey;\\n\\n // Emit an event indicating that the conduit has been deployed.\\n emit NewConduit(conduit, conduitKey);\\n\\n // Emit an event indicating that conduit ownership has been assigned.\\n emit OwnershipTransferred(conduit, address(0), initialOwner);\\n }\\n\\n /**\\n * @notice Open or close a channel on a given conduit, thereby allowing the\\n * specified account to execute transfers against that conduit.\\n * Extreme care must be taken when updating channels, as malicious\\n * or vulnerable channels can transfer any ERC20, ERC721 and ERC1155\\n * tokens where the token holder has granted the conduit approval.\\n * Only the owner of the conduit in question may call this function.\\n *\\n * @param conduit The conduit for which to open or close the channel.\\n * @param channel The channel to open or close on the conduit.\\n * @param isOpen A boolean indicating whether to open or close the channel.\\n */\\n function updateChannel(\\n address conduit,\\n address channel,\\n bool isOpen\\n ) external override {\\n // Ensure the caller is the current owner of the conduit in question.\\n _assertCallerIsConduitOwner(conduit);\\n\\n // Call the conduit, updating the channel.\\n ConduitInterface(conduit).updateChannel(channel, isOpen);\\n\\n // Retrieve storage region where channels for the conduit are tracked.\\n ConduitProperties storage conduitProperties = _conduits[conduit];\\n\\n // Retrieve the index, if one currently exists, for the updated channel.\\n uint256 channelIndexPlusOne = (\\n conduitProperties.channelIndexesPlusOne[channel]\\n );\\n\\n // Determine whether the updated channel is already tracked as open.\\n bool channelPreviouslyOpen = channelIndexPlusOne != 0;\\n\\n // If the channel has been set to open and was previously closed...\\n if (isOpen && !channelPreviouslyOpen) {\\n // Add the channel to the channels array for the conduit.\\n conduitProperties.channels.push(channel);\\n\\n // Add new open channel length to associated mapping as index + 1.\\n conduitProperties.channelIndexesPlusOne[channel] = (\\n conduitProperties.channels.length\\n );\\n } else if (!isOpen && channelPreviouslyOpen) {\\n // Set a previously open channel as closed via \\\"swap & pop\\\" method.\\n // Decrement located index to get the index of the closed channel.\\n uint256 removedChannelIndex;\\n\\n // Skip underflow check as channelPreviouslyOpen being true ensures\\n // that channelIndexPlusOne is nonzero.\\n unchecked {\\n removedChannelIndex = channelIndexPlusOne - 1;\\n }\\n\\n // Use length of channels array to determine index of last channel.\\n uint256 finalChannelIndex = conduitProperties.channels.length - 1;\\n\\n // If closed channel is not last channel in the channels array...\\n if (finalChannelIndex != removedChannelIndex) {\\n // Retrieve the final channel and place the value on the stack.\\n address finalChannel = (\\n conduitProperties.channels[finalChannelIndex]\\n );\\n\\n // Overwrite the removed channel using the final channel value.\\n conduitProperties.channels[removedChannelIndex] = finalChannel;\\n\\n // Update final index in associated mapping to removed index.\\n conduitProperties.channelIndexesPlusOne[finalChannel] = (\\n channelIndexPlusOne\\n );\\n }\\n\\n // Remove the last channel from the channels array for the conduit.\\n conduitProperties.channels.pop();\\n\\n // Remove the closed channel from associated mapping of indexes.\\n delete conduitProperties.channelIndexesPlusOne[channel];\\n }\\n }\\n\\n /**\\n * @notice Initiate conduit ownership transfer by assigning a new potential\\n * owner for the given conduit. Once set, the new potential owner\\n * may call `acceptOwnership` to claim ownership of the conduit.\\n * Only the owner of the conduit in question may call this function.\\n *\\n * @param conduit The conduit for which to initiate ownership transfer.\\n * @param newPotentialOwner The new potential owner of the conduit.\\n */\\n function transferOwnership(address conduit, address newPotentialOwner)\\n external\\n override\\n {\\n // Ensure the caller is the current owner of the conduit in question.\\n _assertCallerIsConduitOwner(conduit);\\n\\n // Ensure the new potential owner is not an invalid address.\\n if (newPotentialOwner == address(0)) {\\n revert NewPotentialOwnerIsZeroAddress(conduit);\\n }\\n\\n // Ensure the new potential owner is not already set.\\n if (newPotentialOwner == _conduits[conduit].potentialOwner) {\\n revert NewPotentialOwnerAlreadySet(conduit, newPotentialOwner);\\n }\\n\\n // Emit an event indicating that the potential owner has been updated.\\n emit PotentialOwnerUpdated(newPotentialOwner);\\n\\n // Set the new potential owner as the potential owner of the conduit.\\n _conduits[conduit].potentialOwner = newPotentialOwner;\\n }\\n\\n /**\\n * @notice Clear the currently set potential owner, if any, from a conduit.\\n * Only the owner of the conduit in question may call this function.\\n *\\n * @param conduit The conduit for which to cancel ownership transfer.\\n */\\n function cancelOwnershipTransfer(address conduit) external override {\\n // Ensure the caller is the current owner of the conduit in question.\\n _assertCallerIsConduitOwner(conduit);\\n\\n // Ensure that ownership transfer is currently possible.\\n if (_conduits[conduit].potentialOwner == address(0)) {\\n revert NoPotentialOwnerCurrentlySet(conduit);\\n }\\n\\n // Emit an event indicating that the potential owner has been cleared.\\n emit PotentialOwnerUpdated(address(0));\\n\\n // Clear the current new potential owner from the conduit.\\n _conduits[conduit].potentialOwner = address(0);\\n }\\n\\n /**\\n * @notice Accept ownership of a supplied conduit. Only accounts that the\\n * current owner has set as the new potential owner may call this\\n * function.\\n *\\n * @param conduit The conduit for which to accept ownership.\\n */\\n function acceptOwnership(address conduit) external override {\\n // Ensure that the conduit in question exists.\\n _assertConduitExists(conduit);\\n\\n // If caller does not match current potential owner of the conduit...\\n if (msg.sender != _conduits[conduit].potentialOwner) {\\n // Revert, indicating that caller is not current potential owner.\\n revert CallerIsNotNewPotentialOwner(conduit);\\n }\\n\\n // Emit an event indicating that the potential owner has been cleared.\\n emit PotentialOwnerUpdated(address(0));\\n\\n // Clear the current new potential owner from the conduit.\\n _conduits[conduit].potentialOwner = address(0);\\n\\n // Emit an event indicating conduit ownership has been transferred.\\n emit OwnershipTransferred(\\n conduit,\\n _conduits[conduit].owner,\\n msg.sender\\n );\\n\\n // Set the caller as the owner of the conduit.\\n _conduits[conduit].owner = msg.sender;\\n }\\n\\n /**\\n * @notice Retrieve the current owner of a deployed conduit.\\n *\\n * @param conduit The conduit for which to retrieve the associated owner.\\n *\\n * @return owner The owner of the supplied conduit.\\n */\\n function ownerOf(address conduit)\\n external\\n view\\n override\\n returns (address owner)\\n {\\n // Ensure that the conduit in question exists.\\n _assertConduitExists(conduit);\\n\\n // Retrieve the current owner of the conduit in question.\\n owner = _conduits[conduit].owner;\\n }\\n\\n /**\\n * @notice Retrieve the conduit key for a deployed conduit via reverse\\n * lookup.\\n *\\n * @param conduit The conduit for which to retrieve the associated conduit\\n * key.\\n *\\n * @return conduitKey The conduit key used to deploy the supplied conduit.\\n */\\n function getKey(address conduit)\\n external\\n view\\n override\\n returns (bytes32 conduitKey)\\n {\\n // Attempt to retrieve a conduit key for the conduit in question.\\n conduitKey = _conduits[conduit].key;\\n\\n // Revert if no conduit key was located.\\n if (conduitKey == bytes32(0)) {\\n revert NoConduit();\\n }\\n }\\n\\n /**\\n * @notice Derive the conduit associated with a given conduit key and\\n * determine whether that conduit exists (i.e. whether it has been\\n * deployed).\\n *\\n * @param conduitKey The conduit key used to derive the conduit.\\n *\\n * @return conduit The derived address of the conduit.\\n