├── .editorconfig ├── .env.example ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .openzeppelin ├── .gitkeep ├── goerli.json ├── mainnet.json └── sepolia.json ├── .prettierignore ├── .prettierrc ├── .solhint.json ├── .solhintignore ├── .vscode └── settings.json ├── GUIDE.md ├── LICENSE ├── Makefile ├── README.md ├── audits ├── NFR Audits - July 2023.pdf └── NFR Audits - May 2023.pdf ├── dependencies.sh ├── docs ├── .gitignore ├── book.css ├── book.toml ├── solidity.min.js └── src │ ├── README.md │ ├── SUMMARY.md │ └── src │ ├── CustomPaymentSplitter.sol │ └── contract.PaymentSplitter.md │ ├── CustomUUPSUpgradeable.sol │ └── abstract.UUPSUpgradeable.md │ ├── IOrb.sol │ └── interface.IOrb.md │ ├── IOrbInvocationRegistry.sol │ └── interface.IOrbInvocationRegistry.md │ ├── IOwnershipTransferrable.sol │ └── interface.IOwnershipTransferrable.md │ ├── Orb.sol │ └── contract.Orb.md │ ├── OrbInvocationRegistry.sol │ └── contract.OrbInvocationRegistry.md │ ├── OrbInvocationRegistryV2.sol │ ├── contract.OrbInvocationRegistryV2.md │ └── struct.LateResponseReceipt.md │ ├── OrbInvocationTipJar.sol │ └── contract.OrbInvocationTipJar.md │ ├── OrbPond.sol │ ├── contract.OrbPond.md │ └── interface.IOwnershipTransferrable.md │ ├── OrbPondV2.sol │ ├── contract.OrbPondV2.md │ └── interface.IOwnershipTransferrable.md │ ├── OrbV2.sol │ └── contract.OrbV2.md │ ├── README.md │ ├── interfaces │ ├── IOrb.sol │ │ └── interface.IOrb.md │ ├── IOrbInvocationRegistry.sol │ │ └── interface.IOrbInvocationRegistry.md │ └── README.md │ └── test-upgrades │ ├── OrbInvocationRegistryTestUpgrade.sol │ └── contract.OrbInvocationRegistryTestUpgrade.md │ ├── OrbInvocationTipJarTestUpgrade.sol │ └── contract.OrbInvocationTipJarTestUpgrade.md │ ├── OrbPondTestUpgrade.sol │ └── contract.OrbPondTestUpgrade.md │ ├── OrbTestUpgrade.sol │ └── contract.OrbTestUpgrade.md │ └── README.md ├── foundry.toml ├── hardhat.config.ts ├── package.json ├── pnpm-lock.yaml ├── remappings.txt ├── script ├── LocalDeployBase.s.sol ├── LocalDeployOrb.s.sol ├── contract-setup.sh ├── deploy-OrbImplementation.ts ├── deploy-OrbInvocationRegistry.ts ├── deploy-OrbInvocationTipJar.ts ├── deploy-OrbPond.ts ├── deploy-OrbPondV2Implementation.ts ├── deploy-OrbV2Implementation.ts ├── deploy-PaymentSplitterImplementation.ts ├── log-versionUpgradeCalls.ts ├── sepolia-setup.sh ├── transferOwnership-OrbInvocationRegistry.ts ├── transferOwnership-OrbInvocationTipJar.ts ├── transferOwnership-OrbPond.ts ├── verify-Orb.ts ├── verify-OrbImplementation.ts ├── verify-OrbInvocationRegistryImplementation.ts ├── verify-OrbInvocationTipJarImplementation.ts ├── verify-OrbPondImplementation.ts ├── verify-OrbPondV2Implementation.ts ├── verify-OrbV2Implementation.ts └── verify-PaymentSplitterImplementation.ts ├── src ├── CustomPaymentSplitter.sol ├── CustomUUPSUpgradeable.sol ├── IOwnershipTransferrable.sol ├── Orb.sol ├── OrbInvocationRegistry.sol ├── OrbInvocationTipJar.sol ├── OrbPond.sol ├── OrbPondV2.sol ├── OrbV1Renamed.sol ├── OrbV2.sol ├── interfaces │ ├── IOrb.sol │ └── IOrbInvocationRegistry.sol └── test-upgrades │ ├── OrbInvocationRegistryTestUpgrade.sol │ ├── OrbInvocationTipJarTestUpgrade.sol │ ├── OrbPondTestUpgrade.sol │ └── OrbTestUpgrade.sol ├── test ├── OrbInvocationRegistry │ ├── ExternalCallee.sol │ └── OrbInvocationRegistry.t.sol ├── OrbInvocationTipJar │ └── OrbInvocationTipJar.t.sol ├── OrbPondV1 │ └── OrbPondV1.t.sol ├── OrbPondV2 │ └── OrbPondV2.t.sol ├── OrbV1 │ ├── OrbV1-Auction.t.sol │ ├── OrbV1-Forfeit.t.sol │ ├── OrbV1-Funding.t.sol │ ├── OrbV1-Purchase.t.sol │ ├── OrbV1-Settings.t.sol │ ├── OrbV1-Upgrade.t.sol │ ├── OrbV1.t.sol │ └── OrbV1Harness.sol ├── OrbV2 │ ├── OrbV2-Auction.t.sol │ ├── OrbV2-Forfeit.t.sol │ ├── OrbV2-Funding.t.sol │ ├── OrbV2-Purchase.t.sol │ ├── OrbV2-Settings.t.sol │ ├── OrbV2-Upgrade.t.sol │ ├── OrbV2.t.sol │ └── OrbV2Harness.sol ├── scripts │ └── Deploy.t.sol └── upgrade.test.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | max_line_length = 120 11 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DEPLOYER_PRIVATE_KEY="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" 2 | CREATOR_PRIVATE_KEY="8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba" 3 | 4 | ORB_ADDRESS="0xe082b26cEf079a095147F35c9647eC97c2401B83" 5 | ORB_TIP_JAR_ADDRESS="0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6" 6 | 7 | CMC_API_KEY="" 8 | ETHERSCAN_API_KEY="" 9 | INFURA_API_KEY="" 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | foundry: 11 | strategy: 12 | fail-fast: true 13 | 14 | name: Foundry project 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: recursive 20 | 21 | - name: Install Foundry 22 | uses: foundry-rs/foundry-toolchain@v1 23 | with: 24 | version: nightly 25 | 26 | - name: Run Forge build 27 | run: | 28 | forge --version 29 | forge build --sizes 30 | id: build 31 | 32 | - name: Run Forge tests 33 | run: | 34 | forge test -vvv 35 | id: forge-test 36 | 37 | - name: Run snapshot 38 | run: NO_COLOR=1 forge snapshot >> $GITHUB_STEP_SUMMARY 39 | 40 | hardhat: 41 | strategy: 42 | fail-fast: true 43 | 44 | name: Hardhat project 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v3 48 | with: 49 | submodules: recursive 50 | 51 | - name: Setup Node 18 52 | uses: actions/setup-node@v3 53 | with: 54 | node-version: "18.x" 55 | 56 | - name: Install dependencies 57 | run: npm install 58 | 59 | - name: Run Tests 60 | run: npm test 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | broadcast 5 | coverage.json 6 | typechain 7 | typechain-types 8 | .DS_Store 9 | .deps 10 | 11 | .pnp.* 12 | .yarn/* 13 | !.yarn/patches 14 | !.yarn/plugins 15 | !.yarn/releases 16 | !.yarn/sdks 17 | !.yarn/versions 18 | 19 | # Foundry 20 | cache/ 21 | out/ 22 | lcov.info 23 | 24 | # JS 25 | node_modules/ 26 | 27 | #Hardhat files 28 | cache_hardhat/ 29 | artifacts/ 30 | 31 | #Hardhat plugin files 32 | typechain-types/ 33 | .openzeppelin/unknown-*.json 34 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "lib/openzeppelin-contracts"] 5 | path = lib/openzeppelin-contracts 6 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 7 | [submodule "lib/openzeppelin-contracts-upgradeable"] 8 | path = lib/openzeppelin-contracts-upgradeable 9 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 10 | -------------------------------------------------------------------------------- /.openzeppelin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbland/contracts/cef59450b81c515db5410a91acaf2a678a3a1aab/.openzeppelin/.gitkeep -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | !*.sol 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false, 4 | "singleQuote": false, 5 | "tabWidth": 4, 6 | "printWidth": 120 7 | } -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "max-states-count": "off", 5 | "max-line-length": [ 6 | "error", 7 | 120 8 | ], 9 | "reason-string": [ 10 | "warn", 11 | { 12 | "maxLength": 32 13 | } 14 | ], 15 | "avoid-call-value": "warn", 16 | "compiler-version": [ 17 | "error", 18 | "0.8.20" 19 | ], 20 | "func-visibility": [ 21 | "warn", 22 | { 23 | "ignoreConstructors": true 24 | } 25 | ], 26 | "private-vars-leading-underscore": "off", 27 | "not-rely-on-time": "off" 28 | } 29 | } -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "solidity.remappings": [ 4 | "forge-std/=lib/forge-std/src/", 5 | "ds-test/=lib/forge-std/lib/ds-test/src/", 6 | "openzeppelin-contracts/=lib/openzeppelin-contracts/", 7 | "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", 8 | "src/=src/", 9 | "lib/=lib/" 10 | ], 11 | "solidity.formatter": "forge", 12 | "git.detectSubmodules": false, 13 | "git.enableCommitSigning": true, 14 | "search.exclude": { 15 | ".git": true, 16 | "lib": true, 17 | "cache": true, 18 | "out": true 19 | }, 20 | "solidity.defaultCompiler": "remote", 21 | "solidity.compileUsingRemoteVersion": "v0.8.20+commit.a1b79de6", 22 | } -------------------------------------------------------------------------------- /GUIDE.md: -------------------------------------------------------------------------------- 1 | # Orb Guide 2 | 3 | This is pretty much everything you need to know about setting up your Orb: all the settings, options and requirements. 4 | 5 | ## Orb Creation 6 | 7 | **Your Ethereum address** 8 | 9 | This address will be the Creator of the Orb. You will receive revenue to it, and use it to submit responses. 10 | 11 | It can be changed later by transferring Orb ownership to another wallet, but access cannot be restored if it's lost. You will need to be able to connect to our web3 app with that wallet and sign messages, so ideally your wallet should support Metamask or WalletConnect. 12 | 13 | Your address will show up in Orb history, so if you have an ENS name, set it as a primary name to show up as your ENS, not just a hash. 14 | 15 | **Orb name and symbol** 16 | 17 | We suggest "NameOrb", for example "EricOrb", and "ORB" as symbol. It will show up on Etherscan. 18 | 19 | A nicer name, likes "Eric's Orb" is also needed, for ERC-721 metadata and the Orb UI. 20 | 21 | **Orb beneficiary** 22 | 23 | This will be automatically set for you. It is an contract that receives all Orb proceeds and splits it based on shares set during creation. Shares or beneficiary payees are immutable, and cannot be changed. 24 | 25 | ## Orb Settings 26 | 27 | These settings can be changed later, but only if you (Creator, and not any Keeper) control the Orb. 28 | 29 | ### Auction Settings 30 | 31 | Note: Orb has two types of auctions: Creator auction and Keeper auction. As a Creator, you can start Creator auction for initial sale of the Orb. All the revenue goes to your Orb beneficiary. Keeper auctions can be started by Orb holders who want to give up the Orb and find the next holder: majority of the proceeds from these auction go to the previous Keeper (minus royalty percentage, see below). 32 | 33 | - **Starting price** - minimum initial bid people can make in the Orb auction. Applies to both Creator and Keeper auctions. (In contract set as wei) 34 | - **Minimum bid step** - by at least how much each bid must be higher. Set to something reasonable to prevent spam bids. (In contract set as wei) 35 | - **Minimum Creator auction duration** - for how long Creator auctions should run for. Can be extended slightly by late bids, defined below. (In contract set as seconds) 36 | - **Minimum Keeper auction duration** - same, but for Keeper auctions. If set to zero, Keeper auctions are disabled. (In contract set as seconds) 37 | - **Late auction bids extension** - a period at the end of the auction, during which bids extend the auction. For example, if it is set to 5 minutes, and bid arrives with with 3 minutes remaining, auction end will be pushed by 2 minutes — to leave at least 5 minutes between last bid and auction end. (In contract set as seconds) 38 | 39 | ### Fee Settings 40 | 41 | - **Keeper tax, or Harberger tax** - how many percent of the Keeper-set Orb price they have to pay per year to maintain ownership. For example, if it's set to 10%, and Orb price is set to 10 ETH, Keeper has to pay 1 ETH / year. If tax rate is 1200%, Keeper pays their set Orb price each month to maintain ownership. (In contract set as basis points, 10 000 = 100%) 42 | - **Royalty** - percentage of how much of the Orb purchase price or Keeper auction proceeds go to the beneficiary. If set to 10% and Orb is purchased from a Keeper for 10 ETH, 1 ETH would go the beneficiary. Cannot be more than 100%, lol. (In contract set as basis points, 1 000 = 10%) 43 | 44 | ### Cooldown Settings 45 | 46 | - **Cooldown duration** - how often the Orb can be invoked by the Keeper. Orb comes fully charged (ready to be invoked) after a Creator auction. (In contract set as seconds) 47 | - **Flagging duration** - how long after a response has been made can a Keeper "flag" it — publicly mark it as unacceptable. If a response is private and gets flagged, Creator always has the right to reveal it. Can be set to zero to disable flagging. (In contract set as seconds) 48 | 49 | ### Invocation Settings 50 | 51 | - **Invocation maximum length** - how long of a question do you accept. Cannot be zero, lol. (In contract stored as a number of bytes in string) 52 | 53 | ## Orb Oath 54 | 55 | Orb Oath has elements that are set off-chain and hashed, and elements that get submitted on-chain, including the hash of off-chain elements. All aspects of the Oath can be changed be re-swearing, but only when Orb is in Creator's controlled (not owned by a Keeper). 56 | 57 | ### Oath and Terms 58 | 59 | - **Orb Oath** - a text that will be clearly visible on the Orb page, a verbalization of the Orb promise. There are no limits of what it can or has to say. It can have newlines, and supports markdown. Here's Eric's: 60 | 61 | ``` 62 | I, Eric Wall, solemnly swear to honor my Orb as far as my eyes, my arms and my mind serve me. I shall answer any question dutifully—as long as I do not violate any law or ethical conduct, or put myself or others into danger by doing so. The sum of my collected knowledge and wisdom is at your disposal. This is my Orb, and I shall bear no other Orbs of this kind under the reign of this Orb. 63 | ``` 64 | 65 | - **Terms** - an addendum to the Orb Oath that will be less visible and can be less exciting, spelling out edge cases, invocation limitations, allowed topics, etc. It can also be any length, with multiple paragraphs, and supports markdown. 66 | - **Timestamp** - mostly added for hash uniqueness, but also signifies when the Oath was written. 67 | 68 | ### Policies 69 | 70 | All policies are set as boolean (true / false). 71 | 72 | - **Allow Public Invocations** - are public Keeper questions allowed? If false, all invocations will be private. 73 | - **Allow Private Invocations** - are public Keeper questions allowed? If false, all invocations will be public. Not compatible with "Allow Public Invocations - false". 74 | - **Allow Private Responses** - can Creator responses be private? If false, all responses will have to be public, and therefore not compatible with "Allow Private Invocations - true". If true, Keepers will have a choice to request a private response - then the response will have to private. If not requested by the Keeper, Creator can still choose to make a response private. 75 | - **Allow Read Past Invocations** - can Keepers read private questions of previous Keepers and responses to these questions? If false, then only the Keeper that asks a private question will ever have access to a response - no one else will, even if this policy is later changed. Reduces Orb value to future Keepers. 76 | - **Allow Reveal Own Invocations** - can Keepers publicly (on the Orb page) reveal a private question or a response to their *own* invocation? If false, what's private stays private. 77 | - **Allow Reveal Past Invocations** - can Keepers also publicly reveal a private question or a response to an invocation of a previous Keeper? Can only be true if "Allow Reveal Own Invocations" is also true. 78 | 79 | ### On-chain Elements 80 | 81 | All previous elements are put into a JSON and `keccak256` hashed to produce an Oath Hash. This hash is submitted on-chain together with: 82 | 83 | - **Honored Until date** - timestamp (in seconds) until which the Oath will be honored by its Creator. Keepers invoking the Orb after that date are informed that they might not receive a response, as the Orb is no longer honored. Can be pushed further into the future any time, even if not owned by Creator. 84 | - **Response duration** - how quickly should Keepers expect a response after an invocation is made. Currently there is no smart contract logic around this duration, but the UI will highlight if a response is made late. (In contract set as seconds) 85 | 86 | ## Orb Configuration 87 | 88 | Lastly, there are elements that can be changed any time and only concern with the UI. 89 | 90 | - **URL** - What should be URL of the orb, defined as a subdomain: XXX.orb.land. 91 | - **Name** - Orb's nice name, like "Eric's Orb" 92 | - **Type** - Currently always "Text Q&A" 93 | - **Creator's Name** - Just the name, like "Eric". Used in the UI like "Eric gives himself 7 days to respond". 94 | - **Creator's Twitter** - Twitter username, used to encourage following there. 95 | - **Expected Auction Start Date** - Orb can launch with a countdown when the auction is expected to start. As a creator you still have to submit `startAuction` transaction. This date and time is just for visitor interest. 96 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022-2023 Orb Land 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include .env 2 | 3 | .PHONY: all test clean deploy-anvil 4 | 5 | all: clean remove install update build 6 | 7 | # Clean the repo 8 | clean :; forge clean 9 | 10 | # Remove modules 11 | remove :; rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib && touch .gitmodules && git add . && git commit -m "modules" 12 | 13 | install :; forge install foundry-rs/forge-std 14 | 15 | # Update Dependencies 16 | update :; forge update 17 | 18 | build :; forge build 19 | 20 | test :; forge test -vvv 21 | 22 | test-dev :; FOUNDRY_FUZZ_RUNS=50 forge test -vvv 23 | 24 | snapshot :; forge snapshot 25 | 26 | slither :; slither ./src --exclude-dependencies --exclude timestamp,solc-version,missing-zero-check --solc-remaps @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ --filter-paths lib 27 | 28 | format :; forge fmt 29 | 30 | # solhint should be installed globally 31 | lint :; solhint src/**/*.sol && solhint src/*.sol && solhint test/**/*.sol && solhint test/*.sol 32 | 33 | anvil :; anvil -m 'test test test test test test test test test test test junk' -b 6 34 | 35 | deploy-base-anvil :; forge script script/LocalDeployBase.s.sol:LocalDeployBase --rpc-url http://localhost:8545 --broadcast 36 | 37 | deploy-orb-anvil :; forge script script/LocalDeployOrb.s.sol:LocalDeployOrb --rpc-url http://localhost:8545 --broadcast 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔮 Orb Contract • [![test](https://github.com/orbland/orb/actions/workflows/ci.yml/badge.svg)](https://github.com/orbland/orb/actions/workflows/ci.yml) ![license](https://img.shields.io/badge/License-MIT-green.svg?label=license) 2 | 3 | Auction + Harberger taxed ownership. Used by [orb.land](https://orb.land). Uses Forge toolkit for building and testing. The contract combines the following areas of functionality: 4 | 5 | - **Auction.** Allows the contract owner to start the Orb auction, determining the first Keeper of the Orb. 6 | - **Funds management.** Allows any user to deposit and withdraw funds. Funds are used to make auction bids and pay Harberger Tax. 7 | - **Harberger Tax.** Uses delayed accounting (settling) to allocate funds from the current Keeper to the contract beneficiary, based on the price set by the Keeper and tax rate. 8 | - **On-chain Orb Invocations.** Allows the Keeper to periodically invoke the Orb, requesting a response from the Orb creator. 9 | - **ERC-721 compatibility.** All transfers revert, but otherwise, the contract appears as supporting all ERC-721 functions. 10 | 11 | The contract is fully documented in NatSpec format. 12 | 13 | ## Usage 14 | 15 | ```shell 16 | forge install foundry-rs/forge-std openzeppelin/openzeppelin-contracts # dependencies 17 | forge test # tests 18 | make anvil # local node 19 | make deploy-anvil # deploy to local node in another terminal 20 | ``` 21 | 22 | ## License 23 | 24 | Released under the [MIT License](https://github.com/orbland/orb/blob/main/LICENSE). 25 | 26 | ## Credits 27 | 28 | - [Eric Wall](https://twitter.com/ercwl) - Concept and mechanics design 29 | - [Jonas Lekevicius](https://twitter.com/lekevicius) - Contract implementation 30 | - [Odysseas.eth](https://twitter.com/odysseas_eth) - Tests, toolkit setup and many other contributions 31 | - [OpenZeppelin Contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) - Contract is based on OZ ERC-721 and Ownable implementations 32 | -------------------------------------------------------------------------------- /audits/NFR Audits - July 2023.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbland/contracts/cef59450b81c515db5410a91acaf2a678a3a1aab/audits/NFR Audits - July 2023.pdf -------------------------------------------------------------------------------- /audits/NFR Audits - May 2023.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbland/contracts/cef59450b81c515db5410a91acaf2a678a3a1aab/audits/NFR Audits - May 2023.pdf -------------------------------------------------------------------------------- /dependencies.sh: -------------------------------------------------------------------------------- 1 | forge install 'foundry-rs/forge-std@v1.7.6' 2 | forge install 'OpenZeppelin/openzeppelin-contracts@v4.9.5' 3 | forge install 'OpenZeppelin/openzeppelin-contracts-upgradeable@v4.9.5' 4 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | book/ -------------------------------------------------------------------------------- /docs/book.css: -------------------------------------------------------------------------------- 1 | table { 2 | margin: 0 auto; 3 | border-collapse: collapse; 4 | width: 100%; 5 | } 6 | 7 | table td:first-child { 8 | width: 15%; 9 | } 10 | 11 | table td:nth-child(2) { 12 | width: 25%; 13 | } -------------------------------------------------------------------------------- /docs/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | src = "src" 3 | title = "" 4 | 5 | [output.html] 6 | no-section-label = true 7 | additional-js = ["solidity.min.js"] 8 | additional-css = ["book.css"] 9 | git-repository-url = "https://github.com/orbland/orb" 10 | 11 | [output.html.fold] 12 | enable = true 13 | -------------------------------------------------------------------------------- /docs/solidity.min.js: -------------------------------------------------------------------------------- 1 | hljs.registerLanguage("solidity",(()=>{"use strict";function e(){try{return!0 2 | }catch(e){return!1}} 3 | var a=/-?(\b0[xX]([a-fA-F0-9]_?)*[a-fA-F0-9]|(\b[1-9](_?\d)*(\.((\d_?)*\d)?)?|\.\d(_?\d)*)([eE][-+]?\d(_?\d)*)?|\b0)(?!\w|\$)/ 4 | ;e()&&(a=a.source.replace(/\\b/g,"(?{ 14 | var a=r(e),o=l(e),c=/[A-Za-z_$][A-Za-z_$0-9.]*/,d=e.inherit(e.TITLE_MODE,{ 15 | begin:/[A-Za-z$_][0-9A-Za-z$_]*/,lexemes:c,keywords:n}),u={className:"params", 16 | begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,lexemes:c,keywords:n, 17 | contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,o,s]},_={ 18 | className:"operator",begin:/:=|->/};return{keywords:n,lexemes:c, 19 | contains:[a,o,i,t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,_,{ 20 | className:"function",lexemes:c,beginKeywords:"function",end:"{",excludeEnd:!0, 21 | contains:[d,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,_]}]}}, 22 | solAposStringMode:r,solQuoteStringMode:l,HEX_APOS_STRING_MODE:i, 23 | HEX_QUOTE_STRING_MODE:t,SOL_NUMBER:s,isNegativeLookbehindAvailable:e} 24 | ;const{baseAssembly:c,solAposStringMode:d,solQuoteStringMode:u,HEX_APOS_STRING_MODE:_,HEX_QUOTE_STRING_MODE:m,SOL_NUMBER:b,isNegativeLookbehindAvailable:E}=o 25 | ;return e=>{for(var a=d(e),s=u(e),n=[],i=0;i<32;i++)n[i]=i+1 26 | ;var t=n.map((e=>8*e)),r=[];for(i=0;i<=80;i++)r[i]=i 27 | ;var l=n.map((e=>"bytes"+e)).join(" ")+" ",o=t.map((e=>"uint"+e)).join(" ")+" ",g=t.map((e=>"int"+e)).join(" ")+" ",M=[].concat.apply([],t.map((e=>r.map((a=>e+"x"+a))))),p={ 28 | keyword:"var bool string int uint "+g+o+"byte bytes "+l+"fixed ufixed "+M.map((e=>"fixed"+e)).join(" ")+" "+M.map((e=>"ufixed"+e)).join(" ")+" enum struct mapping address new delete if else for while continue break return throw emit try catch revert unchecked _ function modifier event constructor fallback receive error virtual override constant immutable anonymous indexed storage memory calldata external public internal payable pure view private returns import from as using pragma contract interface library is abstract type assembly", 29 | literal:"true false wei gwei szabo finney ether seconds minutes hours days weeks years", 30 | built_in:"self this super selfdestruct suicide now msg block tx abi blockhash gasleft assert require Error Panic sha3 sha256 keccak256 ripemd160 ecrecover addmod mulmod log0 log1 log2 log3 log4" 31 | },O={className:"operator",begin:/[+\-!~*\/%<>&^|=]/ 32 | },C=/[A-Za-z_$][A-Za-z_$0-9]*/,N={className:"params",begin:/\(/,end:/\)/, 33 | excludeBegin:!0,excludeEnd:!0,lexemes:C,keywords:p, 34 | contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,s,b,"self"]},f={ 35 | begin:/\.\s*/,end:/[^A-Za-z0-9$_\.]/,excludeBegin:!0,excludeEnd:!0,keywords:{ 36 | built_in:"gas value selector address length push pop send transfer call callcode delegatecall staticcall balance code codehash wrap unwrap name creationCode runtimeCode interfaceId min max" 37 | },relevance:2},y=e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/, 38 | lexemes:C,keywords:p}),w={className:"built_in", 39 | begin:(E()?"(? mapping(uint256 invocationId => LateResponseReceipt receipt)) public lateResponseReceipts; 43 | ``` 44 | 45 | 46 | ### lateResponseReceiptClaimed 47 | Mapping for late response receipts. Used to track late response receipts for invocations that have not been 48 | 49 | 50 | ```solidity 51 | mapping(address orb => mapping(uint256 invocationId => bool receiptClaimed)) public lateResponseReceiptClaimed; 52 | ``` 53 | 54 | 55 | ## Functions 56 | ### constructor 57 | 58 | 59 | ```solidity 60 | constructor(); 61 | ``` 62 | 63 | ### initializeV2 64 | 65 | Re-initializes the contract after upgrade 66 | 67 | 68 | ```solidity 69 | function initializeV2(address lateResponseFund_) public reinitializer(2); 70 | ``` 71 | **Parameters** 72 | 73 | |Name|Type|Description| 74 | |----|----|-----------| 75 | |`lateResponseFund_`|`address`| The address of the Late Response Compensation Fund.| 76 | 77 | 78 | ### respond 79 | 80 | The Orb creator can use this function to respond to any existing invocation, no matter how long ago 81 | it was made. A response to an invocation can only be written once. There is no way to record response 82 | cleartext on-chain. 83 | 84 | *Emits `Response`, and sometimes `LateResponse` if the response was made after the response period.* 85 | 86 | 87 | ```solidity 88 | function respond(address orb, uint256 invocationId, bytes32 contentHash) external virtual override onlyCreator(orb); 89 | ``` 90 | **Parameters** 91 | 92 | |Name|Type|Description| 93 | |----|----|-----------| 94 | |`orb`|`address`|| 95 | |`invocationId`|`uint256`| Id of an invocation to which the response is being made.| 96 | |`contentHash`|`bytes32`| keccak256 hash of the response text.| 97 | 98 | 99 | ### setLateResponseReceiptClaimed 100 | 101 | 102 | ```solidity 103 | function setLateResponseReceiptClaimed(address orb, uint256 invocationId) external; 104 | ``` 105 | 106 | ### version 107 | 108 | Returns the version of the Orb Invocation Registry. Internal constant `_VERSION` will be increased with 109 | each upgrade. 110 | 111 | 112 | ```solidity 113 | function version() public view virtual override returns (uint256 orbInvocationRegistryVersion); 114 | ``` 115 | **Returns** 116 | 117 | |Name|Type|Description| 118 | |----|----|-----------| 119 | |`orbInvocationRegistryVersion`|`uint256`| Version of the Orb Invocation Registry contract.| 120 | 121 | 122 | ## Events 123 | ### LateResponse 124 | 125 | ```solidity 126 | event LateResponse(address indexed orb, uint256 indexed invocationId, address indexed responder, uint256 lateDuration); 127 | ``` 128 | 129 | ## Errors 130 | ### Unauthorized 131 | 132 | ```solidity 133 | error Unauthorized(); 134 | ``` 135 | 136 | ### LateResponseReceiptClaimed 137 | 138 | ```solidity 139 | error LateResponseReceiptClaimed(uint256 invocationId); 140 | ``` 141 | 142 | ## Structs 143 | ### LateResponseReceipt 144 | 145 | ```solidity 146 | struct LateResponseReceipt { 147 | uint256 lateDuration; 148 | uint256 price; 149 | uint256 keeperTaxNumerator; 150 | } 151 | ``` 152 | 153 | -------------------------------------------------------------------------------- /docs/src/src/OrbInvocationRegistryV2.sol/struct.LateResponseReceipt.md: -------------------------------------------------------------------------------- 1 | # LateResponseReceipt 2 | [Git Source](https://github.com/orbland/orb/blob/632d3f84a1d175499b6e0bdcec3c35c110926ac6/src/OrbInvocationRegistryV2.sol) 3 | 4 | 5 | ```solidity 6 | struct LateResponseReceipt { 7 | uint256 lateDuration; 8 | uint256 price; 9 | uint256 keeperTaxNumerator; 10 | } 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /docs/src/src/OrbInvocationTipJar.sol/contract.OrbInvocationTipJar.md: -------------------------------------------------------------------------------- 1 | # OrbInvocationTipJar 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/OrbInvocationTipJar.sol) 3 | 4 | **Inherits:** 5 | OwnableUpgradeable, [UUPSUpgradeable](/src/CustomUUPSUpgradeable.sol/abstract.UUPSUpgradeable.md) 6 | 7 | **Authors:** 8 | Jonas Lekevicius, Oren Yomtov 9 | 10 | This contract allows anyone to suggest an invocation to an Orb and optionally tip the Orb keeper. 11 | 12 | 13 | ## State Variables 14 | ### _VERSION 15 | Orb Invocation Tip Jar version 16 | 17 | 18 | ```solidity 19 | uint256 private constant _VERSION = 1; 20 | ``` 21 | 22 | 23 | ### _FEE_DENOMINATOR 24 | Fee Nominator: basis points (100.00%). Platform fee is in relation to this. 25 | 26 | 27 | ```solidity 28 | uint256 internal constant _FEE_DENOMINATOR = 100_00; 29 | ``` 30 | 31 | 32 | ### totalTips 33 | The sum of all tips for a given invocation 34 | 35 | 36 | ```solidity 37 | mapping(address orb => mapping(bytes32 invocationHash => uint256 tippedAmount)) public totalTips; 38 | ``` 39 | 40 | 41 | ### tipperTips 42 | The sum of all tips for a given invocation by a given tipper 43 | 44 | 45 | ```solidity 46 | mapping(address orb => mapping(address tipper => mapping(bytes32 invocationHash => uint256 tippedAmount))) public 47 | tipperTips; 48 | ``` 49 | 50 | 51 | ### claimedInvocations 52 | Whether a certain invocation's tips have been claimed: invocationId starts from 1 53 | 54 | 55 | ```solidity 56 | mapping(address orb => mapping(bytes32 invocationHash => uint256 invocationId)) public claimedInvocations; 57 | ``` 58 | 59 | 60 | ### minimumTips 61 | The minimum tip value for a given Orb 62 | 63 | 64 | ```solidity 65 | mapping(address orb => uint256 minimumTip) public minimumTips; 66 | ``` 67 | 68 | 69 | ### platformAddress 70 | Orb Land revenue address. Set during contract initialization to Orb Land Revenue multisig. While there is no 71 | function to change this address, it can be changed by upgrading the contract. 72 | 73 | 74 | ```solidity 75 | address public platformAddress; 76 | ``` 77 | 78 | 79 | ### platformFee 80 | Orb Land revenue fee numerator. Set during contract initialization. While there is no function to change this 81 | value, it can be changed by upgrading the contract. The fee is in relation to `_FEE_DENOMINATOR`. 82 | Note: contract upgradability poses risks! Orb Land may upgrade this contract and set the fee to _FEE_DENOMINATOR 83 | (100.00%), taking away all future tips. This is a risk that Orb keepers must be aware of, until upgradability 84 | is removed or modified. 85 | 86 | 87 | ```solidity 88 | uint256 public platformFee; 89 | ``` 90 | 91 | 92 | ### platformFunds 93 | Funds allocated for the Orb Land platform, withdrawable to `platformAddress` 94 | 95 | 96 | ```solidity 97 | uint256 public platformFunds; 98 | ``` 99 | 100 | 101 | ### __gap 102 | Gap used to prevent storage collisions 103 | 104 | 105 | ```solidity 106 | uint256[100] private __gap; 107 | ``` 108 | 109 | 110 | ## Functions 111 | ### constructor 112 | 113 | 114 | ```solidity 115 | constructor(); 116 | ``` 117 | 118 | ### initialize 119 | 120 | *Initializes the contract.* 121 | 122 | 123 | ```solidity 124 | function initialize(address platformAddress_, uint256 platformFee_) public initializer; 125 | ``` 126 | 127 | ### tipInvocation 128 | 129 | Tips a specific invocation content hash on an Orb. Any Keeper can invoke the tipped invocation and 130 | claim the tips. 131 | 132 | 133 | ```solidity 134 | function tipInvocation(address orb, bytes32 invocationHash) external payable virtual; 135 | ``` 136 | **Parameters** 137 | 138 | |Name|Type|Description| 139 | |----|----|-----------| 140 | |`orb`|`address`| The address of the orb| 141 | |`invocationHash`|`bytes32`| The invocation content hash| 142 | 143 | 144 | ### claimTipsForInvocation 145 | 146 | Claims all tips for a given suggested invocation. Meant to be called together with the invocation 147 | itself, using `invokeWith*AndCall` functions on OrbInvocationRegistry. 148 | 149 | 150 | ```solidity 151 | function claimTipsForInvocation(address orb, uint256 invocationIndex, uint256 minimumTipTotal) external virtual; 152 | ``` 153 | **Parameters** 154 | 155 | |Name|Type|Description| 156 | |----|----|-----------| 157 | |`orb`|`address`| The address of the Orb| 158 | |`invocationIndex`|`uint256`| The invocation id to check| 159 | |`minimumTipTotal`|`uint256`| The minimum tip value to claim (reverts if the total tips are less than this value)| 160 | 161 | 162 | ### withdrawTip 163 | 164 | Withdraws a tip from a given invocation. Not possible if invocation has been claimed. 165 | 166 | 167 | ```solidity 168 | function withdrawTip(address orb, bytes32 invocationHash) public virtual; 169 | ``` 170 | **Parameters** 171 | 172 | |Name|Type|Description| 173 | |----|----|-----------| 174 | |`orb`|`address`| The address of the orb| 175 | |`invocationHash`|`bytes32`| The invocation content hash| 176 | 177 | 178 | ### withdrawTips 179 | 180 | Withdraws all tips from a given list of Orbs and invocations. Will revert if any given invocation has 181 | been claimed. 182 | 183 | 184 | ```solidity 185 | function withdrawTips(address[] memory orbs, bytes32[] memory invocationHashes) external virtual; 186 | ``` 187 | **Parameters** 188 | 189 | |Name|Type|Description| 190 | |----|----|-----------| 191 | |`orbs`|`address[]`| Array of orb addresse| 192 | |`invocationHashes`|`bytes32[]`| Array of invocation content hashes| 193 | 194 | 195 | ### withdrawPlatformFunds 196 | 197 | Withdraws all funds set aside as the platform fee. Can be called by anyone. 198 | 199 | 200 | ```solidity 201 | function withdrawPlatformFunds() external virtual; 202 | ``` 203 | 204 | ### setMinimumTip 205 | 206 | Sets the minimum tip value for a given Orb. 207 | 208 | 209 | ```solidity 210 | function setMinimumTip(address orb, uint256 minimumTipValue) external virtual; 211 | ``` 212 | **Parameters** 213 | 214 | |Name|Type|Description| 215 | |----|----|-----------| 216 | |`orb`|`address`| The address of the Orb| 217 | |`minimumTipValue`|`uint256`| The minimum tip value| 218 | 219 | 220 | ### version 221 | 222 | Returns the version of the Orb Invocation Tip Jar. Internal constant `_VERSION` will be increased with 223 | each upgrade. 224 | 225 | 226 | ```solidity 227 | function version() public view virtual returns (uint256 orbInvocationTipJarVersion); 228 | ``` 229 | **Returns** 230 | 231 | |Name|Type|Description| 232 | |----|----|-----------| 233 | |`orbInvocationTipJarVersion`|`uint256`| Version of the Orb Invocation Tip Jar contract.| 234 | 235 | 236 | ### _authorizeUpgrade 237 | 238 | *Authorizes owner address to upgrade the contract.* 239 | 240 | 241 | ```solidity 242 | function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner; 243 | ``` 244 | 245 | ## Events 246 | ### TipDeposit 247 | 248 | ```solidity 249 | event TipDeposit(address indexed orb, bytes32 indexed invocationHash, address indexed tipper, uint256 tipValue); 250 | ``` 251 | 252 | ### TipWithdrawal 253 | 254 | ```solidity 255 | event TipWithdrawal(address indexed orb, bytes32 indexed invocationHash, address indexed tipper, uint256 tipValue); 256 | ``` 257 | 258 | ### TipsClaim 259 | 260 | ```solidity 261 | event TipsClaim(address indexed orb, bytes32 indexed invocationHash, address indexed invoker, uint256 tipsValue); 262 | ``` 263 | 264 | ### MinimumTipUpdate 265 | 266 | ```solidity 267 | event MinimumTipUpdate(address indexed orb, uint256 previousMinimumTip, uint256 indexed newMinimumTip); 268 | ``` 269 | 270 | ## Errors 271 | ### PlatformAddressInvalid 272 | 273 | ```solidity 274 | error PlatformAddressInvalid(); 275 | ``` 276 | 277 | ### PlatformFeeInvalid 278 | 279 | ```solidity 280 | error PlatformFeeInvalid(); 281 | ``` 282 | 283 | ### InsufficientTip 284 | 285 | ```solidity 286 | error InsufficientTip(uint256 tipValue, uint256 minimumTip); 287 | ``` 288 | 289 | ### InvocationNotInvoked 290 | 291 | ```solidity 292 | error InvocationNotInvoked(); 293 | ``` 294 | 295 | ### InvocationAlreadyClaimed 296 | 297 | ```solidity 298 | error InvocationAlreadyClaimed(); 299 | ``` 300 | 301 | ### InsufficientTips 302 | 303 | ```solidity 304 | error InsufficientTips(uint256 minimumTipTotal, uint256 totalClaimableTips); 305 | ``` 306 | 307 | ### TipNotFound 308 | 309 | ```solidity 310 | error TipNotFound(); 311 | ``` 312 | 313 | ### UnevenArrayLengths 314 | 315 | ```solidity 316 | error UnevenArrayLengths(); 317 | ``` 318 | 319 | ### NoFundsAvailable 320 | 321 | ```solidity 322 | error NoFundsAvailable(); 323 | ``` 324 | 325 | ### NotKeeper 326 | 327 | ```solidity 328 | error NotKeeper(); 329 | ``` 330 | 331 | -------------------------------------------------------------------------------- /docs/src/src/OrbPond.sol/contract.OrbPond.md: -------------------------------------------------------------------------------- 1 | # OrbPond 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/OrbPond.sol) 3 | 4 | **Inherits:** 5 | OwnableUpgradeable, [UUPSUpgradeable](/src/CustomUUPSUpgradeable.sol/abstract.UUPSUpgradeable.md) 6 | 7 | **Author:** 8 | Jonas Lekevicius 9 | 10 | Orbs come from a Pond. The Orb Pond is used to efficiently create new Orbs, and track “official” Orbs, 11 | supported by the Orb Land system. The Orb Pond is also used to register allowed Orb upgrade 12 | implementations, and keeps a reference to an Orb Invocation Registry used by all Orbs created with this 13 | Orb Pond. 14 | 15 | *Uses `Ownable`'s `owner()` to limit the creation of new Orbs to the administrator and for upgrades.* 16 | 17 | 18 | ## State Variables 19 | ### _VERSION 20 | Orb Pond version. Value: 1. 21 | 22 | 23 | ```solidity 24 | uint256 private constant _VERSION = 1; 25 | ``` 26 | 27 | 28 | ### orbs 29 | The mapping of Orb ids to Orbs. Increases monotonically. 30 | 31 | 32 | ```solidity 33 | mapping(uint256 orbId => address orbAddress) public orbs; 34 | ``` 35 | 36 | 37 | ### orbCount 38 | The number of Orbs created so far, used to find the next Orb id. 39 | 40 | 41 | ```solidity 42 | uint256 public orbCount; 43 | ``` 44 | 45 | 46 | ### versions 47 | The mapping of version numbers to implementation contract addresses. Looked up by Orbs to find implementation 48 | contracts for upgrades. 49 | 50 | 51 | ```solidity 52 | mapping(uint256 versionNumber => address implementation) public versions; 53 | ``` 54 | 55 | 56 | ### upgradeCalldata 57 | The mapping of version numbers to upgrade calldata. Looked up by Orbs to find initialization calldata for 58 | upgrades. 59 | 60 | 61 | ```solidity 62 | mapping(uint256 versionNumber => bytes upgradeCalldata) public upgradeCalldata; 63 | ``` 64 | 65 | 66 | ### latestVersion 67 | The highest version number so far. Could be used for new Orb creation. 68 | 69 | 70 | ```solidity 71 | uint256 public latestVersion; 72 | ``` 73 | 74 | 75 | ### registry 76 | The address of the Orb Invocation Registry, used to register Orb invocations and responses. 77 | 78 | 79 | ```solidity 80 | address public registry; 81 | ``` 82 | 83 | 84 | ### paymentSplitterImplementation 85 | The address of the PaymentSplitter implementation contract, used to create new PaymentSplitters. 86 | 87 | 88 | ```solidity 89 | address public paymentSplitterImplementation; 90 | ``` 91 | 92 | 93 | ### __gap 94 | Gap used to prevent storage collisions. 95 | 96 | 97 | ```solidity 98 | uint256[100] private __gap; 99 | ``` 100 | 101 | 102 | ## Functions 103 | ### constructor 104 | 105 | 106 | ```solidity 107 | constructor(); 108 | ``` 109 | 110 | ### initialize 111 | 112 | Initializes the contract, setting the `owner` and `registry` variables. 113 | 114 | 115 | ```solidity 116 | function initialize(address registry_, address paymentSplitterImplementation_) public initializer; 117 | ``` 118 | **Parameters** 119 | 120 | |Name|Type|Description| 121 | |----|----|-----------| 122 | |`registry_`|`address`| The address of the Orb Invocation Registry.| 123 | |`paymentSplitterImplementation_`|`address`| The address of the PaymentSplitter implementation contract.| 124 | 125 | 126 | ### createOrb 127 | 128 | Creates a new Orb together with a PaymentSplitter, and emits an event with the Orb's address. 129 | 130 | 131 | ```solidity 132 | function createOrb( 133 | address[] memory payees_, 134 | uint256[] memory shares_, 135 | string memory name, 136 | string memory symbol, 137 | string memory tokenURI 138 | ) external virtual onlyOwner; 139 | ``` 140 | **Parameters** 141 | 142 | |Name|Type|Description| 143 | |----|----|-----------| 144 | |`payees_`|`address[]`| Beneficiaries of the Orb's PaymentSplitter.| 145 | |`shares_`|`uint256[]`| Shares of the Orb's PaymentSplitter.| 146 | |`name`|`string`| Name of the Orb, used for display purposes. Suggestion: "NameOrb".| 147 | |`symbol`|`string`| Symbol of the Orb, used for display purposes. Suggestion: "ORB".| 148 | |`tokenURI`|`string`| Initial tokenURI of the Orb, used as part of ERC-721 tokenURI.| 149 | 150 | 151 | ### registerVersion 152 | 153 | Registers a new version of the Orb implementation contract. The version number must be exactly one 154 | higher than the previous version number, and the implementation address must be non-zero. Versions can 155 | be un-registered by setting the implementation address to 0; only the latest version can be 156 | un-registered. 157 | 158 | 159 | ```solidity 160 | function registerVersion(uint256 version_, address implementation_, bytes calldata upgradeCalldata_) 161 | external 162 | virtual 163 | onlyOwner; 164 | ``` 165 | **Parameters** 166 | 167 | |Name|Type|Description| 168 | |----|----|-----------| 169 | |`version_`|`uint256`| Version number of the new implementation contract.| 170 | |`implementation_`|`address`| Address of the new implementation contract.| 171 | |`upgradeCalldata_`|`bytes`| Initialization calldata to be used for upgrading to the new implementation contract.| 172 | 173 | 174 | ### version 175 | 176 | Returns the version of the Orb Pond. Internal constant `_VERSION` will be increased with each upgrade. 177 | 178 | 179 | ```solidity 180 | function version() public view virtual returns (uint256 orbPondVersion); 181 | ``` 182 | **Returns** 183 | 184 | |Name|Type|Description| 185 | |----|----|-----------| 186 | |`orbPondVersion`|`uint256`| Version of the Orb Pond contract.| 187 | 188 | 189 | ### _authorizeUpgrade 190 | 191 | *Authorizes `owner()` to upgrade this OrbPond contract.* 192 | 193 | 194 | ```solidity 195 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner; 196 | ``` 197 | 198 | ## Events 199 | ### OrbCreation 200 | 201 | ```solidity 202 | event OrbCreation(uint256 indexed orbId, address indexed orbAddress); 203 | ``` 204 | 205 | ### VersionRegistration 206 | 207 | ```solidity 208 | event VersionRegistration(uint256 indexed versionNumber, address indexed implementation); 209 | ``` 210 | 211 | ## Errors 212 | ### InvalidVersion 213 | 214 | ```solidity 215 | error InvalidVersion(); 216 | ``` 217 | 218 | -------------------------------------------------------------------------------- /docs/src/src/OrbPond.sol/interface.IOwnershipTransferrable.md: -------------------------------------------------------------------------------- 1 | # IOwnershipTransferrable 2 | [Git Source](https://github.com/orbland/orb/blob/632d3f84a1d175499b6e0bdcec3c35c110926ac6/src/OrbPond.sol) 3 | 4 | 5 | ## Functions 6 | ### transferOwnership 7 | 8 | 9 | ```solidity 10 | function transferOwnership(address newOwner) external; 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /docs/src/src/OrbPondV2.sol/contract.OrbPondV2.md: -------------------------------------------------------------------------------- 1 | # OrbPondV2 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/OrbPondV2.sol) 3 | 4 | **Inherits:** 5 | [OrbPond](/src/OrbPond.sol/contract.OrbPond.md) 6 | 7 | **Author:** 8 | Jonas Lekevicius 9 | 10 | Orbs come from a Pond. The Orb Pond is used to efficiently create new Orbs, and track “official” Orbs, 11 | supported by the Orb Land system. The Orb Pond is also used to register allowed Orb upgrade 12 | implementations, and keeps a reference to an Orb Invocation Registry used by all Orbs created with this 13 | Orb Pond. 14 | 15 | *Uses `Ownable`'s `owner()` to limit the creation of new Orbs to the administrator and for upgrades. 16 | V2 adds these changes: 17 | - `orbInitialVersion` field for new Orb creation and `setOrbInitialVersion()` function to set it. This 18 | allows to specify which version of the Orb implementation to use for new Orbs. 19 | - `beneficiaryWithdrawalAddresses` mapping to authorize addresses to be used as 20 | `beneficiaryWithdrawalAddress` on Orbs, `authorizeWithdrawalAddress()` function to set it, and 21 | `beneficiaryWithdrawalAddressPermitted()` function to check if address is authorized.* 22 | 23 | 24 | ## State Variables 25 | ### _VERSION 26 | Orb Pond version. Value: 2. 27 | 28 | 29 | ```solidity 30 | uint256 private constant _VERSION = 2; 31 | ``` 32 | 33 | 34 | ### orbInitialVersion 35 | New Orb version 36 | 37 | 38 | ```solidity 39 | uint256 public orbInitialVersion; 40 | ``` 41 | 42 | 43 | ### beneficiaryWithdrawalAddresses 44 | Addresses authorized to be used as beneficiaryWithdrawal address 45 | 46 | 47 | ```solidity 48 | mapping(address withdrawalAddress => bool isPermitted) public beneficiaryWithdrawalAddresses; 49 | ``` 50 | 51 | 52 | ### __gap 53 | Gap used to prevent storage collisions. 54 | 55 | 56 | ```solidity 57 | uint256[100] private __gap; 58 | ``` 59 | 60 | 61 | ## Functions 62 | ### constructor 63 | 64 | 65 | ```solidity 66 | constructor(); 67 | ``` 68 | 69 | ### initializeV2 70 | 71 | Reinitializes the contract with provided initial value for `orbInitialVersion`. 72 | 73 | 74 | ```solidity 75 | function initializeV2(uint256 orbInitialVersion_) public reinitializer(2); 76 | ``` 77 | **Parameters** 78 | 79 | |Name|Type|Description| 80 | |----|----|-----------| 81 | |`orbInitialVersion_`|`uint256`| Registered Orb implementation version to be used for new Orbs.| 82 | 83 | 84 | ### createOrb 85 | 86 | Creates a new Orb together with a PaymentSplitter, and emits an event with the Orb's address. 87 | 88 | 89 | ```solidity 90 | function createOrb( 91 | address[] memory payees_, 92 | uint256[] memory shares_, 93 | string memory name, 94 | string memory symbol, 95 | string memory tokenURI 96 | ) external virtual override onlyOwner; 97 | ``` 98 | **Parameters** 99 | 100 | |Name|Type|Description| 101 | |----|----|-----------| 102 | |`payees_`|`address[]`| Beneficiaries of the Orb's PaymentSplitter.| 103 | |`shares_`|`uint256[]`| Shares of the Orb's PaymentSplitter.| 104 | |`name`|`string`| Name of the Orb, used for display purposes. Suggestion: "NameOrb".| 105 | |`symbol`|`string`| Symbol of the Orb, used for display purposes. Suggestion: "ORB".| 106 | |`tokenURI`|`string`| Initial tokenURI of the Orb, used as part of ERC-721 tokenURI.| 107 | 108 | 109 | ### setOrbInitialVersion 110 | 111 | Sets the registered Orb implementation version to be used for new Orbs. 112 | 113 | 114 | ```solidity 115 | function setOrbInitialVersion(uint256 orbInitialVersion_) external virtual onlyOwner; 116 | ``` 117 | **Parameters** 118 | 119 | |Name|Type|Description| 120 | |----|----|-----------| 121 | |`orbInitialVersion_`|`uint256`| Registered Orb implementation version number to be used for new Orbs.| 122 | 123 | 124 | ### version 125 | 126 | Returns the version of the Orb Pond. Internal constant `_VERSION` will be increased with each upgrade. 127 | 128 | 129 | ```solidity 130 | function version() public view virtual override returns (uint256 orbPondVersion); 131 | ``` 132 | **Returns** 133 | 134 | |Name|Type|Description| 135 | |----|----|-----------| 136 | |`orbPondVersion`|`uint256`| Version of the Orb Pond contract.| 137 | 138 | 139 | ### beneficiaryWithdrawalAddressPermitted 140 | 141 | Returns if address can be used as beneficiary withdrawal address on Orbs. 142 | 143 | 144 | ```solidity 145 | function beneficiaryWithdrawalAddressPermitted(address beneficiaryWithdrawalAddress) 146 | external 147 | virtual 148 | returns (bool isBeneficiaryWithdrawalAddressPermitted); 149 | ``` 150 | **Parameters** 151 | 152 | |Name|Type|Description| 153 | |----|----|-----------| 154 | |`beneficiaryWithdrawalAddress`|`address`|Address to check. Zero address is always permitted.| 155 | 156 | 157 | ### authorizeWithdrawalAddress 158 | 159 | Allows the owner to authorize permitted beneficiary withdrawal addresses. 160 | 161 | 162 | ```solidity 163 | function authorizeWithdrawalAddress(address addressToAuthorize, bool authorizationValue) external virtual onlyOwner; 164 | ``` 165 | **Parameters** 166 | 167 | |Name|Type|Description| 168 | |----|----|-----------| 169 | |`addressToAuthorize`|`address`| Address to authorize (likely contract).| 170 | |`authorizationValue`|`bool`| Boolean value to set the authorization to.| 171 | 172 | 173 | ## Events 174 | ### OrbInitialVersionUpdate 175 | 176 | ```solidity 177 | event OrbInitialVersionUpdate(uint256 previousInitialVersion, uint256 indexed newInitialVersion); 178 | ``` 179 | 180 | ### WithdrawalAddressAuthorization 181 | 182 | ```solidity 183 | event WithdrawalAddressAuthorization(address indexed withdrawalAddress, bool indexed authorized); 184 | ``` 185 | 186 | -------------------------------------------------------------------------------- /docs/src/src/OrbPondV2.sol/interface.IOwnershipTransferrable.md: -------------------------------------------------------------------------------- 1 | # IOwnershipTransferrable 2 | [Git Source](https://github.com/orbland/orb/blob/632d3f84a1d175499b6e0bdcec3c35c110926ac6/src/OrbPondV2.sol) 3 | 4 | 5 | ## Functions 6 | ### transferOwnership 7 | 8 | 9 | ```solidity 10 | function transferOwnership(address newOwner) external; 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /docs/src/src/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contents 4 | - [interfaces](/src/interfaces) 5 | - [test-upgrades](/src/test-upgrades) 6 | - [PaymentSplitter](CustomPaymentSplitter.sol/contract.PaymentSplitter.md) 7 | - [UUPSUpgradeable](CustomUUPSUpgradeable.sol/abstract.UUPSUpgradeable.md) 8 | - [IOwnershipTransferrable](IOwnershipTransferrable.sol/interface.IOwnershipTransferrable.md) 9 | - [Orb](Orb.sol/contract.Orb.md) 10 | - [OrbInvocationRegistry](OrbInvocationRegistry.sol/contract.OrbInvocationRegistry.md) 11 | - [OrbInvocationTipJar](OrbInvocationTipJar.sol/contract.OrbInvocationTipJar.md) 12 | - [OrbPond](OrbPond.sol/contract.OrbPond.md) 13 | - [OrbPondV2](OrbPondV2.sol/contract.OrbPondV2.md) 14 | - [OrbV2](OrbV2.sol/contract.OrbV2.md) 15 | -------------------------------------------------------------------------------- /docs/src/src/interfaces/IOrb.sol/interface.IOrb.md: -------------------------------------------------------------------------------- 1 | # IOrb 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/interfaces/IOrb.sol) 3 | 4 | **Inherits:** 5 | IERC165Upgradeable 6 | 7 | 8 | ## Functions 9 | ### auctionEndTime 10 | 11 | 12 | ```solidity 13 | function auctionEndTime() external view returns (uint256); 14 | ``` 15 | 16 | ### leadingBidder 17 | 18 | 19 | ```solidity 20 | function leadingBidder() external view returns (address); 21 | ``` 22 | 23 | ### leadingBid 24 | 25 | 26 | ```solidity 27 | function leadingBid() external view returns (uint256); 28 | ``` 29 | 30 | ### auctionBeneficiary 31 | 32 | 33 | ```solidity 34 | function auctionBeneficiary() external view returns (address); 35 | ``` 36 | 37 | ### auctionStartingPrice 38 | 39 | 40 | ```solidity 41 | function auctionStartingPrice() external view returns (uint256); 42 | ``` 43 | 44 | ### auctionMinimumBidStep 45 | 46 | 47 | ```solidity 48 | function auctionMinimumBidStep() external view returns (uint256); 49 | ``` 50 | 51 | ### auctionMinimumDuration 52 | 53 | 54 | ```solidity 55 | function auctionMinimumDuration() external view returns (uint256); 56 | ``` 57 | 58 | ### auctionKeeperMinimumDuration 59 | 60 | 61 | ```solidity 62 | function auctionKeeperMinimumDuration() external view returns (uint256); 63 | ``` 64 | 65 | ### auctionBidExtension 66 | 67 | 68 | ```solidity 69 | function auctionBidExtension() external view returns (uint256); 70 | ``` 71 | 72 | ### fundsOf 73 | 74 | 75 | ```solidity 76 | function fundsOf(address owner) external view returns (uint256); 77 | ``` 78 | 79 | ### lastSettlementTime 80 | 81 | 82 | ```solidity 83 | function lastSettlementTime() external view returns (uint256); 84 | ``` 85 | 86 | ### keeperSolvent 87 | 88 | 89 | ```solidity 90 | function keeperSolvent() external view returns (bool); 91 | ``` 92 | 93 | ### keeperTaxNumerator 94 | 95 | 96 | ```solidity 97 | function keeperTaxNumerator() external view returns (uint256); 98 | ``` 99 | 100 | ### feeDenominator 101 | 102 | 103 | ```solidity 104 | function feeDenominator() external view returns (uint256); 105 | ``` 106 | 107 | ### keeperTaxPeriod 108 | 109 | 110 | ```solidity 111 | function keeperTaxPeriod() external view returns (uint256); 112 | ``` 113 | 114 | ### keeper 115 | 116 | 117 | ```solidity 118 | function keeper() external view returns (address); 119 | ``` 120 | 121 | ### keeperReceiveTime 122 | 123 | 124 | ```solidity 125 | function keeperReceiveTime() external view returns (uint256); 126 | ``` 127 | 128 | ### price 129 | 130 | 131 | ```solidity 132 | function price() external view returns (uint256); 133 | ``` 134 | 135 | ### royaltyNumerator 136 | 137 | 138 | ```solidity 139 | function royaltyNumerator() external view returns (uint256); 140 | ``` 141 | 142 | ### cooldown 143 | 144 | 145 | ```solidity 146 | function cooldown() external view returns (uint256); 147 | ``` 148 | 149 | ### flaggingPeriod 150 | 151 | 152 | ```solidity 153 | function flaggingPeriod() external view returns (uint256); 154 | ``` 155 | 156 | ### lastInvocationTime 157 | 158 | 159 | ```solidity 160 | function lastInvocationTime() external view returns (uint256); 161 | ``` 162 | 163 | ### cleartextMaximumLength 164 | 165 | 166 | ```solidity 167 | function cleartextMaximumLength() external view returns (uint256); 168 | ``` 169 | 170 | ### pond 171 | 172 | 173 | ```solidity 174 | function pond() external view returns (address); 175 | ``` 176 | 177 | ### creator 178 | 179 | 180 | ```solidity 181 | function creator() external view returns (address); 182 | ``` 183 | 184 | ### beneficiary 185 | 186 | 187 | ```solidity 188 | function beneficiary() external view returns (address); 189 | ``` 190 | 191 | ### honoredUntil 192 | 193 | 194 | ```solidity 195 | function honoredUntil() external view returns (uint256); 196 | ``` 197 | 198 | ### responsePeriod 199 | 200 | 201 | ```solidity 202 | function responsePeriod() external view returns (uint256); 203 | ``` 204 | 205 | ### version 206 | 207 | 208 | ```solidity 209 | function version() external view returns (uint256); 210 | ``` 211 | 212 | ### requestedUpgradeImplementation 213 | 214 | 215 | ```solidity 216 | function requestedUpgradeImplementation() external view returns (address); 217 | ``` 218 | 219 | ### initialize 220 | 221 | 222 | ```solidity 223 | function initialize(address beneficiary_, string memory name_, string memory symbol_, string memory tokenURI_) 224 | external; 225 | ``` 226 | 227 | ### startAuction 228 | 229 | 230 | ```solidity 231 | function startAuction() external; 232 | ``` 233 | 234 | ### bid 235 | 236 | 237 | ```solidity 238 | function bid(uint256 amount, uint256 priceIfWon) external payable; 239 | ``` 240 | 241 | ### finalizeAuction 242 | 243 | 244 | ```solidity 245 | function finalizeAuction() external; 246 | ``` 247 | 248 | ### deposit 249 | 250 | 251 | ```solidity 252 | function deposit() external payable; 253 | ``` 254 | 255 | ### withdraw 256 | 257 | 258 | ```solidity 259 | function withdraw(uint256 amount) external; 260 | ``` 261 | 262 | ### withdrawAll 263 | 264 | 265 | ```solidity 266 | function withdrawAll() external; 267 | ``` 268 | 269 | ### withdrawAllForBeneficiary 270 | 271 | 272 | ```solidity 273 | function withdrawAllForBeneficiary() external; 274 | ``` 275 | 276 | ### settle 277 | 278 | 279 | ```solidity 280 | function settle() external; 281 | ``` 282 | 283 | ### listWithPrice 284 | 285 | 286 | ```solidity 287 | function listWithPrice(uint256 listingPrice) external; 288 | ``` 289 | 290 | ### setPrice 291 | 292 | 293 | ```solidity 294 | function setPrice(uint256 newPrice) external; 295 | ``` 296 | 297 | ### purchase 298 | 299 | 300 | ```solidity 301 | function purchase( 302 | uint256 newPrice, 303 | uint256 currentPrice, 304 | uint256 currentKeeperTaxNumerator, 305 | uint256 currentRoyaltyNumerator, 306 | uint256 currentCooldown, 307 | uint256 currentCleartextMaximumLength 308 | ) external payable; 309 | ``` 310 | 311 | ### relinquish 312 | 313 | 314 | ```solidity 315 | function relinquish(bool withAuction) external; 316 | ``` 317 | 318 | ### foreclose 319 | 320 | 321 | ```solidity 322 | function foreclose() external; 323 | ``` 324 | 325 | ### setLastInvocationTime 326 | 327 | 328 | ```solidity 329 | function setLastInvocationTime(uint256 timestamp) external; 330 | ``` 331 | 332 | ### swearOath 333 | 334 | 335 | ```solidity 336 | function swearOath(bytes32 oathHash, uint256 newHonoredUntil, uint256 newResponsePeriod) external; 337 | ``` 338 | 339 | ### extendHonoredUntil 340 | 341 | 342 | ```solidity 343 | function extendHonoredUntil(uint256 newHonoredUntil) external; 344 | ``` 345 | 346 | ### setTokenURI 347 | 348 | 349 | ```solidity 350 | function setTokenURI(string memory newTokenURI) external; 351 | ``` 352 | 353 | ### setAuctionParameters 354 | 355 | 356 | ```solidity 357 | function setAuctionParameters( 358 | uint256 newStartingPrice, 359 | uint256 newMinimumBidStep, 360 | uint256 newMinimumDuration, 361 | uint256 newKeeperMinimumDuration, 362 | uint256 newBidExtension 363 | ) external; 364 | ``` 365 | 366 | ### setFees 367 | 368 | 369 | ```solidity 370 | function setFees(uint256 newKeeperTaxNumerator, uint256 newRoyaltyNumerator) external; 371 | ``` 372 | 373 | ### setCooldown 374 | 375 | 376 | ```solidity 377 | function setCooldown(uint256 newCooldown, uint256 newFlaggingPeriod) external; 378 | ``` 379 | 380 | ### setCleartextMaximumLength 381 | 382 | 383 | ```solidity 384 | function setCleartextMaximumLength(uint256 newCleartextMaximumLength) external; 385 | ``` 386 | 387 | ### requestUpgrade 388 | 389 | 390 | ```solidity 391 | function requestUpgrade(address requestedImplementation) external; 392 | ``` 393 | 394 | ### upgradeToNextVersion 395 | 396 | 397 | ```solidity 398 | function upgradeToNextVersion() external; 399 | ``` 400 | 401 | -------------------------------------------------------------------------------- /docs/src/src/interfaces/IOrbInvocationRegistry.sol/interface.IOrbInvocationRegistry.md: -------------------------------------------------------------------------------- 1 | # IOrbInvocationRegistry 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/interfaces/IOrbInvocationRegistry.sol) 3 | 4 | **Inherits:** 5 | IERC165Upgradeable 6 | 7 | 8 | ## Functions 9 | ### invocations 10 | 11 | 12 | ```solidity 13 | function invocations(address orb, uint256 invocationId) 14 | external 15 | view 16 | returns (address invoker, bytes32 contentHash, uint256 timestamp); 17 | ``` 18 | 19 | ### invocationCount 20 | 21 | 22 | ```solidity 23 | function invocationCount(address orb) external view returns (uint256); 24 | ``` 25 | 26 | ### responses 27 | 28 | 29 | ```solidity 30 | function responses(address orb, uint256 invocationId) external view returns (bytes32 contentHash, uint256 timestamp); 31 | ``` 32 | 33 | ### responseFlagged 34 | 35 | 36 | ```solidity 37 | function responseFlagged(address orb, uint256 invocationId) external view returns (bool); 38 | ``` 39 | 40 | ### flaggedResponsesCount 41 | 42 | 43 | ```solidity 44 | function flaggedResponsesCount(address orb) external view returns (uint256); 45 | ``` 46 | 47 | ### authorizedContracts 48 | 49 | 50 | ```solidity 51 | function authorizedContracts(address contractAddress) external view returns (bool); 52 | ``` 53 | 54 | ### version 55 | 56 | 57 | ```solidity 58 | function version() external view returns (uint256); 59 | ``` 60 | 61 | ### invokeWithCleartext 62 | 63 | 64 | ```solidity 65 | function invokeWithCleartext(address orb, string memory cleartext) external; 66 | ``` 67 | 68 | ### invokeWithCleartextAndCall 69 | 70 | 71 | ```solidity 72 | function invokeWithCleartextAndCall( 73 | address orb, 74 | string memory cleartext, 75 | address addressToCall, 76 | bytes memory dataToCall 77 | ) external; 78 | ``` 79 | 80 | ### invokeWithHash 81 | 82 | 83 | ```solidity 84 | function invokeWithHash(address orb, bytes32 contentHash) external; 85 | ``` 86 | 87 | ### invokeWithHashAndCall 88 | 89 | 90 | ```solidity 91 | function invokeWithHashAndCall(address orb, bytes32 contentHash, address addressToCall, bytes memory dataToCall) 92 | external; 93 | ``` 94 | 95 | ### respond 96 | 97 | 98 | ```solidity 99 | function respond(address orb, uint256 invocationId, bytes32 contentHash) external; 100 | ``` 101 | 102 | ### flagResponse 103 | 104 | 105 | ```solidity 106 | function flagResponse(address orb, uint256 invocationId) external; 107 | ``` 108 | 109 | -------------------------------------------------------------------------------- /docs/src/src/interfaces/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contents 4 | - [IOrb](IOrb.sol/interface.IOrb.md) 5 | - [IOrbInvocationRegistry](IOrbInvocationRegistry.sol/interface.IOrbInvocationRegistry.md) 6 | -------------------------------------------------------------------------------- /docs/src/src/test-upgrades/OrbInvocationRegistryTestUpgrade.sol/contract.OrbInvocationRegistryTestUpgrade.md: -------------------------------------------------------------------------------- 1 | # OrbInvocationRegistryTestUpgrade 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/test-upgrades/OrbInvocationRegistryTestUpgrade.sol) 3 | 4 | **Inherits:** 5 | [OrbInvocationRegistry](/src/OrbInvocationRegistry.sol/contract.OrbInvocationRegistry.md) 6 | 7 | **Author:** 8 | Jonas Lekevicius 9 | 10 | The Orb Invocation Registry is used to track invocations and responses for any Orb. 11 | 12 | *`Orb`s using an `OrbInvocationRegistry` must implement `IOrb` interface. Uses `Ownable`'s `owner()` to 13 | guard upgrading. 14 | Test Upgrade records Late Response Receipts if the response is made after the response period. Together 15 | with the `LateResponseDeposit` contract, it can allow Creators to compensate Keepers for late responses.* 16 | 17 | 18 | ## State Variables 19 | ### _VERSION 20 | Orb Invocation Registry version. 21 | 22 | 23 | ```solidity 24 | uint256 private constant _VERSION = 100; 25 | ``` 26 | 27 | 28 | ### lateResponseFund 29 | The address of the Late Response Deposit contract. 30 | 31 | 32 | ```solidity 33 | address public lateResponseFund; 34 | ``` 35 | 36 | 37 | ### lateResponseReceipts 38 | Mapping for late response receipts. Used to track late response receipts for invocations that have not been 39 | 40 | 41 | ```solidity 42 | mapping(address orb => mapping(uint256 invocationId => LateResponseReceipt receipt)) public lateResponseReceipts; 43 | ``` 44 | 45 | 46 | ### lateResponseReceiptClaimed 47 | Mapping for late response receipts. Used to track late response receipts for invocations that have not been 48 | 49 | 50 | ```solidity 51 | mapping(address orb => mapping(uint256 invocationId => bool receiptClaimed)) public lateResponseReceiptClaimed; 52 | ``` 53 | 54 | 55 | ## Functions 56 | ### constructor 57 | 58 | 59 | ```solidity 60 | constructor(); 61 | ``` 62 | 63 | ### initializeTestUpgrade 64 | 65 | Re-initializes the contract after upgrade 66 | 67 | 68 | ```solidity 69 | function initializeTestUpgrade(address lateResponseFund_) public reinitializer(100); 70 | ``` 71 | **Parameters** 72 | 73 | |Name|Type|Description| 74 | |----|----|-----------| 75 | |`lateResponseFund_`|`address`| The address of the Late Response Compensation Fund.| 76 | 77 | 78 | ### respond 79 | 80 | The Orb creator can use this function to respond to any existing invocation, no matter how long ago 81 | it was made. A response to an invocation can only be written once. There is no way to record response 82 | cleartext on-chain. 83 | 84 | *Emits `Response`, and sometimes `LateResponse` if the response was made after the response period.* 85 | 86 | 87 | ```solidity 88 | function respond(address orb, uint256 invocationId, bytes32 contentHash) external virtual override onlyCreator(orb); 89 | ``` 90 | **Parameters** 91 | 92 | |Name|Type|Description| 93 | |----|----|-----------| 94 | |`orb`|`address`|| 95 | |`invocationId`|`uint256`| Id of an invocation to which the response is being made.| 96 | |`contentHash`|`bytes32`| keccak256 hash of the response text.| 97 | 98 | 99 | ### setLateResponseReceiptClaimed 100 | 101 | 102 | ```solidity 103 | function setLateResponseReceiptClaimed(address orb, uint256 invocationId) external; 104 | ``` 105 | 106 | ### version 107 | 108 | Returns the version of the Orb Invocation Registry. Internal constant `_VERSION` will be increased with 109 | each upgrade. 110 | 111 | 112 | ```solidity 113 | function version() public view virtual override returns (uint256 orbInvocationRegistryVersion); 114 | ``` 115 | **Returns** 116 | 117 | |Name|Type|Description| 118 | |----|----|-----------| 119 | |`orbInvocationRegistryVersion`|`uint256`| Version of the Orb Invocation Registry contract.| 120 | 121 | 122 | ## Events 123 | ### LateResponse 124 | 125 | ```solidity 126 | event LateResponse(address indexed orb, uint256 indexed invocationId, address indexed responder, uint256 lateDuration); 127 | ``` 128 | 129 | ## Errors 130 | ### Unauthorized 131 | 132 | ```solidity 133 | error Unauthorized(); 134 | ``` 135 | 136 | ### LateResponseReceiptClaimed 137 | 138 | ```solidity 139 | error LateResponseReceiptClaimed(uint256 invocationId); 140 | ``` 141 | 142 | ## Structs 143 | ### LateResponseReceipt 144 | 145 | ```solidity 146 | struct LateResponseReceipt { 147 | uint256 lateDuration; 148 | uint256 price; 149 | uint256 keeperTaxNumerator; 150 | } 151 | ``` 152 | 153 | -------------------------------------------------------------------------------- /docs/src/src/test-upgrades/OrbInvocationTipJarTestUpgrade.sol/contract.OrbInvocationTipJarTestUpgrade.md: -------------------------------------------------------------------------------- 1 | # OrbInvocationTipJarTestUpgrade 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/test-upgrades/OrbInvocationTipJarTestUpgrade.sol) 3 | 4 | **Inherits:** 5 | [OrbInvocationTipJar](/src/OrbInvocationTipJar.sol/contract.OrbInvocationTipJar.md) 6 | 7 | **Author:** 8 | Jonas Lekevicius 9 | 10 | This contract allows anyone to suggest an invocation to an Orb and optionally tip the Orb keeper 11 | Test Upgrade requires all tips to be multiples of fixed amount of ETH. 12 | 13 | 14 | ## State Variables 15 | ### _VERSION 16 | Orb Invocation Tip Jar version. 17 | 18 | 19 | ```solidity 20 | uint256 private constant _VERSION = 100; 21 | ``` 22 | 23 | 24 | ### tipModulo 25 | An amount of ETH that is used as a tip modulo. All tips must be multiples of this amount. 26 | 27 | 28 | ```solidity 29 | uint256 public tipModulo; 30 | ``` 31 | 32 | 33 | ## Functions 34 | ### constructor 35 | 36 | 37 | ```solidity 38 | constructor(); 39 | ``` 40 | 41 | ### initializeTestUpgrade 42 | 43 | Re-initializes the contract after upgrade 44 | 45 | 46 | ```solidity 47 | function initializeTestUpgrade(uint256 tipModulo_) public reinitializer(100); 48 | ``` 49 | **Parameters** 50 | 51 | |Name|Type|Description| 52 | |----|----|-----------| 53 | |`tipModulo_`|`uint256`| An amount of ETH that is used as a tip modulo.| 54 | 55 | 56 | ### tipInvocation 57 | 58 | Tips an orb keeper to invoke their orb with a specific content hash 59 | 60 | 61 | ```solidity 62 | function tipInvocation(address orb, bytes32 invocationHash) public payable virtual override; 63 | ``` 64 | **Parameters** 65 | 66 | |Name|Type|Description| 67 | |----|----|-----------| 68 | |`orb`|`address`| The address of the orb| 69 | |`invocationHash`|`bytes32`| The invocation content hash| 70 | 71 | 72 | ### setTipModulo 73 | 74 | 75 | ```solidity 76 | function setTipModulo(uint256 tipModulo_) public virtual onlyOwner; 77 | ``` 78 | 79 | ### version 80 | 81 | Returns the version of the Orb Invocation TipJar. Internal constant `_VERSION` will be increased with 82 | each upgrade. 83 | 84 | 85 | ```solidity 86 | function version() public view virtual override returns (uint256 orbInvocationTipJarVersion); 87 | ``` 88 | **Returns** 89 | 90 | |Name|Type|Description| 91 | |----|----|-----------| 92 | |`orbInvocationTipJarVersion`|`uint256`| Version of the Orb Invocation TipJar contract.| 93 | 94 | 95 | ## Events 96 | ### TipModuloUpdate 97 | 98 | ```solidity 99 | event TipModuloUpdate(uint256 previousTipModulo, uint256 indexed newTipModulo); 100 | ``` 101 | 102 | ## Errors 103 | ### TipNotAModuloMultiple 104 | 105 | ```solidity 106 | error TipNotAModuloMultiple(uint256 tip, uint256 tipModulo); 107 | ``` 108 | 109 | ### InvalidTipModulo 110 | 111 | ```solidity 112 | error InvalidTipModulo(); 113 | ``` 114 | 115 | -------------------------------------------------------------------------------- /docs/src/src/test-upgrades/OrbPondTestUpgrade.sol/contract.OrbPondTestUpgrade.md: -------------------------------------------------------------------------------- 1 | # OrbPondTestUpgrade 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/test-upgrades/OrbPondTestUpgrade.sol) 3 | 4 | **Inherits:** 5 | [OrbPondV2](/src/OrbPondV2.sol/contract.OrbPondV2.md) 6 | 7 | **Author:** 8 | Jonas Lekevicius 9 | 10 | Orbs come from a Pond. The Orb Pond is used to efficiently create new Orbs, and track “official” Orbs, 11 | supported by the Orb Land system. The Orb Pond is also used to register allowed Orb upgrade 12 | implementations, and keeps a reference to an Orb Invocation Registry used by all Orbs created with this 13 | Orb Pond. 14 | 15 | *Uses `Ownable`'s `owner()` to limit the creation of new Orbs to the administrator and for upgrades. 16 | Test Upgrade allows anyone to create orbs, not just the owner, automatically splitting proceeds between the 17 | creator and the Orb Land wallet.* 18 | 19 | 20 | ## State Variables 21 | ### _VERSION 22 | Orb Pond version. 23 | 24 | 25 | ```solidity 26 | uint256 private constant _VERSION = 100; 27 | ``` 28 | 29 | 30 | ### orbLandWallet 31 | 32 | ```solidity 33 | address public orbLandWallet; 34 | ``` 35 | 36 | 37 | ## Functions 38 | ### constructor 39 | 40 | 41 | ```solidity 42 | constructor(); 43 | ``` 44 | 45 | ### initializeTestUpgrade 46 | 47 | Re-initializes the contract after upgrade 48 | 49 | 50 | ```solidity 51 | function initializeTestUpgrade(address orbLandWallet_) public reinitializer(100); 52 | ``` 53 | **Parameters** 54 | 55 | |Name|Type|Description| 56 | |----|----|-----------| 57 | |`orbLandWallet_`|`address`| The address of the Orb Land wallet.| 58 | 59 | 60 | ### createOrb 61 | 62 | Creates a new Orb, and emits an event with the Orb's address. 63 | 64 | 65 | ```solidity 66 | function createOrb(string memory name, string memory symbol, string memory tokenURI) external virtual; 67 | ``` 68 | **Parameters** 69 | 70 | |Name|Type|Description| 71 | |----|----|-----------| 72 | |`name`|`string`| Name of the Orb, used for display purposes. Suggestion: "NameOrb".| 73 | |`symbol`|`string`| Symbol of the Orb, used for display purposes. Suggestion: "ORB".| 74 | |`tokenURI`|`string`| Initial tokenURI of the Orb, used as part of ERC-721 tokenURI.| 75 | 76 | 77 | ### version 78 | 79 | Returns the version of the Orb Pond. Internal constant `_VERSION` will be increased with each upgrade. 80 | 81 | 82 | ```solidity 83 | function version() public view virtual override returns (uint256 orbPondVersion); 84 | ``` 85 | **Returns** 86 | 87 | |Name|Type|Description| 88 | |----|----|-----------| 89 | |`orbPondVersion`|`uint256`| Version of the Orb Pond contract.| 90 | 91 | 92 | -------------------------------------------------------------------------------- /docs/src/src/test-upgrades/OrbTestUpgrade.sol/contract.OrbTestUpgrade.md: -------------------------------------------------------------------------------- 1 | # OrbTestUpgrade 2 | [Git Source](https://github.com/orbland/orb/blob/a97224f7f48993b3e85f6cac56cd5342ebaa9cd0/src/test-upgrades/OrbTestUpgrade.sol) 3 | 4 | **Inherits:** 5 | [OrbV2](/src/OrbV2.sol/contract.OrbV2.md) 6 | 7 | **Authors:** 8 | Jonas Lekevicius, Eric Wall 9 | 10 | The Orb is issued by a Creator: the user who swore an Orb Oath together with a date until which the Oath 11 | will be honored. The Creator can list the Orb for sale at a fixed price, or run an auction for it. The user 12 | acquiring the Orb is known as the Keeper. The Keeper always has an Orb sale price set and is paying 13 | Harberger tax based on their set price and a tax rate set by the Creator. This tax is accounted for per 14 | second, and the Keeper must have enough funds on this contract to cover their ownership; otherwise the Orb 15 | is re-auctioned, delivering most of the auction proceeds to the previous Keeper. The Orb also has a 16 | cooldown that allows the Keeper to invoke the Orb — ask the Creator a question and receive their response, 17 | based on conditions set in the Orb Oath. Invocation and response hashes and timestamps are tracked in an 18 | Orb Invocation Registry. 19 | 20 | *Supports ERC-721 interface, including metadata, but reverts on all transfers and approvals. Uses 21 | `Ownable`'s `owner()` to identify the Creator of the Orb. Uses a custom `UUPSUpgradeable` implementation to 22 | allow upgrades, if they are requested by the Creator and executed by the Keeper. The Orb is created as an 23 | ERC-1967 proxy to an `Orb` implementation by the `OrbPond` contract, which is also used to track allowed 24 | Orb upgrades and keeps a reference to an `OrbInvocationRegistry` used by this Orb. 25 | Test Upgrade adds a new storage variable `number`, settable with `setNumber`, changes Orb name and symbol, 26 | and allows the Creator to set the cleartext maximum length to zero. FOR TESTING ONLY!* 27 | 28 | 29 | ## State Variables 30 | ### _VERSION 31 | Orb version. 32 | 33 | 34 | ```solidity 35 | uint256 private constant _VERSION = 100; 36 | ``` 37 | 38 | 39 | ### number 40 | Testing new storage variable in upgrade. It's a number! 41 | 42 | 43 | ```solidity 44 | uint256 public number; 45 | ``` 46 | 47 | 48 | ## Functions 49 | ### initializeTestUpgrade 50 | 51 | Re-initializes the contract after upgrade, sets initial number value 52 | 53 | 54 | ```solidity 55 | function initializeTestUpgrade(string memory newName_, string memory newSymbol_) public reinitializer(100); 56 | ``` 57 | **Parameters** 58 | 59 | |Name|Type|Description| 60 | |----|----|-----------| 61 | |`newName_`|`string`| New name of the Orb| 62 | |`newSymbol_`|`string`| New symbol of the Orb| 63 | 64 | 65 | ### setNumber 66 | 67 | Allows anyone to record a number! 68 | 69 | 70 | ```solidity 71 | function setNumber(uint256 newNumber) external; 72 | ``` 73 | **Parameters** 74 | 75 | |Name|Type|Description| 76 | |----|----|-----------| 77 | |`newNumber`|`uint256`| New number value!| 78 | 79 | 80 | ### version 81 | 82 | Returns the version of the Orb. Internal constant `_VERSION` will be increased with each upgrade. 83 | 84 | 85 | ```solidity 86 | function version() public view virtual override returns (uint256 orbVersion); 87 | ``` 88 | **Returns** 89 | 90 | |Name|Type|Description| 91 | |----|----|-----------| 92 | |`orbVersion`|`uint256`| Version of the Orb.| 93 | 94 | 95 | -------------------------------------------------------------------------------- /docs/src/src/test-upgrades/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Contents 4 | - [OrbInvocationRegistryTestUpgrade](OrbInvocationRegistryTestUpgrade.sol/contract.OrbInvocationRegistryTestUpgrade.md) 5 | - [OrbInvocationTipJarTestUpgrade](OrbInvocationTipJarTestUpgrade.sol/contract.OrbInvocationTipJarTestUpgrade.md) 6 | - [OrbPondTestUpgrade](OrbPondTestUpgrade.sol/contract.OrbPondTestUpgrade.md) 7 | - [OrbTestUpgrade](OrbTestUpgrade.sol/contract.OrbTestUpgrade.md) 8 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | solc_version = '0.8.20' # Version of solc that we use 3 | evm_version = 'shanghai' 4 | remappings = [ # Libraries that we use from node_modules and are used by the smart contracts 5 | "forge-std/=lib/forge-std/src/", 6 | "ds-test/=lib/forge-std/lib/ds-test/src/", 7 | "openzeppelin-contracts/=lib/openzeppelin-contracts/", 8 | "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", 9 | ] 10 | optimizer = true # Enable or disable the solc optimizer 11 | optimizer_runs = 20_000 # The number of optimizer runs 12 | verbosity = 3 # The verbosity of tests 13 | bytecode_hash = "none" # For deterministic code 14 | block_timestamp = 10_000_000 # Timestamp for tests (non-zero) 15 | 16 | src = 'src' 17 | out = 'out' 18 | libs = ['lib'] 19 | test = 'test' 20 | 21 | # silence warnings 22 | # 2462 - ignored constructor visibility in OZ dependency 23 | ignored_error_codes = [2462] 24 | 25 | ffi = false 26 | 27 | [fuzz] 28 | runs = 10_000 29 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config" 2 | import "@nomicfoundation/hardhat-toolbox" 3 | import "@nomicfoundation/hardhat-verify" 4 | import "@nomiclabs/hardhat-solhint" 5 | import "@openzeppelin/hardhat-upgrades" 6 | import "@typechain/hardhat" 7 | import "hardhat-contract-sizer" 8 | import "hardhat-gas-reporter" 9 | import "hardhat-preprocessor" 10 | import "solidity-coverage" 11 | import "hardhat-deploy" 12 | 13 | import * as dotenv from "dotenv" 14 | dotenv.config() 15 | const { INFURA_API_KEY, ETHERSCAN_API_KEY, DEPLOYER_PRIVATE_KEY, CMC_API_KEY } = process.env 16 | const deployerPrivateKey = DEPLOYER_PRIVATE_KEY || "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" 17 | 18 | const config: HardhatUserConfig = { 19 | solidity: { 20 | version: "0.8.20", 21 | settings: { 22 | optimizer: { 23 | enabled: true, 24 | runs: 20_000, 25 | }, 26 | }, 27 | }, 28 | paths: { 29 | sources: "./src", // Use ./src rather than ./contracts as Hardhat expects 30 | cache: "./cache_hardhat", // Use a different cache for Hardhat than Foundry 31 | }, 32 | defaultNetwork: "hardhat", 33 | networks: { 34 | anvil: { 35 | url: "http://127.0.0.1:8545", 36 | accounts: { 37 | mnemonic: "test test test test test test test test test test test junk", 38 | path: "m/44'/60'/0'/0", 39 | initialIndex: 0, 40 | count: 20, 41 | passphrase: "", 42 | }, 43 | }, 44 | goerli: { 45 | url: `https://goerli.infura.io/v3/${INFURA_API_KEY}`, 46 | accounts: [deployerPrivateKey], 47 | }, 48 | sepolia: { 49 | url: `https://sepolia.infura.io/v3/${INFURA_API_KEY}`, 50 | accounts: [deployerPrivateKey], 51 | }, 52 | mainnet: { 53 | url: `https://mainnet.infura.io/v3/${INFURA_API_KEY}`, 54 | accounts: [deployerPrivateKey], 55 | }, 56 | }, 57 | etherscan: { 58 | apiKey: ETHERSCAN_API_KEY, 59 | }, 60 | gasReporter: { 61 | enabled: true, 62 | currency: "USD", 63 | coinmarketcap: CMC_API_KEY, 64 | }, 65 | } 66 | 67 | export default config 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "orb", 3 | "version": "1.0.0", 4 | "description": "Orb contract. Auction + Harberger taxed ownership + invocations.", 5 | "directories": { 6 | "lib": "lib", 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "compile": "hardhat compile", 11 | "test": "hardhat test --show-stack-traces", 12 | "node": "hardhat node" 13 | }, 14 | "author": "Jonas Lekevicius ", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.3", 18 | "@nomicfoundation/hardhat-ethers": "^3.0.4", 19 | "@nomicfoundation/hardhat-network-helpers": "^1.0.10", 20 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 21 | "@nomicfoundation/hardhat-verify": "^2.0.3", 22 | "@nomiclabs/hardhat-solhint": "^3.0.1", 23 | "@openzeppelin/hardhat-upgrades": "^3.0.2", 24 | "@typechain/ethers-v6": "^0.5.1", 25 | "@typechain/hardhat": "^9.1.0", 26 | "@types/chai": "^4.3.11", 27 | "@types/mocha": "^10.0.6", 28 | "@types/node": "^20.11.5", 29 | "chai": "^4.3.7", 30 | "dotenv": "^16.3.1", 31 | "eth-gas-reporter": "^0.2.25", 32 | "ethers": "^6.10.0", 33 | "hardhat": "^2.19.4", 34 | "hardhat-contract-sizer": "^2.10.0", 35 | "hardhat-deploy": "^0.11.45", 36 | "hardhat-gas-reporter": "^1.0.9", 37 | "hardhat-preprocessor": "^0.1.5", 38 | "solidity-coverage": "^0.8.4", 39 | "ts-node": "^10.9.2", 40 | "typechain": "^8.2.0", 41 | "typescript": "^5.3.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | forge-std/=lib/forge-std/src/ 2 | ds-test/=lib/forge-std/lib/ds-test/src/ 3 | openzeppelin-contracts/=lib/openzeppelin-contracts/ 4 | openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ 5 | -------------------------------------------------------------------------------- /script/LocalDeployBase.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | /* solhint-disable no-console */ 5 | import {console} from "../lib/forge-std/src/console.sol"; 6 | import {Script} from "../lib/forge-std/src/Script.sol"; 7 | import {ERC1967Proxy} from "../lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 8 | 9 | import {PaymentSplitter} from "../src/CustomPaymentSplitter.sol"; 10 | import {OrbPond} from "../src/OrbPond.sol"; 11 | import {OrbPondV2} from "../src/OrbPondV2.sol"; 12 | import {OrbInvocationRegistry} from "../src/OrbInvocationRegistry.sol"; 13 | import {OrbInvocationTipJar} from "../src/OrbInvocationTipJar.sol"; 14 | import {Orb} from "../src/Orb.sol"; 15 | import {OrbV2} from "../src/OrbV2.sol"; 16 | 17 | contract LocalDeployBase is Script { 18 | address public immutable orbLandAddress = 0x9F49230672c52A2b958F253134BB17Ac84d30833; 19 | 20 | // Deploy addresses 21 | PaymentSplitter public paymentSplitterImplementation; 22 | 23 | OrbInvocationRegistry public orbInvocationRegistryImplementation; 24 | OrbInvocationRegistry public orbInvocationRegistry; 25 | 26 | OrbInvocationTipJar public orbInvocationTipJarImplementation; 27 | OrbInvocationTipJar public orbInvocationTipJar; 28 | 29 | OrbPond public orbPondImplementation; 30 | OrbPondV2 public orbPondV2Implementation; 31 | OrbPondV2 public orbPond; 32 | 33 | Orb public orbImplementation; 34 | OrbV2 public orbV2Implementation; 35 | 36 | function deployContracts() public { 37 | orbInvocationRegistryImplementation = new OrbInvocationRegistry(); 38 | console.log("OrbInvocationRegistry implementation: ", address(orbInvocationRegistryImplementation)); 39 | 40 | orbPondImplementation = new OrbPond(); 41 | console.log("OrbPond V1 implementation: ", address(orbPondImplementation)); 42 | orbPondV2Implementation = new OrbPondV2(); 43 | console.log("OrbPond V2 implementation: ", address(orbPondV2Implementation)); 44 | 45 | orbImplementation = new Orb(); 46 | console.log("Orb V1 implementation: ", address(orbImplementation)); 47 | orbV2Implementation = new OrbV2(); 48 | console.log("Orb V2 implementation: ", address(orbV2Implementation)); 49 | 50 | paymentSplitterImplementation = new PaymentSplitter(); 51 | console.log("PaymentSplitter implementation: ", address(paymentSplitterImplementation)); 52 | 53 | orbInvocationTipJarImplementation = new OrbInvocationTipJar(); 54 | console.log("OrbInvocationTipJar implementation: ", address(orbInvocationTipJarImplementation)); 55 | 56 | ERC1967Proxy orbInvocationRegistryProxy = new ERC1967Proxy( 57 | address(orbInvocationRegistryImplementation), 58 | abi.encodeWithSelector(OrbInvocationRegistry.initialize.selector) 59 | ); 60 | orbInvocationRegistry = OrbInvocationRegistry(address(orbInvocationRegistryProxy)); 61 | console.log("OrbInvocationRegistry: ", address(orbInvocationRegistry)); 62 | 63 | ERC1967Proxy orbInvocationTipJarProxy = new ERC1967Proxy( 64 | address(orbInvocationTipJarImplementation), 65 | abi.encodeWithSelector(OrbInvocationTipJar.initialize.selector, address(orbLandAddress), 5_00) 66 | ); 67 | orbInvocationTipJar = OrbInvocationTipJar(address(orbInvocationTipJarProxy)); 68 | console.log("OrbInvocationTipJar: ", address(orbInvocationTipJar)); 69 | 70 | ERC1967Proxy orbPondProxy = new ERC1967Proxy( 71 | address(orbPondImplementation), 72 | abi.encodeWithSelector( 73 | OrbPond.initialize.selector, 74 | address(orbInvocationRegistry), 75 | address(paymentSplitterImplementation) 76 | ) 77 | ); 78 | bytes memory orbPondV1InitializeCalldata = 79 | abi.encodeWithSelector(Orb.initialize.selector, address(0), "", "", ""); 80 | OrbPond(address(orbPondProxy)).registerVersion(1, address(orbImplementation), orbPondV1InitializeCalldata); 81 | 82 | bytes memory orbPondV2InitializeCalldata = abi.encodeWithSelector(OrbV2.initializeV2.selector); 83 | OrbPond(address(orbPondProxy)).registerVersion(2, address(orbV2Implementation), orbPondV2InitializeCalldata); 84 | 85 | OrbPond(address(orbPondProxy)).upgradeToAndCall( 86 | address(orbPondV2Implementation), abi.encodeWithSelector(OrbPondV2.initializeV2.selector, 2) 87 | ); 88 | orbPond = OrbPondV2(address(orbPondProxy)); 89 | console.log("OrbPond (V2): ", address(orbPond)); 90 | } 91 | 92 | function run() external { 93 | uint256 deployerKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); 94 | vm.startBroadcast(deployerKey); 95 | deployContracts(); 96 | vm.stopBroadcast(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /script/LocalDeployOrb.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | /* solhint-disable no-console */ 5 | import {console} from "../lib/forge-std/src/console.sol"; 6 | import {Script} from "../lib/forge-std/src/Script.sol"; 7 | import {ERC1967Proxy} from "../lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 8 | 9 | import {PaymentSplitter} from "../src/CustomPaymentSplitter.sol"; 10 | import {OrbPond} from "../src/OrbPond.sol"; 11 | import {OrbPondV2} from "../src/OrbPondV2.sol"; 12 | import {OrbInvocationRegistry} from "../src/OrbInvocationRegistry.sol"; 13 | import {OrbInvocationTipJar} from "../src/OrbInvocationTipJar.sol"; 14 | import {Orb} from "../src/OrbV1Renamed.sol"; 15 | import {OrbV2} from "../src/OrbV2.sol"; 16 | 17 | contract LocalDeployOrb is Script { 18 | address public immutable creatorAddress = 0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc; 19 | address public immutable orbLandAddress = 0x9F49230672c52A2b958F253134BB17Ac84d30833; 20 | 21 | // Fixed base deployment addresses 22 | OrbInvocationRegistry public immutable orbInvocationRegistry = 23 | OrbInvocationRegistry(0xa513E6E4b8f2a923D98304ec87F64353C4D5C853); 24 | OrbPondV2 public immutable orbPond = OrbPondV2(0x8A791620dd6260079BF849Dc5567aDC3F2FdC318); 25 | OrbInvocationTipJar public immutable orbInvocationTipJar = 26 | OrbInvocationTipJar(0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6); 27 | 28 | PaymentSplitter public orbBeneficiary; 29 | OrbV2 public orb; 30 | 31 | string public orbName = "Test Orb"; 32 | string public orbSymbol = "ORB"; 33 | 34 | uint256 public immutable auctionStartingPrice = 0.1 ether; 35 | uint256 public immutable auctionMinimumBidStep = 0.1 ether; 36 | uint256 public immutable auctionMinimumDuration = 2 minutes; 37 | uint256 public immutable auctionKeeperMinimumDuration = 1 minutes; 38 | uint256 public immutable auctionBidExtension = 30 seconds; 39 | 40 | uint256 public immutable keeperTaxNumerator = 120_00; 41 | uint256 public immutable purchaseRoyaltyNumerator = 10_00; 42 | uint256 public immutable auctionRoyaltyNumerator = 30_00; 43 | 44 | uint256 public immutable cooldown = 2 minutes; 45 | uint256 public immutable responsePeriod = 7 * 24 * 60 * 60; 46 | uint256 public immutable flaggingPeriod = 5 minutes; 47 | uint256 public immutable cleartextMaximumLength = 280; 48 | 49 | bytes32 public immutable oathHash = 0x21144ebccf78f508f97c58c356209917be7cc4f7f8466da7b3bbacc1132af54c; 50 | uint256 public immutable honoredUntil = 1_800_000_000; 51 | 52 | function deployContracts() public { 53 | int256 initialVersion = vm.envInt("INITIAL_VERSION"); 54 | bool createAsV1 = initialVersion == 1; 55 | if (createAsV1) { 56 | orbPond.setOrbInitialVersion(1); 57 | } 58 | 59 | address[] memory beneficiaryAddresses = new address[](2); 60 | beneficiaryAddresses[0] = creatorAddress; 61 | beneficiaryAddresses[1] = orbLandAddress; 62 | uint256[] memory beneficiaryShares = new uint256[](2); 63 | beneficiaryShares[0] = 95; 64 | beneficiaryShares[1] = 5; 65 | 66 | orbPond.createOrb( 67 | beneficiaryAddresses, beneficiaryShares, orbName, orbSymbol, "https://static.orb.land/localhost/metadata" 68 | ); 69 | uint256 orbId = orbPond.orbCount() - 1; 70 | console.log("Orb: ", orbPond.orbs(orbId)); 71 | 72 | orb = OrbV2(orbPond.orbs(orbId)); 73 | orbBeneficiary = PaymentSplitter(payable(orb.beneficiary())); 74 | console.log("Orb beneficiary: ", address(orbBeneficiary)); 75 | 76 | console.log("Orb version: ", orb.version()); 77 | console.log("Orb implementation: ", orbPond.versions(orb.version())); 78 | 79 | orb.setAuctionParameters( 80 | auctionStartingPrice, 81 | auctionMinimumBidStep, 82 | auctionMinimumDuration, 83 | auctionKeeperMinimumDuration, 84 | auctionBidExtension 85 | ); 86 | 87 | if (createAsV1) { 88 | Orb orbV1 = Orb(orb); 89 | orbV1.setFees(keeperTaxNumerator, purchaseRoyaltyNumerator); 90 | orbV1.setCooldown(cooldown, flaggingPeriod); 91 | orbV1.setCleartextMaximumLength(cleartextMaximumLength); 92 | } else { 93 | orb.setFees(keeperTaxNumerator, purchaseRoyaltyNumerator, auctionRoyaltyNumerator); 94 | orb.setInvocationParameters(cooldown, responsePeriod, flaggingPeriod, cleartextMaximumLength); 95 | } 96 | 97 | orb.transferOwnership(creatorAddress); 98 | console.log("Orb ownership transferred to: ", creatorAddress); 99 | 100 | if (createAsV1) { 101 | orbPond.setOrbInitialVersion(2); 102 | } 103 | } 104 | 105 | function run() external { 106 | uint256 deployerKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); 107 | uint256 creatorKey = vm.envUint("CREATOR_PRIVATE_KEY"); 108 | 109 | int256 initialVersion = vm.envInt("INITIAL_VERSION"); 110 | bool createAsV1 = initialVersion == 1; 111 | 112 | vm.startBroadcast(deployerKey); 113 | deployContracts(); 114 | vm.stopBroadcast(); 115 | 116 | if (vm.envBool("SWEAR_OATH")) { 117 | console.log("Swearing Oath"); 118 | vm.startBroadcast(creatorKey); 119 | if (createAsV1) { 120 | Orb orbV1 = Orb(orb); 121 | orbV1.swearOath(oathHash, honoredUntil, responsePeriod); 122 | } else { 123 | orb.swearOath(oathHash, honoredUntil); 124 | } 125 | orb.listWithPrice(1 ether); 126 | orbInvocationTipJar.setMinimumTip(address(orb), 0.05 ether); 127 | orb.relinquish(false); 128 | vm.stopBroadcast(); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /script/contract-setup.sh: -------------------------------------------------------------------------------- 1 | npx hardhat run script/deploy-PaymentSplitterImplementation.ts --network anvil 2 | # Payment Splitter implementation deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 3 | 4 | npx hardhat run script/deploy-OrbInvocationRegistry.ts --network anvil 5 | # Orb Invocation Registry deployed to: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 6 | # Orb Invocation Registry Implementation deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 7 | 8 | ORB_INVOCATION_REGISTRY_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 \ 9 | PAYMENT_SPLITTER_IMPLEMENTATION_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3 \ 10 | npx hardhat run script/deploy-OrbPond.ts --network anvil 11 | # Orb Pond deployed to: 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 12 | # Orb Pond Implementation deployed to: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 13 | 14 | ORB_POND_ADDRESS=0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 \ 15 | npx hardhat run script/upgrade-OrbPondV2.ts --network anvil 16 | # Orb Pond V2 Implementation deployed to: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 17 | # Orb Pond upgraded to V2: 0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 18 | 19 | npx hardhat run script/deploy-OrbImplementation.ts --network anvil 20 | # Orb V1 implementation deployed to: 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 21 | 22 | npx hardhat run script/deploy-OrbV2Implementation.ts --network anvil 23 | # Orb V2 implementation deployed to: 0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6 24 | 25 | ORB_POND_ADDRESS=0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 \ 26 | ORB_V1_IMPLEMENTATION=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 \ 27 | ORB_V2_IMPLEMENTATION=0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6 \ 28 | npx hardhat run script/call-registerOrbVersions.ts --network anvil 29 | 30 | PLATFORM_WALLET=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 \ 31 | PLATFORM_FEE=500 \ 32 | npx hardhat run script/deploy-OrbInvocationTipJar.ts --network anvil 33 | # Orb Invocation Tip Jar deployed to: 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 34 | # Orb Invocation Tip Jar Implementation deployed to: 0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0 35 | 36 | # Contract Verifications 37 | 38 | PAYMENT_SPLITTER_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3 \ 39 | npx hardhat run script/verify-PaymentSplitterImplementation.ts --network anvil 40 | 41 | ORB_INVOCATION_REGISTRY_ADDRESS=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 \ 42 | npx hardhat run script/verify-OrbInvocationRegistryImplementation.ts --network anvil 43 | 44 | ORB_POND_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 \ 45 | npx hardhat run script/verify-OrbPondImplementation.ts --network anvil 46 | 47 | ORB_POND_ADDRESS=0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 \ 48 | npx hardhat run script/verify-OrbPondV2Implementation.ts --network anvil 49 | 50 | ORB_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853 \ 51 | npx hardhat run script/verify-OrbImplementation.ts --network anvil 52 | 53 | ORB_ADDRESS=0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6 \ 54 | npx hardhat run script/verify-OrbV2Implementation.ts --network anvil 55 | 56 | ORB_INVOCATION_TIP_JAR_ADDRESS=0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0 \ 57 | npx hardhat run script/verify-OrbInvocationTipJarImplementation.ts --network anvil 58 | 59 | # Orb Proxy Verification 60 | 61 | # ORB_IMPLEMENTATION_ADDRESS=0x10b0e79D892Be4345F800501378F1eD144824Ae1 \ 62 | # ORB_ADDRESS=0x7aAd4004576482a952B8382f0DcF71D64CF77f25 \ 63 | # BENEFICIARY=0xAE684f43F12758ED4eE6016Ae8cbfb61416B618a \ 64 | # ORB_NAME="TestOrb" \ 65 | # ORB_SYMBOL="ORB" \ 66 | # ORB_TOKEN_URI="https://static.orb.land/staging/metadata" \ 67 | # npx hardhat run script/verify-Orb.ts --network goerli 68 | -------------------------------------------------------------------------------- /script/deploy-OrbImplementation.ts: -------------------------------------------------------------------------------- 1 | import { TransactionResponse } from "ethers" 2 | import { ethers, upgrades } from "hardhat" 3 | 4 | async function main() { 5 | const Orb = await ethers.getContractFactory("src/Orb.sol:Orb") 6 | const orbDeployTx = (await upgrades.deployImplementation(Orb, { 7 | getTxResponse: true, 8 | unsafeAllow: ["delegatecall"], 9 | })) as TransactionResponse 10 | const orbTxReceipt = await orbDeployTx.wait() 11 | if (!orbTxReceipt) { 12 | throw new Error("Cannot verify tx receipt") 13 | } 14 | const orbAddress = orbTxReceipt.contractAddress 15 | console.log("Orb V1 implementation deployed to:", orbAddress) 16 | } 17 | 18 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 19 | main().catch((error) => { 20 | console.error(error) 21 | process.exitCode = 1 22 | }) 23 | -------------------------------------------------------------------------------- /script/deploy-OrbInvocationRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | 3 | async function main() { 4 | const OrbInvocationRegistry = await ethers.getContractFactory("OrbInvocationRegistry") 5 | const orbInvocationRegistry = await upgrades.deployProxy(OrbInvocationRegistry, { 6 | kind: "uups", 7 | initializer: "initialize", 8 | }) 9 | await orbInvocationRegistry.waitForDeployment() 10 | const orbInvocationRegistryAddress = await orbInvocationRegistry.getAddress() 11 | const orbInvocationRegistryImplementationAddress = await upgrades.erc1967.getImplementationAddress( 12 | orbInvocationRegistryAddress 13 | ) 14 | 15 | console.log("Orb Invocation Registry deployed to:", orbInvocationRegistryAddress) 16 | console.log("Orb Invocation Registry Implementation deployed to:", orbInvocationRegistryImplementationAddress) 17 | } 18 | 19 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 20 | main().catch((error) => { 21 | console.error(error) 22 | process.exitCode = 1 23 | }) 24 | -------------------------------------------------------------------------------- /script/deploy-OrbInvocationTipJar.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | 3 | async function main() { 4 | console.log("Platform Wallet Address:", process.env.PLATFORM_WALLET) 5 | console.log("Platform Fee as basis points:", process.env.PLATFORM_FEE) 6 | 7 | const OrbInvocationTipJar = await ethers.getContractFactory("OrbInvocationTipJar") 8 | const orbInvocationTipJar = await upgrades.deployProxy( 9 | OrbInvocationTipJar, 10 | [process.env.PLATFORM_WALLET, parseInt(process.env.PLATFORM_FEE as string)], 11 | { 12 | kind: "uups", 13 | initializer: "initialize", 14 | } 15 | ) 16 | await orbInvocationTipJar.waitForDeployment() 17 | const orbInvocationTipJarAddress = await orbInvocationTipJar.getAddress() 18 | const orbInvocationTipJarImplementationAddress = await upgrades.erc1967.getImplementationAddress( 19 | orbInvocationTipJarAddress 20 | ) 21 | 22 | console.log("Orb Invocation Tip Jar deployed to:", orbInvocationTipJarAddress) 23 | console.log("Orb Invocation Tip Jar Implementation deployed to:", orbInvocationTipJarImplementationAddress) 24 | } 25 | 26 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 27 | main().catch((error) => { 28 | console.error(error) 29 | process.exitCode = 1 30 | }) 31 | -------------------------------------------------------------------------------- /script/deploy-OrbPond.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat" 2 | 3 | async function main() { 4 | console.log("Orb Invocation Registry Address:", process.env.ORB_INVOCATION_REGISTRY_ADDRESS) 5 | console.log("Payment Splitter Implementation Address:", process.env.PAYMENT_SPLITTER_IMPLEMENTATION_ADDRESS) 6 | 7 | const OrbPond = await ethers.getContractFactory("OrbPond") 8 | const orbPond = await upgrades.deployProxy( 9 | OrbPond, 10 | [process.env.ORB_INVOCATION_REGISTRY_ADDRESS, process.env.PAYMENT_SPLITTER_IMPLEMENTATION_ADDRESS], 11 | { 12 | kind: "uups", 13 | initializer: "initialize", 14 | } 15 | ) 16 | await orbPond.waitForDeployment() 17 | const orbPondAddress = await orbPond.getAddress() 18 | const orbPondImplementationAddress = await upgrades.erc1967.getImplementationAddress(orbPondAddress) 19 | 20 | console.log("Orb Pond deployed to:", orbPondAddress) 21 | console.log("Orb Pond Implementation deployed to:", orbPondImplementationAddress) 22 | } 23 | 24 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 25 | main().catch((error) => { 26 | console.error(error) 27 | process.exitCode = 1 28 | }) 29 | -------------------------------------------------------------------------------- /script/deploy-OrbPondV2Implementation.ts: -------------------------------------------------------------------------------- 1 | import { TransactionResponse } from "ethers" 2 | import { ethers, upgrades } from "hardhat" 3 | 4 | async function main() { 5 | const OrbPondV2 = await ethers.getContractFactory("OrbPondV2") 6 | 7 | const orbPondDeployTx = (await upgrades.deployImplementation(OrbPondV2, { 8 | getTxResponse: true, 9 | })) as TransactionResponse 10 | const orbPondTxReceipt = await orbPondDeployTx.wait() 11 | if (!orbPondTxReceipt) { 12 | throw new Error("Cannot verify tx receipt") 13 | } 14 | const orbPondV2Implementation = orbPondTxReceipt.contractAddress as string 15 | console.log("OrbPondV2 implementation deployed to:", orbPondV2Implementation) 16 | 17 | const data = OrbPondV2.interface.encodeFunctionData("initializeV2(uint256)", [1]) 18 | console.log(`call: upgradeToAndCall(${orbPondV2Implementation}, ${data})`) 19 | } 20 | 21 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 22 | main().catch((error) => { 23 | console.error(error) 24 | process.exitCode = 1 25 | }) 26 | -------------------------------------------------------------------------------- /script/deploy-OrbV2Implementation.ts: -------------------------------------------------------------------------------- 1 | import { TransactionResponse } from "ethers" 2 | import { ethers, upgrades } from "hardhat" 3 | 4 | async function main() { 5 | const Orb = await ethers.getContractFactory("OrbV2") 6 | const orbDeployTx = (await upgrades.deployImplementation(Orb, { 7 | getTxResponse: true, 8 | unsafeAllow: ["delegatecall"], 9 | })) as TransactionResponse 10 | const orbTxReceipt = await orbDeployTx.wait() 11 | if (!orbTxReceipt) { 12 | throw new Error("Cannot verify tx receipt") 13 | } 14 | const orbAddress = orbTxReceipt.contractAddress 15 | console.log("Orb V2 implementation deployed to:", orbAddress) 16 | } 17 | 18 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 19 | main().catch((error) => { 20 | console.error(error) 21 | process.exitCode = 1 22 | }) 23 | -------------------------------------------------------------------------------- /script/deploy-PaymentSplitterImplementation.ts: -------------------------------------------------------------------------------- 1 | import { TransactionResponse } from "ethers" 2 | import { ethers, upgrades } from "hardhat" 3 | 4 | async function main() { 5 | const PaymentSplitter = await ethers.getContractFactory("src/CustomPaymentSplitter.sol:PaymentSplitter") 6 | const paymentSplitterDeployTx = (await upgrades.deployImplementation(PaymentSplitter, { 7 | getTxResponse: true, 8 | })) as TransactionResponse 9 | const paymentSplitterTxReceipt = await paymentSplitterDeployTx.wait() 10 | if (!paymentSplitterTxReceipt) { 11 | throw new Error("Cannot verify tx receipt") 12 | } 13 | const paymentSplitterAddress = paymentSplitterTxReceipt.contractAddress 14 | console.log("Payment Splitter implementation deployed to:", paymentSplitterAddress) 15 | } 16 | 17 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 18 | main().catch((error) => { 19 | console.error(error) 20 | process.exitCode = 1 21 | }) 22 | -------------------------------------------------------------------------------- /script/log-versionUpgradeCalls.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | 3 | async function main() { 4 | const OrbV2 = await ethers.getContractFactory("OrbV2") 5 | const orbV2Initializer = OrbV2.interface.encodeFunctionData("initializeV2()") 6 | console.log(orbV2Initializer) 7 | 8 | // const registerV1Tx = await orbPondV2.registerVersion(1n, orbV1ImplementationAddress, "0x") 9 | // const registerV2Tx = await orbPondV2.registerVersion(2n, orbV2ImplementationAddress, orbV2Initializer) 10 | // const setInitialVersionTx = await orbPondV2.setOrbInitialVersion(2n) 11 | } 12 | 13 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 14 | main().catch((error) => { 15 | console.error(error) 16 | process.exitCode = 1 17 | }) 18 | -------------------------------------------------------------------------------- /script/sepolia-setup.sh: -------------------------------------------------------------------------------- 1 | npx hardhat run script/deploy-PaymentSplitterImplementation.ts --network sepolia 2 | # Payment Splitter implementation deployed to: 0xF149432073BE88524D90669DA79684B9eB26F372 3 | 4 | npx hardhat run script/deploy-OrbInvocationRegistry.ts --network sepolia 5 | # Orb Invocation Registry deployed to: 0x573425F92bc94B00372Bc9Ef0DE95f393e0B6b1B 6 | # Orb Invocation Registry Implementation deployed to: 0x89f41A34B5d7B8d03ae1508ee7476c066b02847A 7 | 8 | ORB_INVOCATION_REGISTRY_ADDRESS=0x573425F92bc94B00372Bc9Ef0DE95f393e0B6b1B \ 9 | PAYMENT_SPLITTER_IMPLEMENTATION_ADDRESS=0xF149432073BE88524D90669DA79684B9eB26F372 \ 10 | npx hardhat run script/deploy-OrbPond.ts --network sepolia 11 | # Orb Pond deployed to: 0xa1Db3a8fE99e065c37BEd990D4321De047fbb281 12 | # Orb Pond Implementation deployed to: 0xFe2636c58DC08F0117690A80f24335Ef96e8eA0d 13 | 14 | ORB_POND_ADDRESS=0xa1Db3a8fE99e065c37BEd990D4321De047fbb281 \ 15 | npx hardhat run script/upgrade-OrbPondV2.ts --network sepolia 16 | # Orb Pond V2 Implementation deployed to: 0xda9FF398707D90af0Fa6885E4E1576abAd7c4286 17 | # Orb Pond upgraded to V2: 0xa1Db3a8fE99e065c37BEd990D4321De047fbb281 18 | 19 | npx hardhat run script/deploy-OrbImplementation.ts --network sepolia 20 | # Orb V1 implementation deployed to: 0xd09dEA92b1608bD3F9451ff2fa451688ce771EE6 21 | 22 | npx hardhat run script/deploy-OrbV2Implementation.ts --network sepolia 23 | # Orb V2 implementation deployed to: 0xb88923709222183dd17D58c6d6c78C77a7b4AC7a 24 | 25 | ORB_POND_ADDRESS=0xa1Db3a8fE99e065c37BEd990D4321De047fbb281 \ 26 | ORB_V1_IMPLEMENTATION=0xd09dEA92b1608bD3F9451ff2fa451688ce771EE6 \ 27 | ORB_V2_IMPLEMENTATION=0xb88923709222183dd17D58c6d6c78C77a7b4AC7a \ 28 | npx hardhat run script/call-registerOrbVersions.ts --network sepolia 29 | 30 | PLATFORM_WALLET=0xa46598F1446e64AB6609C2Bea970335D9d8099FE \ 31 | PLATFORM_FEE=500 \ 32 | npx hardhat run script/deploy-OrbInvocationTipJar.ts --network sepolia 33 | # Orb Invocation Tip Jar deployed to: 0x7ef56321ED1861e20CD3aEe923bd47230c63D094 34 | # Orb Invocation Tip Jar Implementation deployed to: 0x850Fd63A25990f5F1191dD4FD52edD0F391a6BC9 35 | 36 | # Contract Verifications 37 | 38 | PAYMENT_SPLITTER_ADDRESS=0xF149432073BE88524D90669DA79684B9eB26F372 \ 39 | npx hardhat run script/verify-PaymentSplitterImplementation.ts --network sepolia 40 | 41 | ORB_INVOCATION_REGISTRY_ADDRESS=0x89f41A34B5d7B8d03ae1508ee7476c066b02847A \ 42 | npx hardhat run script/verify-OrbInvocationRegistryImplementation.ts --network sepolia 43 | 44 | ORB_POND_ADDRESS=0xFe2636c58DC08F0117690A80f24335Ef96e8eA0d \ 45 | npx hardhat run script/verify-OrbPondImplementation.ts --network sepolia 46 | 47 | ORB_POND_ADDRESS=0xda9FF398707D90af0Fa6885E4E1576abAd7c4286 \ 48 | npx hardhat run script/verify-OrbPondV2Implementation.ts --network sepolia 49 | 50 | ORB_ADDRESS=0xd09dEA92b1608bD3F9451ff2fa451688ce771EE6 \ 51 | npx hardhat run script/verify-OrbImplementation.ts --network sepolia 52 | 53 | ORB_ADDRESS=0xb88923709222183dd17D58c6d6c78C77a7b4AC7a \ 54 | npx hardhat run script/verify-OrbV2Implementation.ts --network sepolia 55 | 56 | ORB_INVOCATION_TIP_JAR_ADDRESS=0x850Fd63A25990f5F1191dD4FD52edD0F391a6BC9 \ 57 | npx hardhat run script/verify-OrbInvocationTipJarImplementation.ts --network sepolia 58 | 59 | # Transfer Ownership 60 | 61 | ORB_INVOCATION_REGISTRY_ADDRESS=0x573425F92bc94B00372Bc9Ef0DE95f393e0B6b1B \ 62 | SAFE_ADDRESS=0x84EC4e9e9897C3EA25cfe9d34Ed427074aDAAA8D \ 63 | npx hardhat run script/transferOwnership-OrbInvocationRegistry.ts --network sepolia 64 | 65 | ORB_POND_ADDRESS=0xa1Db3a8fE99e065c37BEd990D4321De047fbb281 \ 66 | SAFE_ADDRESS=0x84EC4e9e9897C3EA25cfe9d34Ed427074aDAAA8D \ 67 | npx hardhat run script/transferOwnership-OrbPond.ts --network sepolia 68 | 69 | ORB_INVOCATION_TIP_JAR_ADDRESS=0x7ef56321ED1861e20CD3aEe923bd47230c63D094 \ 70 | SAFE_ADDRESS=0x84EC4e9e9897C3EA25cfe9d34Ed427074aDAAA8D \ 71 | npx hardhat run script/transferOwnership-OrbInvocationTipJar.ts --network sepolia 72 | 73 | # Orb Proxy Verification 74 | 75 | # ORB_IMPLEMENTATION_ADDRESS=0x10b0e79D892Be4345F800501378F1eD144824Ae1 \ 76 | # ORB_ADDRESS=0x7aAd4004576482a952B8382f0DcF71D64CF77f25 \ 77 | # BENEFICIARY=0xAE684f43F12758ED4eE6016Ae8cbfb61416B618a \ 78 | # ORB_NAME="TestOrb" \ 79 | # ORB_SYMBOL="ORB" \ 80 | # ORB_TOKEN_URI="https://static.orb.land/staging/metadata" \ 81 | # npx hardhat run script/verify-Orb.ts --network goerli 82 | -------------------------------------------------------------------------------- /script/transferOwnership-OrbInvocationRegistry.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | 3 | async function main() { 4 | console.log("Orb Invocation Registry Address:", process.env.ORB_INVOCATION_REGISTRY_ADDRESS) 5 | console.log("Safe Wallet Address:", process.env.SAFE_ADDRESS) 6 | if ( 7 | process.env.SAFE_ADDRESS === undefined || 8 | process.env.SAFE_ADDRESS === "" || 9 | process.env.ORB_INVOCATION_REGISTRY_ADDRESS === undefined || 10 | process.env.ORB_INVOCATION_REGISTRY_ADDRESS === "" 11 | ) { 12 | console.log("Please set the SAFE_ADDRESS and ORB_INVOCATION_REGISTRY_ADDRESS environment variables.") 13 | return 14 | } 15 | const orbInvocationRegistry = await ethers.getContractAt( 16 | "OrbInvocationRegistry", 17 | process.env.ORB_INVOCATION_REGISTRY_ADDRESS 18 | ) 19 | await orbInvocationRegistry.transferOwnership(process.env.SAFE_ADDRESS) 20 | } 21 | 22 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 23 | main().catch((error) => { 24 | console.error(error) 25 | process.exitCode = 1 26 | }) 27 | -------------------------------------------------------------------------------- /script/transferOwnership-OrbInvocationTipJar.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | 3 | async function main() { 4 | console.log("Orb Invocation Tip Jar Address:", process.env.ORB_INVOCATION_TIP_JAR_ADDRESS) 5 | console.log("Safe Wallet Address:", process.env.SAFE_ADDRESS) 6 | if ( 7 | process.env.SAFE_ADDRESS === undefined || 8 | process.env.SAFE_ADDRESS === "" || 9 | process.env.ORB_INVOCATION_TIP_JAR_ADDRESS === undefined || 10 | process.env.ORB_INVOCATION_TIP_JAR_ADDRESS === "" 11 | ) { 12 | console.log("Please set the SAFE_ADDRESS and ORB_INVOCATION_TIP_JAR_ADDRESS environment variables.") 13 | return 14 | } 15 | const orbInvocationTipJar = await ethers.getContractAt( 16 | "OrbInvocationTipJar", 17 | process.env.ORB_INVOCATION_TIP_JAR_ADDRESS 18 | ) 19 | await orbInvocationTipJar.transferOwnership(process.env.SAFE_ADDRESS) 20 | } 21 | 22 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 23 | main().catch((error) => { 24 | console.error(error) 25 | process.exitCode = 1 26 | }) 27 | -------------------------------------------------------------------------------- /script/transferOwnership-OrbPond.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat" 2 | 3 | async function main() { 4 | console.log("Orb Pond Address:", process.env.ORB_POND_ADDRESS) 5 | console.log("Safe Wallet Address:", process.env.SAFE_ADDRESS) 6 | if ( 7 | process.env.SAFE_ADDRESS === undefined || 8 | process.env.SAFE_ADDRESS === "" || 9 | process.env.ORB_POND_ADDRESS === undefined || 10 | process.env.ORB_POND_ADDRESS === "" 11 | ) { 12 | console.log("Please set the SAFE_ADDRESS and ORB_POND_ADDRESS environment variables.") 13 | return 14 | } 15 | const orbPond = await ethers.getContractAt("OrbPond", process.env.ORB_POND_ADDRESS) 16 | await orbPond.transferOwnership(process.env.SAFE_ADDRESS) 17 | } 18 | 19 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 20 | main().catch((error) => { 21 | console.error(error) 22 | process.exitCode = 1 23 | }) 24 | -------------------------------------------------------------------------------- /script/verify-Orb.ts: -------------------------------------------------------------------------------- 1 | import { network, run, ethers } from "hardhat" 2 | 3 | async function main() { 4 | const orbImplementationAddress = process.env.ORB_IMPLEMENTATION_ADDRESS 5 | const orbAddress = process.env.ORB_ADDRESS 6 | const beneficiary = process.env.BENEFICIARY 7 | const orbName = process.env.ORB_NAME 8 | const orbSymbol = process.env.ORB_SYMBOL 9 | const orbTokenURI = process.env.ORB_TOKEN_URI 10 | 11 | console.log("Orb Implementation Address:", orbImplementationAddress) 12 | console.log("Orb Address:", orbAddress) 13 | console.log("Beneficiary:", beneficiary) 14 | console.log("Orb Name:", orbName) 15 | console.log("Orb Symbol:", orbSymbol) 16 | console.log("Orb Token URI:", orbTokenURI) 17 | 18 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 19 | console.log(`Beginning Orb contract ${orbAddress} verification on ${network.name} Etherscan...`) 20 | 21 | // bytes memory initializeCalldata = 22 | // abi.encodeWithSelector(IOrb.initialize.selector, beneficiary, name, symbol, tokenURI); 23 | // ERC1967Proxy proxy = new ERC1967Proxy(versions[1], initializeCalldata); 24 | 25 | const iface = new ethers.Interface(["function initialize(address,string,string,string)"]) 26 | // console.log("sighash", iface.getFunction("initialize")?.selector) // 5f1e6f6d ? 27 | 28 | // But also, you would often need/want to encode parameters 29 | const initializationCalldata = iface.encodeFunctionData("initialize", [ 30 | beneficiary, 31 | orbName, 32 | orbSymbol, 33 | orbTokenURI, 34 | ]) 35 | console.log("initializationCalldata", initializationCalldata) 36 | 37 | await run(`verify:verify`, { 38 | address: orbAddress, 39 | constructorArguments: [orbImplementationAddress, initializationCalldata], 40 | }) 41 | console.log(`Contract verification complete.`) 42 | } else { 43 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 44 | } 45 | } 46 | 47 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 48 | main().catch((error) => { 49 | console.error(error) 50 | process.exitCode = 1 51 | }) 52 | -------------------------------------------------------------------------------- /script/verify-OrbImplementation.ts: -------------------------------------------------------------------------------- 1 | import { network, run } from "hardhat" 2 | 3 | async function main() { 4 | const orbAddress = process.env.ORB_ADDRESS 5 | console.log("Orb Address:", orbAddress) 6 | 7 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 8 | console.log(`Beginning OrbV1 contract ${orbAddress} verification on ${network.name} Etherscan...`) 9 | await run(`verify:verify`, { 10 | address: orbAddress, 11 | constructorArguments: [], 12 | }) 13 | console.log(`Contract verification complete.`) 14 | } else { 15 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 16 | } 17 | } 18 | 19 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 20 | main().catch((error) => { 21 | console.error(error) 22 | process.exitCode = 1 23 | }) 24 | -------------------------------------------------------------------------------- /script/verify-OrbInvocationRegistryImplementation.ts: -------------------------------------------------------------------------------- 1 | import { network, run } from "hardhat" 2 | 3 | async function main() { 4 | const orbInvocationRegistryImplementationAddress = process.env.ORB_INVOCATION_REGISTRY_ADDRESS 5 | console.log("Orb Invocation Registry Address:", orbInvocationRegistryImplementationAddress) 6 | 7 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 8 | console.log( 9 | `Beginning OrbInvocationRegistry contract ${orbInvocationRegistryImplementationAddress} verification on ${network.name} Etherscan...` 10 | ) 11 | await run(`verify:verify`, { 12 | address: orbInvocationRegistryImplementationAddress, 13 | constructorArguments: [], 14 | }) 15 | console.log(`Contract verification complete.`) 16 | } else { 17 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 18 | } 19 | } 20 | 21 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 22 | main().catch((error) => { 23 | console.error(error) 24 | process.exitCode = 1 25 | }) 26 | -------------------------------------------------------------------------------- /script/verify-OrbInvocationTipJarImplementation.ts: -------------------------------------------------------------------------------- 1 | import { network, run } from "hardhat" 2 | 3 | async function main() { 4 | const orbInvocationTipJarImplementationAddress = process.env.ORB_INVOCATION_TIP_JAR_ADDRESS 5 | 6 | console.log("Orb Invocation Tip Jar Address:", orbInvocationTipJarImplementationAddress) 7 | 8 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 9 | console.log( 10 | `Beginning OrbInvocationTipJar contract ${orbInvocationTipJarImplementationAddress} verification on ${network.name} Etherscan...` 11 | ) 12 | await run(`verify:verify`, { 13 | address: orbInvocationTipJarImplementationAddress, 14 | constructorArguments: [], 15 | }) 16 | console.log(`Contract verification complete.`) 17 | } else { 18 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 19 | } 20 | } 21 | 22 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 23 | main().catch((error) => { 24 | console.error(error) 25 | process.exitCode = 1 26 | }) 27 | -------------------------------------------------------------------------------- /script/verify-OrbPondImplementation.ts: -------------------------------------------------------------------------------- 1 | import { network, run } from "hardhat" 2 | 3 | async function main() { 4 | const orbPondImplementationAddress = process.env.ORB_POND_ADDRESS 5 | 6 | console.log("Orb Pond Implementation Address:", orbPondImplementationAddress) 7 | 8 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 9 | console.log( 10 | `Beginning OrbPond contract ${orbPondImplementationAddress} verification on ${network.name} Etherscan...` 11 | ) 12 | await run(`verify:verify`, { 13 | address: orbPondImplementationAddress, 14 | constructorArguments: [], 15 | }) 16 | console.log(`Contract verification complete.`) 17 | } else { 18 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 19 | } 20 | } 21 | 22 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 23 | main().catch((error) => { 24 | console.error(error) 25 | process.exitCode = 1 26 | }) 27 | -------------------------------------------------------------------------------- /script/verify-OrbPondV2Implementation.ts: -------------------------------------------------------------------------------- 1 | import { network, run } from "hardhat" 2 | 3 | async function main() { 4 | const orbPondImplementationAddress = process.env.ORB_POND_ADDRESS 5 | 6 | console.log("Orb Pond V2 Implementation Address:", orbPondImplementationAddress) 7 | 8 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 9 | console.log( 10 | `Beginning OrbPondV2 contract ${orbPondImplementationAddress} verification on ${network.name} Etherscan...` 11 | ) 12 | await run(`verify:verify`, { 13 | address: orbPondImplementationAddress, 14 | constructorArguments: [], 15 | }) 16 | console.log(`Contract verification complete.`) 17 | } else { 18 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 19 | } 20 | } 21 | 22 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 23 | main().catch((error) => { 24 | console.error(error) 25 | process.exitCode = 1 26 | }) 27 | -------------------------------------------------------------------------------- /script/verify-OrbV2Implementation.ts: -------------------------------------------------------------------------------- 1 | import { network, run } from "hardhat" 2 | 3 | async function main() { 4 | const orbAddress = process.env.ORB_ADDRESS 5 | console.log("Orb Address:", orbAddress) 6 | 7 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 8 | console.log(`Beginning OrbV2 contract ${orbAddress} verification on ${network.name} Etherscan...`) 9 | await run(`verify:verify`, { 10 | address: orbAddress, 11 | constructorArguments: [], 12 | }) 13 | console.log(`Contract verification complete.`) 14 | } else { 15 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 16 | } 17 | } 18 | 19 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 20 | main().catch((error) => { 21 | console.error(error) 22 | process.exitCode = 1 23 | }) 24 | -------------------------------------------------------------------------------- /script/verify-PaymentSplitterImplementation.ts: -------------------------------------------------------------------------------- 1 | import { network, run } from "hardhat" 2 | 3 | async function main() { 4 | const paymentSplitterAddress = process.env.PAYMENT_SPLITTER_ADDRESS 5 | console.log("PaymentSplitter Address:", paymentSplitterAddress) 6 | 7 | if (network.name === "goerli" || network.name === "sepolia" || network.name === "mainnet") { 8 | console.log( 9 | `Beginning PaymentSplitter contract ${paymentSplitterAddress} verification on ${network.name} Etherscan...` 10 | ) 11 | await run(`verify:verify`, { 12 | address: paymentSplitterAddress, 13 | constructorArguments: [], 14 | }) 15 | console.log(`Contract verification complete.`) 16 | } else { 17 | console.log(`Cannot verify contract on ${network.name} Etherscan.`) 18 | } 19 | } 20 | 21 | // We recommend this pattern to be able to use async/await everywhere and properly handle errors. 22 | main().catch((error) => { 23 | console.error(error) 24 | process.exitCode = 1 25 | }) 26 | -------------------------------------------------------------------------------- /src/CustomPaymentSplitter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {PaymentSplitterUpgradeable} from 5 | "../lib/openzeppelin-contracts-upgradeable/contracts/finance/PaymentSplitterUpgradeable.sol"; 6 | 7 | /// @title CustomPaymentSplitter - Payment Splitter with initializer 8 | /// @author Jonas Lekevicius 9 | /// @dev This is a non-abstract version of the OpenZeppelin Contract `PaymentSplitterUpgradeable` contract that 10 | /// implements an initializer, and has a constructor to disable the initializer on base deployment. Meant to be 11 | /// used as an implementation to a EIP-1167 clone factory. This contract is not actually upgradeable despite 12 | /// the name of the base contract. 13 | /// @custom:security-contact security@orb.land 14 | contract PaymentSplitter is PaymentSplitterUpgradeable { 15 | /// @custom:oz-upgrades-unsafe-allow constructor 16 | constructor() { 17 | _disableInitializers(); 18 | } 19 | 20 | /// @dev Calls the initializer of the `PaymentSplitterUpgradeable` contract, with payees and their shares. 21 | /// @param payees_ Payees addresses. 22 | /// @param shares_ Payees shares. 23 | function initialize(address[] memory payees_, uint256[] memory shares_) public initializer { 24 | __PaymentSplitter_init(payees_, shares_); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/CustomUUPSUpgradeable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {IERC1822ProxiableUpgradeable} from 5 | "../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/draft-IERC1822Upgradeable.sol"; 6 | import {ERC1967UpgradeUpgradeable} from 7 | "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol"; 8 | 9 | // solhint-disable func-name-mixedcase 10 | 11 | /// @title CustomUUPSUpgradeable - UUPSUpgradeable without public functions 12 | /// @author Jonas Lekevicius 13 | /// @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade 14 | /// of an `ERC1967Proxy`, when this contract is set as the implementation behind such a proxy. This is a 15 | /// modified version of the OpenZeppelin Contract `UUPSUpgradeable` contract that does not expose any public 16 | /// functions, to allow custom upgradeability logic to be implemented in the `Orb` contract. 17 | /// Also, replaces string errors with custom errors. 18 | /// @custom:security-contact security@orb.land 19 | abstract contract UUPSUpgradeable is IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable { 20 | // Errors 21 | error RequiresCallViaDelegatecall(); 22 | error RequiresCallViaActiveProxy(); 23 | error CallViaDelegatecallDisallowed(); 24 | 25 | /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment 26 | address private immutable __self = address(this); 27 | 28 | // solhint-disable-next-line no-empty-blocks 29 | function __UUPSUpgradeable_init() internal onlyInitializing {} 30 | 31 | // solhint-disable-next-line no-empty-blocks 32 | function __UUPSUpgradeable_init_unchained() internal onlyInitializing {} 33 | 34 | /// @dev Check that the execution is being performed through a delegatecall call and that the execution context is 35 | /// a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the 36 | /// case for UUPS and transparent proxies that are using the current contract as their implementation. 37 | /// Execution of a function through ERC1167 minimal proxies (clones) would not normally pass this test, but is 38 | /// not guaranteed to fail. 39 | modifier onlyProxy() { 40 | if (address(this) == __self) { 41 | revert RequiresCallViaDelegatecall(); 42 | } 43 | if (_getImplementation() != __self) { 44 | revert RequiresCallViaActiveProxy(); 45 | } 46 | _; 47 | } 48 | 49 | /// @dev Check that the execution is not being performed through a delegate call. This allows a function to be 50 | /// on the implementing contract but not through proxies. 51 | modifier notDelegated() { 52 | if (address(this) != __self) { 53 | revert CallViaDelegatecallDisallowed(); 54 | } 55 | _; 56 | } 57 | 58 | /// @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the 59 | /// implementation. It is used to validate the implementation's compatibility when performing an upgrade. 60 | /// IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because 61 | /// this risks bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is 62 | /// critical that this function revert if invoked through a proxy. This is guaranteed by the `notDelegated` 63 | /// modifier. 64 | function proxiableUUID() external view virtual override notDelegated returns (bytes32) { 65 | return _IMPLEMENTATION_SLOT; 66 | } 67 | 68 | /// @dev This empty reserved space is put in place to allow future versions to add new variables without shifting 69 | /// down storage in the inheritance chain. 70 | /// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps 71 | uint256[50] private __gap; 72 | } 73 | -------------------------------------------------------------------------------- /src/IOwnershipTransferrable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | interface IOwnershipTransferrable { 5 | function transferOwnership(address newOwner) external; 6 | } 7 | -------------------------------------------------------------------------------- /src/OrbPond.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {ERC1967Proxy} from "../lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 5 | import {OwnableUpgradeable} from "../lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; 6 | import {UUPSUpgradeable} from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; 7 | import {ClonesUpgradeable} from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/ClonesUpgradeable.sol"; 8 | 9 | import {PaymentSplitter} from "./CustomPaymentSplitter.sol"; 10 | import {IOwnershipTransferrable} from "./IOwnershipTransferrable.sol"; 11 | import {Orb} from "./Orb.sol"; 12 | 13 | /// @title Orb Pond - The Orb Factory 14 | /// @author Jonas Lekevicius 15 | /// @notice Orbs come from a Pond. The Orb Pond is used to efficiently create new Orbs, and track “official” Orbs, 16 | /// supported by the Orb Land system. The Orb Pond is also used to register allowed Orb upgrade 17 | /// implementations, and keeps a reference to an Orb Invocation Registry used by all Orbs created with this 18 | /// Orb Pond. 19 | /// @dev Uses `Ownable`'s `owner()` to limit the creation of new Orbs to the administrator and for upgrades. 20 | /// @custom:security-contact security@orb.land 21 | contract OrbPond is OwnableUpgradeable, UUPSUpgradeable { 22 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 23 | // EVENTS 24 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 25 | 26 | event OrbCreation(uint256 indexed orbId, address indexed orbAddress); 27 | event VersionRegistration(uint256 indexed versionNumber, address indexed implementation); 28 | 29 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 30 | // ERRORS 31 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 32 | 33 | error InvalidVersion(); 34 | 35 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 36 | // STORAGE 37 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 38 | 39 | /// Orb Pond version. Value: 1. 40 | uint256 private constant _VERSION = 1; 41 | 42 | /// The mapping of Orb ids to Orbs. Increases monotonically. 43 | mapping(uint256 orbId => address orbAddress) public orbs; 44 | /// The number of Orbs created so far, used to find the next Orb id. 45 | uint256 public orbCount; 46 | 47 | /// The mapping of version numbers to implementation contract addresses. Looked up by Orbs to find implementation 48 | /// contracts for upgrades. 49 | mapping(uint256 versionNumber => address implementation) public versions; 50 | /// The mapping of version numbers to upgrade calldata. Looked up by Orbs to find initialization calldata for 51 | /// upgrades. 52 | mapping(uint256 versionNumber => bytes upgradeCalldata) public upgradeCalldata; 53 | /// The highest version number so far. Could be used for new Orb creation. 54 | uint256 public latestVersion; 55 | 56 | /// The address of the Orb Invocation Registry, used to register Orb invocations and responses. 57 | address public registry; 58 | /// The address of the PaymentSplitter implementation contract, used to create new PaymentSplitters. 59 | address public paymentSplitterImplementation; 60 | 61 | /// Gap used to prevent storage collisions. 62 | uint256[100] private __gap; 63 | 64 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 65 | // INITIALIZER 66 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 67 | 68 | /// @custom:oz-upgrades-unsafe-allow constructor 69 | constructor() { 70 | _disableInitializers(); 71 | } 72 | 73 | /// @notice Initializes the contract, setting the `owner` and `registry` variables. 74 | /// @param registry_ The address of the Orb Invocation Registry. 75 | /// @param paymentSplitterImplementation_ The address of the PaymentSplitter implementation contract. 76 | function initialize(address registry_, address paymentSplitterImplementation_) public initializer { 77 | __Ownable_init(); 78 | __UUPSUpgradeable_init(); 79 | 80 | registry = registry_; 81 | paymentSplitterImplementation = paymentSplitterImplementation_; 82 | } 83 | 84 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 85 | // FUNCTIONS: ORB CREATION 86 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 87 | 88 | /// @notice Creates a new Orb together with a PaymentSplitter, and emits an event with the Orb's address. 89 | /// @param payees_ Beneficiaries of the Orb's PaymentSplitter. 90 | /// @param shares_ Shares of the Orb's PaymentSplitter. 91 | /// @param name Name of the Orb, used for display purposes. Suggestion: "NameOrb". 92 | /// @param symbol Symbol of the Orb, used for display purposes. Suggestion: "ORB". 93 | /// @param tokenURI Initial tokenURI of the Orb, used as part of ERC-721 tokenURI. 94 | function createOrb( 95 | address[] memory payees_, 96 | uint256[] memory shares_, 97 | string memory name, 98 | string memory symbol, 99 | string memory tokenURI 100 | ) external virtual onlyOwner { 101 | address beneficiary = ClonesUpgradeable.clone(paymentSplitterImplementation); 102 | PaymentSplitter(payable(beneficiary)).initialize(payees_, shares_); 103 | 104 | bytes memory initializeCalldata = abi.encodeCall(Orb.initialize, (beneficiary, name, symbol, tokenURI)); 105 | ERC1967Proxy proxy = new ERC1967Proxy(versions[1], initializeCalldata); 106 | orbs[orbCount] = address(proxy); 107 | IOwnershipTransferrable(orbs[orbCount]).transferOwnership(msg.sender); 108 | 109 | emit OrbCreation(orbCount, address(proxy)); 110 | 111 | orbCount++; 112 | } 113 | 114 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 115 | // FUNCTIONS: UPGRADING 116 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 117 | 118 | /// @notice Registers a new version of the Orb implementation contract. The version number must be exactly one 119 | /// higher than the previous version number, and the implementation address must be non-zero. Versions can 120 | /// be un-registered by setting the implementation address to 0; only the latest version can be 121 | /// un-registered. 122 | /// @param version_ Version number of the new implementation contract. 123 | /// @param implementation_ Address of the new implementation contract. 124 | /// @param upgradeCalldata_ Initialization calldata to be used for upgrading to the new implementation contract. 125 | function registerVersion(uint256 version_, address implementation_, bytes calldata upgradeCalldata_) 126 | external 127 | virtual 128 | onlyOwner 129 | { 130 | if (version_ < latestVersion && implementation_ == address(0)) { 131 | revert InvalidVersion(); 132 | } 133 | if (version_ > latestVersion + 1) { 134 | revert InvalidVersion(); 135 | } 136 | versions[version_] = implementation_; 137 | upgradeCalldata[version_] = upgradeCalldata_; 138 | if (version_ > latestVersion) { 139 | latestVersion = version_; 140 | } 141 | if (version_ == latestVersion && implementation_ == address(0)) { 142 | latestVersion--; 143 | } 144 | emit VersionRegistration(version_, implementation_); 145 | } 146 | 147 | /// @notice Returns the version of the Orb Pond. Internal constant `_VERSION` will be increased with each upgrade. 148 | /// @return orbPondVersion Version of the Orb Pond contract. 149 | function version() public view virtual returns (uint256 orbPondVersion) { 150 | return _VERSION; 151 | } 152 | 153 | /// @dev Authorizes `owner()` to upgrade this OrbPond contract. 154 | // solhint-disable no-empty-blocks 155 | function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} 156 | } 157 | -------------------------------------------------------------------------------- /src/OrbPondV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {ERC1967Proxy} from "../lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 5 | import {ClonesUpgradeable} from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/ClonesUpgradeable.sol"; 6 | 7 | import {PaymentSplitter} from "./CustomPaymentSplitter.sol"; 8 | import {OrbPond} from "./OrbPond.sol"; 9 | import {IOwnershipTransferrable} from "./IOwnershipTransferrable.sol"; 10 | import {Orb} from "./Orb.sol"; 11 | 12 | /// @title Orb Pond - The Orb Factory 13 | /// @author Jonas Lekevicius 14 | /// @notice Orbs come from a Pond. The Orb Pond is used to efficiently create new Orbs, and track “official” Orbs, 15 | /// supported by the Orb Land system. The Orb Pond is also used to register allowed Orb upgrade 16 | /// implementations, and keeps a reference to an Orb Invocation Registry used by all Orbs created with this 17 | /// Orb Pond. 18 | /// @dev Uses `Ownable`'s `owner()` to limit the creation of new Orbs to the administrator and for upgrades. 19 | /// V2 adds these changes: 20 | /// - `orbInitialVersion` field for new Orb creation and `setOrbInitialVersion()` function to set it. This 21 | /// allows to specify which version of the Orb implementation to use for new Orbs. 22 | /// - `beneficiaryWithdrawalAddresses` mapping to authorize addresses to be used as 23 | /// `beneficiaryWithdrawalAddress` on Orbs, `authorizeWithdrawalAddress()` function to set it, and 24 | /// `beneficiaryWithdrawalAddressPermitted()` function to check if address is authorized. 25 | /// @custom:security-contact security@orb.land 26 | contract OrbPondV2 is OrbPond { 27 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 28 | // EVENTS 29 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 30 | 31 | event OrbInitialVersionUpdate(uint256 previousInitialVersion, uint256 indexed newInitialVersion); 32 | event WithdrawalAddressAuthorization(address indexed withdrawalAddress, bool indexed authorized); 33 | 34 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 35 | // STORAGE 36 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 37 | 38 | /// Orb Pond version. Value: 2. 39 | uint256 private constant _VERSION = 2; 40 | 41 | /// New Orb version 42 | uint256 public orbInitialVersion; 43 | 44 | /// Addresses authorized to be used as beneficiaryWithdrawal address 45 | mapping(address withdrawalAddress => bool isPermitted) public beneficiaryWithdrawalAddresses; 46 | 47 | /// Gap used to prevent storage collisions. 48 | uint256[100] private __gap; 49 | 50 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 51 | // INITIALIZER 52 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 53 | 54 | /// @custom:oz-upgrades-unsafe-allow constructor 55 | constructor() { 56 | _disableInitializers(); 57 | } 58 | 59 | /// @notice Reinitializes the contract with provided initial value for `orbInitialVersion`. 60 | /// @param orbInitialVersion_ Registered Orb implementation version to be used for new Orbs. 61 | function initializeV2(uint256 orbInitialVersion_) public reinitializer(2) { 62 | orbInitialVersion = orbInitialVersion_; 63 | } 64 | 65 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 66 | // FUNCTIONS: ORB CREATION 67 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 68 | 69 | /// @notice Creates a new Orb together with a PaymentSplitter, and emits an event with the Orb's address. 70 | /// @param payees_ Beneficiaries of the Orb's PaymentSplitter. 71 | /// @param shares_ Shares of the Orb's PaymentSplitter. 72 | /// @param name Name of the Orb, used for display purposes. Suggestion: "NameOrb". 73 | /// @param symbol Symbol of the Orb, used for display purposes. Suggestion: "ORB". 74 | /// @param tokenURI Initial tokenURI of the Orb, used as part of ERC-721 tokenURI. 75 | function createOrb( 76 | address[] memory payees_, 77 | uint256[] memory shares_, 78 | string memory name, 79 | string memory symbol, 80 | string memory tokenURI 81 | ) external virtual override onlyOwner { 82 | address beneficiary = ClonesUpgradeable.clone(paymentSplitterImplementation); 83 | PaymentSplitter(payable(beneficiary)).initialize(payees_, shares_); 84 | 85 | bytes memory initializeCalldata = abi.encodeCall(Orb.initialize, (beneficiary, name, symbol, tokenURI)); 86 | ERC1967Proxy proxy = new ERC1967Proxy(versions[orbInitialVersion], initializeCalldata); 87 | orbs[orbCount] = address(proxy); 88 | IOwnershipTransferrable(orbs[orbCount]).transferOwnership(msg.sender); 89 | 90 | emit OrbCreation(orbCount, address(proxy)); 91 | 92 | orbCount++; 93 | } 94 | 95 | /// @notice Sets the registered Orb implementation version to be used for new Orbs. 96 | /// @param orbInitialVersion_ Registered Orb implementation version number to be used for new Orbs. 97 | function setOrbInitialVersion(uint256 orbInitialVersion_) external virtual onlyOwner { 98 | if (orbInitialVersion_ > latestVersion) { 99 | revert InvalidVersion(); 100 | } 101 | uint256 previousInitialVersion = orbInitialVersion; 102 | orbInitialVersion = orbInitialVersion_; 103 | emit OrbInitialVersionUpdate(previousInitialVersion, orbInitialVersion_); 104 | } 105 | 106 | /// @notice Returns the version of the Orb Pond. Internal constant `_VERSION` will be increased with each upgrade. 107 | /// @return orbPondVersion Version of the Orb Pond contract. 108 | function version() public view virtual override returns (uint256 orbPondVersion) { 109 | return _VERSION; 110 | } 111 | 112 | /// @notice Returns if address can be used as beneficiary withdrawal address on Orbs. 113 | /// @param beneficiaryWithdrawalAddress Address to check. Zero address is always permitted. 114 | function beneficiaryWithdrawalAddressPermitted(address beneficiaryWithdrawalAddress) 115 | external 116 | virtual 117 | returns (bool isBeneficiaryWithdrawalAddressPermitted) 118 | { 119 | return 120 | beneficiaryWithdrawalAddress == address(0) || beneficiaryWithdrawalAddresses[beneficiaryWithdrawalAddress]; 121 | } 122 | 123 | /// @notice Allows the owner to authorize permitted beneficiary withdrawal addresses. 124 | /// @param addressToAuthorize Address to authorize (likely contract). 125 | /// @param authorizationValue Boolean value to set the authorization to. 126 | function authorizeWithdrawalAddress(address addressToAuthorize, bool authorizationValue) 127 | external 128 | virtual 129 | onlyOwner 130 | { 131 | beneficiaryWithdrawalAddresses[addressToAuthorize] = authorizationValue; 132 | emit WithdrawalAddressAuthorization(addressToAuthorize, authorizationValue); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/interfaces/IOrb.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {IERC165Upgradeable} from 5 | "../../lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol"; 6 | 7 | interface IOrb is IERC165Upgradeable { 8 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 9 | // VIEW FUNCTIONS 10 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | // Auction View Functions 13 | function auctionEndTime() external view returns (uint256); 14 | function leadingBidder() external view returns (address); 15 | function leadingBid() external view returns (uint256); 16 | function auctionBeneficiary() external view returns (address); 17 | function auctionStartingPrice() external view returns (uint256); 18 | function auctionMinimumBidStep() external view returns (uint256); 19 | function auctionMinimumDuration() external view returns (uint256); 20 | function auctionKeeperMinimumDuration() external view returns (uint256); 21 | function auctionBidExtension() external view returns (uint256); 22 | 23 | // Funding View Functions 24 | function fundsOf(address owner) external view returns (uint256); 25 | function lastSettlementTime() external view returns (uint256); 26 | function keeperSolvent() external view returns (bool); 27 | function keeperTaxNumerator() external view returns (uint256); 28 | function feeDenominator() external view returns (uint256); 29 | function keeperTaxPeriod() external view returns (uint256); 30 | 31 | // Purchasing View Functions 32 | function keeper() external view returns (address); 33 | function keeperReceiveTime() external view returns (uint256); 34 | function price() external view returns (uint256); 35 | function royaltyNumerator() external view returns (uint256); 36 | 37 | // Invoking and Responding View Functions 38 | function cooldown() external view returns (uint256); 39 | function flaggingPeriod() external view returns (uint256); 40 | function lastInvocationTime() external view returns (uint256); 41 | function cleartextMaximumLength() external view returns (uint256); 42 | 43 | // Orb Parameter View Functions 44 | function pond() external view returns (address); 45 | function creator() external view returns (address); 46 | function beneficiary() external view returns (address); 47 | function honoredUntil() external view returns (uint256); 48 | function responsePeriod() external view returns (uint256); 49 | 50 | // Upgrading View Functions 51 | function version() external view returns (uint256); 52 | function requestedUpgradeImplementation() external view returns (address); 53 | 54 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 55 | // FUNCTIONS 56 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 57 | 58 | function initialize(address beneficiary_, string memory name_, string memory symbol_, string memory tokenURI_) 59 | external; 60 | 61 | // Auction Functions 62 | function startAuction() external; 63 | function bid(uint256 amount, uint256 priceIfWon) external payable; 64 | function finalizeAuction() external; 65 | 66 | // Funding Functions 67 | function deposit() external payable; 68 | function withdraw(uint256 amount) external; 69 | function withdrawAll() external; 70 | function withdrawAllForBeneficiary() external; 71 | function settle() external; 72 | 73 | // Purchasing Functions 74 | function listWithPrice(uint256 listingPrice) external; 75 | function setPrice(uint256 newPrice) external; 76 | function purchase( 77 | uint256 newPrice, 78 | uint256 currentPrice, 79 | uint256 currentKeeperTaxNumerator, 80 | uint256 currentRoyaltyNumerator, 81 | uint256 currentCooldown, 82 | uint256 currentCleartextMaximumLength 83 | ) external payable; 84 | 85 | // Orb Ownership Functions 86 | function relinquish(bool withAuction) external; 87 | function foreclose() external; 88 | 89 | // Invoking Functions 90 | function setLastInvocationTime(uint256 timestamp) external; 91 | 92 | // Orb Parameter Functions 93 | function swearOath(bytes32 oathHash, uint256 newHonoredUntil, uint256 newResponsePeriod) external; 94 | function extendHonoredUntil(uint256 newHonoredUntil) external; 95 | function setTokenURI(string memory newTokenURI) external; 96 | function setAuctionParameters( 97 | uint256 newStartingPrice, 98 | uint256 newMinimumBidStep, 99 | uint256 newMinimumDuration, 100 | uint256 newKeeperMinimumDuration, 101 | uint256 newBidExtension 102 | ) external; 103 | function setFees(uint256 newKeeperTaxNumerator, uint256 newRoyaltyNumerator) external; 104 | function setCooldown(uint256 newCooldown, uint256 newFlaggingPeriod) external; 105 | function setCleartextMaximumLength(uint256 newCleartextMaximumLength) external; 106 | 107 | // Upgrading Functions 108 | function requestUpgrade(address requestedImplementation) external; 109 | function upgradeToNextVersion() external; 110 | } 111 | -------------------------------------------------------------------------------- /src/interfaces/IOrbInvocationRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {IERC165Upgradeable} from 5 | "../../lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol"; 6 | 7 | interface IOrbInvocationRegistry is IERC165Upgradeable { 8 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 9 | // VIEW FUNCTIONS 10 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | function invocations(address orb, uint256 invocationId) 13 | external 14 | view 15 | returns (address invoker, bytes32 contentHash, uint256 timestamp); 16 | function invocationCount(address orb) external view returns (uint256); 17 | 18 | function responses(address orb, uint256 invocationId) 19 | external 20 | view 21 | returns (bytes32 contentHash, uint256 timestamp); 22 | function responseFlagged(address orb, uint256 invocationId) external view returns (bool); 23 | function flaggedResponsesCount(address orb) external view returns (uint256); 24 | 25 | function authorizedContracts(address contractAddress) external view returns (bool); 26 | 27 | function version() external view returns (uint256); 28 | 29 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 30 | // FUNCTIONS 31 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 32 | 33 | function invokeWithCleartext(address orb, string memory cleartext) external; 34 | function invokeWithCleartextAndCall( 35 | address orb, 36 | string memory cleartext, 37 | address addressToCall, 38 | bytes memory dataToCall 39 | ) external; 40 | function invokeWithHash(address orb, bytes32 contentHash) external; 41 | function invokeWithHashAndCall(address orb, bytes32 contentHash, address addressToCall, bytes memory dataToCall) 42 | external; 43 | function respond(address orb, uint256 invocationId, bytes32 contentHash) external; 44 | function flagResponse(address orb, uint256 invocationId) external; 45 | } 46 | -------------------------------------------------------------------------------- /src/test-upgrades/OrbInvocationRegistryTestUpgrade.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {Orb} from "../Orb.sol"; 5 | import {OrbInvocationRegistry} from "../OrbInvocationRegistry.sol"; 6 | 7 | /// @title Orb Invocation Registry Test Upgrade - Record-keeping contract for Orb invocations and responses 8 | /// @author Jonas Lekevicius 9 | /// @notice The Orb Invocation Registry is used to track invocations and responses for any Orb. 10 | /// @dev `Orb`s using an `OrbInvocationRegistry` must implement `IOrb` interface. Uses `Ownable`'s `owner()` to 11 | /// guard upgrading. 12 | /// Test Upgrade records Late Response Receipts if the response is made after the response period. Together 13 | /// with the `LateResponseDeposit` contract, it can allow Creators to compensate Keepers for late responses. 14 | contract OrbInvocationRegistryTestUpgrade is OrbInvocationRegistry { 15 | struct LateResponseReceipt { 16 | uint256 lateDuration; 17 | uint256 price; 18 | uint256 keeperTaxNumerator; 19 | } 20 | 21 | error Unauthorized(); 22 | error LateResponseReceiptClaimed(uint256 invocationId); 23 | 24 | event LateResponse( 25 | address indexed orb, uint256 indexed invocationId, address indexed responder, uint256 lateDuration 26 | ); 27 | 28 | /// Orb Invocation Registry version. 29 | uint256 private constant _VERSION = 100; 30 | 31 | /// The address of the Late Response Deposit contract. 32 | address public lateResponseFund; 33 | /// Mapping for late response receipts. Used to track late response receipts for invocations that have not been 34 | mapping(address orb => mapping(uint256 invocationId => LateResponseReceipt receipt)) public lateResponseReceipts; 35 | /// Mapping for late response receipts. Used to track late response receipts for invocations that have not been 36 | mapping(address orb => mapping(uint256 invocationId => bool receiptClaimed)) public lateResponseReceiptClaimed; 37 | 38 | /// @custom:oz-upgrades-unsafe-allow constructor 39 | constructor() { 40 | _disableInitializers(); 41 | } 42 | 43 | /// @notice Re-initializes the contract after upgrade 44 | /// @param lateResponseFund_ The address of the Late Response Compensation Fund. 45 | function initializeTestUpgrade(address lateResponseFund_) public reinitializer(100) { 46 | lateResponseFund = lateResponseFund_; 47 | } 48 | 49 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 50 | // FUNCTIONS: RESPONDING AND FLAGGING 51 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 52 | 53 | /// @notice The Orb creator can use this function to respond to any existing invocation, no matter how long ago 54 | /// it was made. A response to an invocation can only be written once. There is no way to record response 55 | /// cleartext on-chain. 56 | /// @dev Emits `Response`, and sometimes `LateResponse` if the response was made after the response period. 57 | /// @param invocationId Id of an invocation to which the response is being made. 58 | /// @param contentHash keccak256 hash of the response text. 59 | function respond(address orb, uint256 invocationId, bytes32 contentHash) 60 | external 61 | virtual 62 | override 63 | onlyCreator(orb) 64 | { 65 | if (invocationId > invocationCount[orb] || invocationId == 0) { 66 | revert InvocationNotFound(orb, invocationId); 67 | } 68 | if (_responseExists(orb, invocationId)) { 69 | revert ResponseExists(orb, invocationId); 70 | } 71 | 72 | responses[orb][invocationId] = ResponseData(contentHash, block.timestamp); 73 | 74 | uint256 invocationTime = invocations[orb][invocationId].timestamp; 75 | uint256 responseDuration = block.timestamp - invocationTime; 76 | if (responseDuration > Orb(orb).responsePeriod()) { 77 | uint256 lateDuration = responseDuration - Orb(orb).responsePeriod(); 78 | lateResponseReceipts[orb][invocationId] = 79 | LateResponseReceipt(lateDuration, Orb(orb).price(), Orb(orb).keeperTaxNumerator()); 80 | emit LateResponse(orb, invocationId, msg.sender, lateDuration); 81 | } 82 | 83 | emit Response(orb, invocationId, msg.sender, block.timestamp, contentHash); 84 | } 85 | 86 | function setLateResponseReceiptClaimed(address orb, uint256 invocationId) external { 87 | if (msg.sender != lateResponseFund) { 88 | revert Unauthorized(); 89 | } 90 | if (lateResponseReceipts[orb][invocationId].lateDuration == 0) { 91 | revert ResponseNotFound(orb, invocationId); 92 | } 93 | if (!lateResponseReceiptClaimed[orb][invocationId]) { 94 | revert LateResponseReceiptClaimed(invocationId); 95 | } 96 | 97 | lateResponseReceiptClaimed[orb][invocationId] = true; 98 | } 99 | 100 | /// @notice Returns the version of the Orb Invocation Registry. Internal constant `_VERSION` will be increased with 101 | /// each upgrade. 102 | /// @return orbInvocationRegistryVersion Version of the Orb Invocation Registry contract. 103 | function version() public view virtual override returns (uint256 orbInvocationRegistryVersion) { 104 | return _VERSION; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/test-upgrades/OrbInvocationTipJarTestUpgrade.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {Orb} from "../Orb.sol"; 5 | import {OrbInvocationTipJar} from "../OrbInvocationTipJar.sol"; 6 | 7 | /// @title Orb Invocation Tip Jar Test Upgrade - A contract for suggesting Orb invocations and tipping Orb keepers 8 | /// @author Jonas Lekevicius 9 | /// @notice This contract allows anyone to suggest an invocation to an Orb and optionally tip the Orb keeper 10 | /// Test Upgrade requires all tips to be multiples of fixed amount of ETH. 11 | contract OrbInvocationTipJarTestUpgrade is OrbInvocationTipJar { 12 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 13 | // STORAGE 14 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 15 | 16 | /// Orb Invocation Tip Jar version. 17 | uint256 private constant _VERSION = 100; 18 | 19 | /// An amount of ETH that is used as a tip modulo. All tips must be multiples of this amount. 20 | uint256 public tipModulo; 21 | 22 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 23 | // EVENTS 24 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 25 | 26 | event TipModuloUpdate(uint256 previousTipModulo, uint256 indexed newTipModulo); 27 | 28 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 29 | // ERRORS 30 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 31 | 32 | error TipNotAModuloMultiple(uint256 tip, uint256 tipModulo); 33 | error InvalidTipModulo(); 34 | 35 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 36 | // FUNCTIONS 37 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 38 | 39 | /// @custom:oz-upgrades-unsafe-allow constructor 40 | constructor() { 41 | _disableInitializers(); 42 | } 43 | 44 | /// @notice Re-initializes the contract after upgrade 45 | /// @param tipModulo_ An amount of ETH that is used as a tip modulo. 46 | function initializeTestUpgrade(uint256 tipModulo_) public reinitializer(100) { 47 | setTipModulo(tipModulo_); 48 | } 49 | 50 | /// @notice Tips an orb keeper to invoke their orb with a specific content hash 51 | /// @param orb The address of the orb 52 | /// @param invocationHash The invocation content hash 53 | function tipInvocation(address orb, bytes32 invocationHash) public payable virtual override { 54 | uint256 _minimumTip = minimumTips[orb]; 55 | if (msg.value < _minimumTip) { 56 | revert InsufficientTip(msg.value, _minimumTip); 57 | } 58 | uint256 _tipModulo = tipModulo; 59 | if (msg.value % _tipModulo != 0) { 60 | revert TipNotAModuloMultiple(msg.value, _tipModulo); 61 | } 62 | if (claimedInvocations[orb][invocationHash] > 0) { 63 | revert InvocationAlreadyClaimed(); 64 | } 65 | 66 | totalTips[orb][invocationHash] += msg.value; 67 | tipperTips[msg.sender][orb][invocationHash] += msg.value; 68 | 69 | emit TipDeposit(orb, invocationHash, msg.sender, msg.value); 70 | } 71 | 72 | function setTipModulo(uint256 tipModulo_) public virtual onlyOwner { 73 | if (tipModulo_ == 0) { 74 | revert InvalidTipModulo(); 75 | } 76 | uint256 _previousTipModulo = tipModulo; 77 | tipModulo = tipModulo_; 78 | emit TipModuloUpdate(_previousTipModulo, tipModulo_); 79 | } 80 | 81 | /// @notice Returns the version of the Orb Invocation TipJar. Internal constant `_VERSION` will be increased with 82 | /// each upgrade. 83 | /// @return orbInvocationTipJarVersion Version of the Orb Invocation TipJar contract. 84 | function version() public view virtual override returns (uint256 orbInvocationTipJarVersion) { 85 | return _VERSION; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test-upgrades/OrbPondTestUpgrade.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {ERC1967Proxy} from "../../lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 5 | import {ClonesUpgradeable} from "../../lib/openzeppelin-contracts-upgradeable/contracts/proxy/ClonesUpgradeable.sol"; 6 | 7 | import {PaymentSplitter} from "../CustomPaymentSplitter.sol"; 8 | import {IOwnershipTransferrable} from "../IOwnershipTransferrable.sol"; 9 | import {Orb} from "../Orb.sol"; 10 | import {OrbPondV2} from "../OrbPondV2.sol"; 11 | 12 | /// @title Orb Pond - The Orb Factory 13 | /// @author Jonas Lekevicius 14 | /// @notice Orbs come from a Pond. The Orb Pond is used to efficiently create new Orbs, and track “official” Orbs, 15 | /// supported by the Orb Land system. The Orb Pond is also used to register allowed Orb upgrade 16 | /// implementations, and keeps a reference to an Orb Invocation Registry used by all Orbs created with this 17 | /// Orb Pond. 18 | /// @dev Uses `Ownable`'s `owner()` to limit the creation of new Orbs to the administrator and for upgrades. 19 | /// Test Upgrade allows anyone to create orbs, not just the owner, automatically splitting proceeds between the 20 | /// creator and the Orb Land wallet. 21 | contract OrbPondTestUpgrade is OrbPondV2 { 22 | /// Orb Pond version. 23 | uint256 private constant _VERSION = 100; 24 | 25 | address public orbLandWallet; 26 | 27 | /// @custom:oz-upgrades-unsafe-allow constructor 28 | constructor() { 29 | _disableInitializers(); 30 | } 31 | 32 | /// @notice Re-initializes the contract after upgrade 33 | /// @param orbLandWallet_ The address of the Orb Land wallet. 34 | function initializeTestUpgrade(address orbLandWallet_) public reinitializer(100) { 35 | orbLandWallet = orbLandWallet_; 36 | } 37 | 38 | /// @notice Creates a new Orb, and emits an event with the Orb's address. 39 | /// @param name Name of the Orb, used for display purposes. Suggestion: "NameOrb". 40 | /// @param symbol Symbol of the Orb, used for display purposes. Suggestion: "ORB". 41 | /// @param tokenURI Initial tokenURI of the Orb, used as part of ERC-721 tokenURI. 42 | function createOrb(string memory name, string memory symbol, string memory tokenURI) external virtual { 43 | address[] memory beneficiaryAddresses = new address[](2); 44 | beneficiaryAddresses[0] = msg.sender; 45 | beneficiaryAddresses[1] = orbLandWallet; 46 | uint256[] memory beneficiaryShares = new uint256[](2); 47 | beneficiaryShares[0] = 95; 48 | beneficiaryShares[1] = 5; 49 | 50 | address beneficiary = ClonesUpgradeable.clone(paymentSplitterImplementation); 51 | PaymentSplitter(payable(beneficiary)).initialize(beneficiaryAddresses, beneficiaryShares); 52 | 53 | bytes memory initializeCalldata = abi.encodeCall(Orb.initialize, (beneficiary, name, symbol, tokenURI)); 54 | ERC1967Proxy proxy = new ERC1967Proxy(versions[1], initializeCalldata); 55 | orbs[orbCount] = address(proxy); 56 | IOwnershipTransferrable(orbs[orbCount]).transferOwnership(msg.sender); 57 | 58 | emit OrbCreation(orbCount, address(proxy)); 59 | 60 | orbCount++; 61 | } 62 | 63 | /// @notice Returns the version of the Orb Pond. Internal constant `_VERSION` will be increased with each upgrade. 64 | /// @return orbPondVersion Version of the Orb Pond contract. 65 | function version() public view virtual override returns (uint256 orbPondVersion) { 66 | return _VERSION; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test-upgrades/OrbTestUpgrade.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * 3 | . . 4 | . . 5 | . ./ (@@@@@@@@@@@@@@@@@, . 6 | . &@@@@ /@@@@&. *&@@@@@@@@@@* . 7 | . %@@@@@@. (@@@ &@@@@@@@@@& . 8 | . .@@@@@@@@ @@@ ,@@@@@@@@@@/ . 9 | . *@@@@@@@@@ (@% &@@@@@@@@@@/ . 10 | . @@@@@@@@@@/ @@ (@@@@@@@@@@@ . 11 | . @@@@@@@@@@@ &@ %@@@@@@@@@@@ . 12 | . @@@@@@@@@@@# @ @@@@@@@@@@@@ . 13 | . #@@@@@@@@@@@. /@@@@@@@@@@@@ . 14 | . @@@@@@@@@@@@ @@@@@@@@@@@@ . 15 | . @@@@@@@@@@@@ @@@@@@@@@@@@ . 16 | . @@@@@@@@@@@@. @@@@@@@@@@@@ . 17 | . @@@@@@@@@@@@% ,@@@@@@@@@@@@ . 18 | . ,@@@@@@@@@@@@ @@@@@@@@@@@@/ . 19 | . %@@@@@@@@@@@& .@@@@@@@@@@@@ . 20 | . #@@@@@@@@@@@# @@@@@@@@@@@& . 21 | . .@@@@@@@@@@@& ,@@@@@@@@@@@, . 22 | . *@@@@@@@@@@@, @@@@@@@@@@@# . 23 | . @@@@@@@@@@@* @@@@@@@@@@@. . 24 | . .&@@@@@@@@@@* .@@@@@@@@@@@. . 25 | . &@@@@@@@@@@@%*.. ..,#@@@@@@@@@@@@@* . 26 | . ,@@@@ ,#&@@@@@@@@@@@@@@@@@@#* &@@@# . 27 | . @@@@@ #@@@@. . 28 | . @@@@@* @@@@@, . 29 | . @@@@@@@( .@@@@@@@ . 30 | . (@@@@@@@@@@@@@@%/*,. ..,/#@@@@@@@@@@@@@@@ . 31 | . #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@% . 32 | . ./%@@@@@@@@@@@@@@@@@@@%/, . 33 | . . 34 | . . 35 | * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 36 | pragma solidity 0.8.20; 37 | 38 | import {OrbV2} from "../OrbV2.sol"; 39 | 40 | /// @title Orb Test Upgrade - Oath-honored, Harberger-taxed NFT with built-in auction and on-chain invocations 41 | /// @author Jonas Lekevicius 42 | /// @author Eric Wall 43 | /// @notice The Orb is issued by a Creator: the user who swore an Orb Oath together with a date until which the Oath 44 | /// will be honored. The Creator can list the Orb for sale at a fixed price, or run an auction for it. The user 45 | /// acquiring the Orb is known as the Keeper. The Keeper always has an Orb sale price set and is paying 46 | /// Harberger tax based on their set price and a tax rate set by the Creator. This tax is accounted for per 47 | /// second, and the Keeper must have enough funds on this contract to cover their ownership; otherwise the Orb 48 | /// is re-auctioned, delivering most of the auction proceeds to the previous Keeper. The Orb also has a 49 | /// cooldown that allows the Keeper to invoke the Orb — ask the Creator a question and receive their response, 50 | /// based on conditions set in the Orb Oath. Invocation and response hashes and timestamps are tracked in an 51 | /// Orb Invocation Registry. 52 | /// @dev Supports ERC-721 interface, including metadata, but reverts on all transfers and approvals. Uses 53 | /// `Ownable`'s `owner()` to identify the Creator of the Orb. Uses a custom `UUPSUpgradeable` implementation to 54 | /// allow upgrades, if they are requested by the Creator and executed by the Keeper. The Orb is created as an 55 | /// ERC-1967 proxy to an `Orb` implementation by the `OrbPond` contract, which is also used to track allowed 56 | /// Orb upgrades and keeps a reference to an `OrbInvocationRegistry` used by this Orb. 57 | /// Test Upgrade adds a new storage variable `number`, settable with `setNumber`, changes Orb name and symbol, 58 | /// and allows the Creator to set the cleartext maximum length to zero. FOR TESTING ONLY! 59 | contract OrbTestUpgrade is OrbV2 { 60 | /// Orb version. 61 | uint256 private constant _VERSION = 100; 62 | 63 | /// Testing new storage variable in upgrade. It's a number! 64 | uint256 public number; 65 | 66 | /// @notice Re-initializes the contract after upgrade, sets initial number value 67 | /// @param newName_ New name of the Orb 68 | /// @param newSymbol_ New symbol of the Orb 69 | function initializeTestUpgrade(string memory newName_, string memory newSymbol_) public reinitializer(100) { 70 | name = newName_; 71 | symbol = newSymbol_; 72 | 73 | // Set the new number to 69! 74 | number = 69; 75 | } 76 | 77 | /// @notice Allows anyone to record a number! 78 | /// @param newNumber New number value! 79 | function setNumber(uint256 newNumber) external { 80 | number = newNumber; 81 | } 82 | 83 | /// @notice Returns the version of the Orb. Internal constant `_VERSION` will be increased with each upgrade. 84 | /// @return orbVersion Version of the Orb. 85 | function version() public view virtual override returns (uint256 orbVersion) { 86 | return _VERSION; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /test/OrbInvocationRegistry/ExternalCallee.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | /// @title External Callee for Orb Invocation Registry test 5 | /// @author Jonas Lekevicius 6 | /// @dev Contract used to test `invokeWithXAndCall()` functions 7 | contract ExternalCallee { 8 | error InvalidNumber(uint256 number); 9 | 10 | event NumberUpdate(uint256 number); 11 | 12 | uint256 public number = 42; 13 | 14 | function setNumber(uint256 newNumber) public { 15 | if (newNumber == 0) revert InvalidNumber(newNumber); 16 | number = newNumber; 17 | emit NumberUpdate(number); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/OrbV1/OrbV1-Forfeit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {Test} from "../../lib/forge-std/src/Test.sol"; 5 | 6 | import {OrbTestBase} from "./OrbV1.t.sol"; 7 | import {Orb} from "../../src/Orb.sol"; 8 | 9 | /* solhint-disable func-name-mixedcase */ 10 | contract RelinquishmentTest is OrbTestBase { 11 | function test_revertsIfNotKeeper() public { 12 | uint256 leadingBid = 10 ether; 13 | makeKeeperAndWarp(user, leadingBid); 14 | vm.expectRevert(Orb.NotKeeper.selector); 15 | vm.prank(user2); 16 | orb.relinquish(false); 17 | 18 | vm.prank(user); 19 | orb.relinquish(false); 20 | assertEq(orb.keeper(), address(orb)); 21 | } 22 | 23 | function test_revertsIfKeeperInsolvent() public { 24 | makeKeeperAndWarp(user, 1 ether); 25 | vm.warp(block.timestamp + 1300 days); 26 | vm.prank(user); 27 | vm.expectRevert(Orb.KeeperInsolvent.selector); 28 | orb.relinquish(false); 29 | vm.warp(block.timestamp - 1300 days); 30 | vm.prank(user); 31 | orb.relinquish(false); 32 | assertEq(orb.keeper(), address(orb)); 33 | } 34 | 35 | function test_settlesFirst() public { 36 | makeKeeperAndWarp(user, 1 ether); 37 | // after making `user` the current keeper of the Orb, `makeKeeperAndWarp(user, )` warps 30 days into the future 38 | assertEq(orb.lastSettlementTime(), block.timestamp - 30 days); 39 | vm.prank(user); 40 | orb.relinquish(false); 41 | assertEq(orb.lastSettlementTime(), block.timestamp); 42 | } 43 | 44 | event Relinquishment(address indexed formerKeeper); 45 | event Withdrawal(address indexed recipient, uint256 indexed amount); 46 | 47 | function test_succeedsCorrectly() public { 48 | makeKeeperAndWarp(user, 1 ether); 49 | vm.prank(user); 50 | assertEq(orb.keeper(), user); 51 | vm.expectEmit(true, true, true, true); 52 | emit Relinquishment(user); 53 | vm.expectEmit(true, true, true, true); 54 | uint256 effectiveFunds = effectiveFundsOf(user); 55 | emit Withdrawal(user, effectiveFunds); 56 | vm.prank(user); 57 | orb.relinquish(false); 58 | assertEq(orb.keeper(), address(orb)); 59 | assertEq(orb.price(), 0); 60 | } 61 | } 62 | 63 | contract RelinquishmentWithAuctionTest is OrbTestBase { 64 | function test_revertsIfNotKeeper() public { 65 | uint256 leadingBid = 10 ether; 66 | makeKeeperAndWarp(user, leadingBid); 67 | vm.expectRevert(Orb.NotKeeper.selector); 68 | vm.prank(user2); 69 | orb.relinquish(true); 70 | 71 | vm.prank(user); 72 | orb.relinquish(true); 73 | assertEq(orb.keeper(), address(orb)); 74 | } 75 | 76 | function test_revertsIfKeeperInsolvent() public { 77 | makeKeeperAndWarp(user, 1 ether); 78 | vm.warp(block.timestamp + 1300 days); 79 | vm.prank(user); 80 | vm.expectRevert(Orb.KeeperInsolvent.selector); 81 | orb.relinquish(true); 82 | vm.warp(block.timestamp - 1300 days); 83 | vm.prank(user); 84 | orb.relinquish(true); 85 | assertEq(orb.keeper(), address(orb)); 86 | } 87 | 88 | function test_revertsIfCreator() public { 89 | orb.listWithPrice(1 ether); 90 | vm.expectRevert(Orb.NotPermitted.selector); 91 | orb.relinquish(true); 92 | } 93 | 94 | function test_noAuctionIfKeeperDurationZero() public { 95 | orb.setAuctionParameters(0, 1, 1 days, 0, 5 minutes); 96 | makeKeeperAndWarp(user, 1 ether); 97 | vm.prank(user); 98 | orb.relinquish(true); 99 | assertEq(orb.auctionEndTime(), 0); 100 | } 101 | 102 | function test_settlesFirst() public { 103 | makeKeeperAndWarp(user, 1 ether); 104 | // after making `user` the current keeper of the Orb, `makeKeeperAndWarp(user, )` warps 30 days into the future 105 | assertEq(orb.lastSettlementTime(), block.timestamp - 30 days); 106 | vm.prank(user); 107 | orb.relinquish(true); 108 | assertEq(orb.lastSettlementTime(), block.timestamp); 109 | } 110 | 111 | event Relinquishment(address indexed formerKeeper); 112 | event AuctionStart( 113 | uint256 indexed auctionStartTime, uint256 indexed auctionEndTime, address indexed auctionBeneficiary 114 | ); 115 | event Withdrawal(address indexed recipient, uint256 indexed amount); 116 | 117 | function test_succeedsCorrectly() public { 118 | makeKeeperAndWarp(user, 1 ether); 119 | vm.prank(user); 120 | assertEq(orb.keeper(), user); 121 | assertEq(orb.price(), 1 ether); 122 | assertEq(orb.auctionBeneficiary(), beneficiary); 123 | assertEq(orb.auctionEndTime(), 0); 124 | uint256 effectiveFunds = effectiveFundsOf(user); 125 | vm.expectEmit(true, true, true, true); 126 | emit Relinquishment(user); 127 | vm.expectEmit(true, true, true, true); 128 | emit AuctionStart(block.timestamp, block.timestamp + orb.auctionKeeperMinimumDuration(), user); 129 | vm.expectEmit(true, true, true, true); 130 | emit Withdrawal(user, effectiveFunds); 131 | vm.prank(user); 132 | orb.relinquish(true); 133 | assertEq(orb.keeper(), address(orb)); 134 | assertEq(orb.price(), 0); 135 | assertEq(orb.auctionBeneficiary(), user); 136 | assertEq(orb.auctionEndTime(), block.timestamp + orb.auctionKeeperMinimumDuration()); 137 | } 138 | } 139 | 140 | contract ForecloseTest is OrbTestBase { 141 | function test_revertsIfNotKeeperHeld() public { 142 | vm.expectRevert(Orb.ContractHoldsOrb.selector); 143 | vm.prank(user2); 144 | orb.foreclose(); 145 | 146 | uint256 leadingBid = 10 ether; 147 | makeKeeperAndWarp(user, leadingBid); 148 | vm.warp(block.timestamp + 100000 days); 149 | vm.prank(user2); 150 | orb.foreclose(); 151 | assertEq(orb.keeper(), address(orb)); 152 | } 153 | 154 | event Foreclosure(address indexed formerKeeper); 155 | event AuctionStart( 156 | uint256 indexed auctionStartTime, uint256 indexed auctionEndTime, address indexed auctionBeneficiary 157 | ); 158 | 159 | function test_revertsifKeeperSolvent() public { 160 | uint256 leadingBid = 10 ether; 161 | makeKeeperAndWarp(user, leadingBid); 162 | vm.expectRevert(Orb.KeeperSolvent.selector); 163 | orb.foreclose(); 164 | vm.warp(block.timestamp + 10000 days); 165 | vm.expectEmit(true, true, true, true); 166 | emit Foreclosure(user); 167 | orb.foreclose(); 168 | } 169 | 170 | function test_noAuctionIfKeeperDurationZero() public { 171 | orb.setAuctionParameters(0, 1, 1 days, 0, 5 minutes); 172 | makeKeeperAndWarp(user, 10 ether); 173 | vm.warp(block.timestamp + 10000 days); 174 | vm.expectEmit(true, true, true, true); 175 | emit Foreclosure(user); 176 | assertEq(orb.keeper(), user); 177 | orb.foreclose(); 178 | assertEq(orb.auctionEndTime(), 0); 179 | assertEq(orb.keeper(), address(orb)); 180 | assertEq(orb.price(), 0); 181 | } 182 | 183 | function test_succeeds() public { 184 | makeKeeperAndWarp(user, 10 ether); 185 | vm.warp(block.timestamp + 10000 days); 186 | uint256 exepectedEndTime = block.timestamp + orb.auctionKeeperMinimumDuration(); 187 | vm.expectEmit(true, true, true, true); 188 | emit Foreclosure(user); 189 | vm.expectEmit(true, true, true, true); 190 | emit AuctionStart(block.timestamp, exepectedEndTime, user); 191 | assertEq(orb.keeper(), user); 192 | orb.foreclose(); 193 | assertEq(orb.auctionBeneficiary(), user); 194 | assertEq(orb.auctionEndTime(), exepectedEndTime); 195 | assertEq(orb.keeper(), address(orb)); 196 | assertEq(orb.price(), 0); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /test/OrbV1/OrbV1-Upgrade.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {Test} from "../../lib/forge-std/src/Test.sol"; 5 | 6 | import {OrbTestBase} from "./OrbV1.t.sol"; 7 | import {Orb} from "../../src/Orb.sol"; 8 | import {OrbTestUpgrade} from "../../src/test-upgrades/OrbTestUpgrade.sol"; 9 | 10 | /* solhint-disable func-name-mixedcase,private-vars-leading-underscore */ 11 | contract RequestUpgradeTest is OrbTestBase { 12 | event UpgradeRequest(address indexed requestedImplementation); 13 | 14 | function test_revertWhenNotNextVersion() public { 15 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 16 | vm.expectRevert(Orb.NotNextVersion.selector); 17 | vm.prank(owner); 18 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 19 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 20 | 21 | orbPond.registerVersion( 22 | orb.version() + 1, 23 | address(orbTestUpgradeImplementation), 24 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 25 | ); 26 | 27 | vm.expectRevert(Orb.NotNextVersion.selector); 28 | vm.prank(owner); 29 | orb.requestUpgrade(address(0xCAFEBABE)); 30 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 31 | 32 | vm.expectEmit(true, true, true, true); 33 | emit UpgradeRequest(address(orbTestUpgradeImplementation)); 34 | vm.prank(owner); 35 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 36 | assertEq(orb.requestedUpgradeImplementation(), address(orbTestUpgradeImplementation)); 37 | } 38 | 39 | function test_ownerCanCancel() public { 40 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 41 | orbPond.registerVersion( 42 | orb.version() + 1, 43 | address(orbTestUpgradeImplementation), 44 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 45 | ); 46 | vm.expectEmit(true, true, true, true); 47 | emit UpgradeRequest(address(orbTestUpgradeImplementation)); 48 | vm.prank(owner); 49 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 50 | assertEq(orb.requestedUpgradeImplementation(), address(orbTestUpgradeImplementation)); 51 | 52 | vm.expectEmit(true, true, true, true); 53 | emit UpgradeRequest(address(0)); 54 | vm.prank(owner); 55 | orb.requestUpgrade(address(0)); 56 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 57 | } 58 | 59 | function test_revertWhenNotOwner() public { 60 | orbPond.registerVersion( 61 | orb.version() + 1, 62 | address(orbTestUpgradeImplementation), 63 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 64 | ); 65 | 66 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 67 | vm.expectRevert("Ownable: caller is not the owner"); 68 | vm.prank(user); 69 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 70 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 71 | 72 | vm.expectEmit(true, true, true, true); 73 | emit UpgradeRequest(address(orbTestUpgradeImplementation)); 74 | vm.prank(owner); 75 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 76 | assertEq(orb.requestedUpgradeImplementation(), address(orbTestUpgradeImplementation)); 77 | } 78 | } 79 | 80 | contract CompleteUpgradeTest is OrbTestBase { 81 | event Upgraded(address indexed implementation); 82 | 83 | function test_revertWhenNotRequested() public { 84 | makeKeeperAndWarp(user, 1 ether); 85 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 86 | vm.prank(user); 87 | vm.expectRevert(Orb.NoUpgradeRequested.selector); 88 | orb.upgradeToNextVersion(); 89 | assertEq(OrbTestUpgrade(address(orb)).name(), "Orb"); 90 | } 91 | 92 | function test_revertWhenNotKeeper() public { 93 | makeKeeperAndWarp(user, 1 ether); 94 | orbPond.registerVersion( 95 | orb.version() + 1, 96 | address(orbTestUpgradeImplementation), 97 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 98 | ); 99 | vm.prank(owner); 100 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 101 | 102 | assertEq(orb.requestedUpgradeImplementation(), address(orbTestUpgradeImplementation)); 103 | vm.expectRevert(Orb.NotPermitted.selector); 104 | vm.prank(user2); 105 | orb.upgradeToNextVersion(); 106 | assertEq(OrbTestUpgrade(address(orb)).name(), "Orb"); 107 | 108 | vm.prank(user); 109 | orb.upgradeToNextVersion(); 110 | assertEq(OrbTestUpgrade(address(orb)).name(), "Whorb"); 111 | } 112 | 113 | function test_revertWhenNotKeeperSolvent() public { 114 | makeKeeperAndWarp(user, 1 ether); 115 | orbPond.registerVersion( 116 | orb.version() + 1, 117 | address(orbTestUpgradeImplementation), 118 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 119 | ); 120 | vm.prank(owner); 121 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 122 | assertEq(orb.requestedUpgradeImplementation(), address(orbTestUpgradeImplementation)); 123 | 124 | vm.warp(block.timestamp + 10000 days); 125 | vm.expectRevert(Orb.NotPermitted.selector); 126 | vm.prank(user); 127 | orb.upgradeToNextVersion(); 128 | assertEq(OrbTestUpgrade(address(orb)).name(), "Orb"); 129 | 130 | vm.warp(block.timestamp - 10000 days); 131 | vm.prank(user); 132 | orb.upgradeToNextVersion(); 133 | assertEq(OrbTestUpgrade(address(orb)).name(), "Whorb"); 134 | } 135 | 136 | function test_revertWhenNotOwner() public { 137 | orbPond.registerVersion( 138 | orb.version() + 1, 139 | address(orbTestUpgradeImplementation), 140 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 141 | ); 142 | vm.prank(owner); 143 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 144 | assertEq(orb.keeper(), address(orb)); 145 | 146 | vm.expectRevert(Orb.NotPermitted.selector); 147 | vm.prank(user); 148 | orb.upgradeToNextVersion(); 149 | assertEq(OrbTestUpgrade(address(orb)).name(), "Orb"); 150 | 151 | vm.prank(owner); 152 | orb.upgradeToNextVersion(); 153 | assertEq(OrbTestUpgrade(address(orb)).name(), "Whorb"); 154 | } 155 | 156 | function test_revertWhenAuctionRunning() public { 157 | orbPond.registerVersion( 158 | orb.version() + 1, 159 | address(orbTestUpgradeImplementation), 160 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 161 | ); 162 | vm.prank(owner); 163 | orb.startAuction(); 164 | 165 | vm.prank(owner); 166 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 167 | 168 | vm.expectRevert(Orb.NotPermitted.selector); 169 | vm.prank(owner); 170 | orb.upgradeToNextVersion(); 171 | assertEq(OrbTestUpgrade(address(orb)).name(), "Orb"); 172 | 173 | vm.warp(orb.auctionEndTime() + 1); 174 | orb.finalizeAuction(); 175 | 176 | vm.prank(owner); 177 | orb.upgradeToNextVersion(); 178 | assertEq(OrbTestUpgrade(address(orb)).name(), "Whorb"); 179 | } 180 | 181 | function test_revertWhenImplementationChanged() public { 182 | orbPond.registerVersion( 183 | orb.version() + 1, 184 | address(orbTestUpgradeImplementation), 185 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 186 | ); 187 | vm.prank(owner); 188 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 189 | 190 | OrbTestUpgrade orbTestUpgradeAnotherImplementation = new OrbTestUpgrade(); 191 | orbPond.registerVersion( 192 | orb.version() + 1, 193 | address(orbTestUpgradeAnotherImplementation), 194 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 195 | ); 196 | 197 | vm.expectRevert(Orb.NotNextVersion.selector); 198 | vm.prank(owner); 199 | orb.upgradeToNextVersion(); 200 | assertEq(OrbTestUpgrade(address(orb)).name(), "Orb"); 201 | 202 | orbPond.registerVersion( 203 | orb.version() + 1, 204 | address(orbTestUpgradeImplementation), 205 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 206 | ); 207 | vm.prank(owner); 208 | orb.upgradeToNextVersion(); 209 | assertEq(OrbTestUpgrade(address(orb)).name(), "Whorb"); 210 | } 211 | 212 | function test_upgradeSucceeds() public { 213 | orbPond.registerVersion( 214 | orb.version() + 1, 215 | address(orbTestUpgradeImplementation), 216 | abi.encodeWithSelector(OrbTestUpgrade.initializeTestUpgrade.selector, "Whorb", "WHORB") 217 | ); 218 | vm.prank(owner); 219 | orb.requestUpgrade(address(orbTestUpgradeImplementation)); 220 | assertEq(OrbTestUpgrade(address(orb)).name(), "Orb"); 221 | assertEq(OrbTestUpgrade(address(orb)).symbol(), "ORB"); 222 | // Note: needs to be updated with every new base version 223 | assertEq(OrbTestUpgrade(address(orb)).version(), 1); 224 | assertEq(orb.requestedUpgradeImplementation(), address(orbTestUpgradeImplementation)); 225 | 226 | bytes4 numberSelector = bytes4(keccak256("number()")); 227 | 228 | // solhint-disable-next-line avoid-low-level-calls 229 | (bool successBefore,) = address(orb).call(abi.encodeWithSelector(numberSelector)); 230 | assertEq(successBefore, false); 231 | 232 | vm.expectEmit(true, true, true, true); 233 | emit Upgraded(address(orbTestUpgradeImplementation)); 234 | vm.prank(owner); 235 | orb.upgradeToNextVersion(); 236 | 237 | // solhint-disable-next-line avoid-low-level-calls 238 | (bool successAfter,) = address(orb).call(abi.encodeWithSelector(numberSelector)); 239 | assertEq(successAfter, true); 240 | assertEq(OrbTestUpgrade(address(orb)).number(), 69); 241 | assertEq(OrbTestUpgrade(address(orb)).name(), "Whorb"); 242 | assertEq(OrbTestUpgrade(address(orb)).symbol(), "WHORB"); 243 | assertEq(OrbTestUpgrade(address(orb)).version(), 100); 244 | 245 | assertEq(orb.requestedUpgradeImplementation(), address(0)); 246 | 247 | vm.expectRevert("Initializable: contract is already initialized"); 248 | OrbTestUpgrade(address(orb)).initializeTestUpgrade("Error", "ERROR"); 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /test/OrbV1/OrbV1Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {Orb} from "../../src/Orb.sol"; 5 | 6 | /* solhint-disable func-name-mixedcase */ 7 | contract OrbHarness is Orb { 8 | function workaround_cooldownMaximumDuration() public pure returns (uint256) { 9 | return _COOLDOWN_MAXIMUM_DURATION; 10 | } 11 | 12 | function workaround_maximumPrice() public pure returns (uint256) { 13 | return _MAXIMUM_PRICE; 14 | } 15 | 16 | function workaround_tokenURI() public view returns (string memory) { 17 | return _tokenURI; 18 | } 19 | 20 | function workaround_auctionRunning() public view returns (bool) { 21 | return _auctionRunning(); 22 | } 23 | 24 | function workaround_minimumBid() public view returns (uint256) { 25 | return _minimumBid(); 26 | } 27 | 28 | function workaround_setPrice(uint256 _price) public { 29 | price = _price; 30 | } 31 | 32 | function workaround_setLastSettlementTime(uint256 time) public { 33 | lastSettlementTime = time; 34 | } 35 | 36 | function workaround_setOrbKeeper(address keeper_) public { 37 | _transferOrb(keeper, keeper_); 38 | } 39 | 40 | function workaround_owedSinceLastSettlement() public view returns (uint256) { 41 | return _owedSinceLastSettlement(); 42 | } 43 | 44 | function workaround_settle() public { 45 | _settle(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/OrbV2/OrbV2-Forfeit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {Test} from "../../lib/forge-std/src/Test.sol"; 5 | 6 | import {OrbTestBase} from "./OrbV2.t.sol"; 7 | import {Orb} from "../../src/Orb.sol"; 8 | import {OrbV2} from "../../src/OrbV2.sol"; 9 | 10 | /* solhint-disable func-name-mixedcase */ 11 | contract RelinquishmentTest is OrbTestBase { 12 | function test_revertsIfNotKeeper() public { 13 | uint256 leadingBid = 10 ether; 14 | makeKeeperAndWarp(user, leadingBid); 15 | vm.expectRevert(Orb.NotKeeper.selector); 16 | vm.prank(user2); 17 | orb.relinquish(false); 18 | 19 | vm.prank(user); 20 | orb.relinquish(false); 21 | assertEq(orb.keeper(), address(orb)); 22 | } 23 | 24 | function test_revertsIfKeeperInsolvent() public { 25 | makeKeeperAndWarp(user, 1 ether); 26 | vm.warp(block.timestamp + 1300 days); 27 | vm.prank(user); 28 | vm.expectRevert(Orb.KeeperInsolvent.selector); 29 | orb.relinquish(false); 30 | vm.warp(block.timestamp - 1300 days); 31 | vm.prank(user); 32 | orb.relinquish(false); 33 | assertEq(orb.keeper(), address(orb)); 34 | } 35 | 36 | function test_settlesFirst() public { 37 | makeKeeperAndWarp(user, 1 ether); 38 | // after making `user` the current keeper of the Orb, `makeKeeperAndWarp(user, )` warps 30 days into the future 39 | assertEq(orb.lastSettlementTime(), block.timestamp - 30 days); 40 | vm.prank(user); 41 | orb.relinquish(false); 42 | assertEq(orb.lastSettlementTime(), block.timestamp); 43 | } 44 | 45 | event Relinquishment(address indexed formerKeeper); 46 | event Withdrawal(address indexed recipient, uint256 indexed amount); 47 | 48 | function test_succeedsCorrectly() public { 49 | makeKeeperAndWarp(user, 1 ether); 50 | vm.prank(user); 51 | assertEq(orb.keeper(), user); 52 | vm.expectEmit(true, true, true, true); 53 | emit Relinquishment(user); 54 | vm.expectEmit(true, true, true, true); 55 | uint256 effectiveFunds = effectiveFundsOf(user); 56 | emit Withdrawal(user, effectiveFunds); 57 | vm.prank(user); 58 | orb.relinquish(false); 59 | assertEq(orb.keeper(), address(orb)); 60 | assertEq(orb.price(), 0); 61 | } 62 | } 63 | 64 | contract RelinquishmentWithAuctionTest is OrbTestBase { 65 | function test_revertsIfNotKeeper() public { 66 | uint256 leadingBid = 10 ether; 67 | makeKeeperAndWarp(user, leadingBid); 68 | vm.expectRevert(Orb.NotKeeper.selector); 69 | vm.prank(user2); 70 | orb.relinquish(true); 71 | 72 | vm.prank(user); 73 | orb.relinquish(true); 74 | assertEq(orb.keeper(), address(orb)); 75 | } 76 | 77 | function test_revertsIfKeeperInsolvent() public { 78 | makeKeeperAndWarp(user, 1 ether); 79 | vm.warp(block.timestamp + 1300 days); 80 | vm.prank(user); 81 | vm.expectRevert(Orb.KeeperInsolvent.selector); 82 | orb.relinquish(true); 83 | vm.warp(block.timestamp - 1300 days); 84 | vm.prank(user); 85 | orb.relinquish(true); 86 | assertEq(orb.keeper(), address(orb)); 87 | } 88 | 89 | function test_revertsIfCreator() public { 90 | orb.listWithPrice(1 ether); 91 | vm.expectRevert(Orb.NotPermitted.selector); 92 | orb.relinquish(true); 93 | } 94 | 95 | function test_noAuctionIfKeeperDurationZero() public { 96 | orb.setAuctionParameters(0, 1, 1 days, 0, 5 minutes); 97 | makeKeeperAndWarp(user, 1 ether); 98 | vm.prank(user); 99 | orb.relinquish(true); 100 | assertEq(orb.auctionEndTime(), 0); 101 | } 102 | 103 | function test_settlesFirst() public { 104 | makeKeeperAndWarp(user, 1 ether); 105 | // after making `user` the current keeper of the Orb, `makeKeeperAndWarp(user, )` warps 30 days into the future 106 | assertEq(orb.lastSettlementTime(), block.timestamp - 30 days); 107 | vm.prank(user); 108 | orb.relinquish(true); 109 | assertEq(orb.lastSettlementTime(), block.timestamp); 110 | } 111 | 112 | event Relinquishment(address indexed formerKeeper); 113 | event AuctionStart( 114 | uint256 indexed auctionStartTime, uint256 indexed auctionEndTime, address indexed auctionBeneficiary 115 | ); 116 | event Withdrawal(address indexed recipient, uint256 indexed amount); 117 | 118 | function test_succeedsCorrectly() public { 119 | makeKeeperAndWarp(user, 1 ether); 120 | vm.prank(user); 121 | assertEq(orb.keeper(), user); 122 | assertEq(orb.price(), 1 ether); 123 | assertEq(orb.auctionBeneficiary(), beneficiary); 124 | assertEq(orb.auctionEndTime(), 0); 125 | uint256 effectiveFunds = effectiveFundsOf(user); 126 | vm.expectEmit(true, true, true, true); 127 | emit Relinquishment(user); 128 | vm.expectEmit(true, true, true, true); 129 | emit AuctionStart(block.timestamp, block.timestamp + orb.auctionKeeperMinimumDuration(), user); 130 | vm.expectEmit(true, true, true, true); 131 | emit Withdrawal(user, effectiveFunds); 132 | vm.prank(user); 133 | orb.relinquish(true); 134 | assertEq(orb.keeper(), address(orb)); 135 | assertEq(orb.price(), 0); 136 | assertEq(orb.auctionBeneficiary(), user); 137 | assertEq(orb.auctionEndTime(), block.timestamp + orb.auctionKeeperMinimumDuration()); 138 | } 139 | } 140 | 141 | contract ForecloseTest is OrbTestBase { 142 | function test_revertsIfNotKeeperHeld() public { 143 | vm.expectRevert(Orb.ContractHoldsOrb.selector); 144 | vm.prank(user2); 145 | orb.foreclose(); 146 | 147 | uint256 leadingBid = 10 ether; 148 | makeKeeperAndWarp(user, leadingBid); 149 | vm.warp(block.timestamp + 100000 days); 150 | vm.prank(user2); 151 | orb.foreclose(); 152 | assertEq(orb.keeper(), address(orb)); 153 | } 154 | 155 | event Foreclosure(address indexed formerKeeper); 156 | event AuctionStart( 157 | uint256 indexed auctionStartTime, uint256 indexed auctionEndTime, address indexed auctionBeneficiary 158 | ); 159 | 160 | function test_revertsifKeeperSolvent() public { 161 | uint256 leadingBid = 10 ether; 162 | makeKeeperAndWarp(user, leadingBid); 163 | vm.expectRevert(Orb.KeeperSolvent.selector); 164 | orb.foreclose(); 165 | vm.warp(block.timestamp + 10000 days); 166 | vm.expectEmit(true, true, true, true); 167 | emit Foreclosure(user); 168 | orb.foreclose(); 169 | } 170 | 171 | function test_noAuctionIfKeeperDurationZero() public { 172 | orb.setAuctionParameters(0, 1, 1 days, 0, 5 minutes); 173 | makeKeeperAndWarp(user, 10 ether); 174 | vm.warp(block.timestamp + 10000 days); 175 | vm.expectEmit(true, true, true, true); 176 | emit Foreclosure(user); 177 | assertEq(orb.keeper(), user); 178 | orb.foreclose(); 179 | assertEq(orb.auctionEndTime(), 0); 180 | assertEq(orb.keeper(), address(orb)); 181 | assertEq(orb.price(), 0); 182 | } 183 | 184 | function test_succeeds() public { 185 | makeKeeperAndWarp(user, 10 ether); 186 | vm.warp(block.timestamp + 10000 days); 187 | uint256 exepectedEndTime = block.timestamp + orb.auctionKeeperMinimumDuration(); 188 | vm.expectEmit(true, true, true, true); 189 | emit Foreclosure(user); 190 | vm.expectEmit(true, true, true, true); 191 | emit AuctionStart(block.timestamp, exepectedEndTime, user); 192 | assertEq(orb.keeper(), user); 193 | orb.foreclose(); 194 | assertEq(orb.auctionBeneficiary(), user); 195 | assertEq(orb.auctionEndTime(), exepectedEndTime); 196 | assertEq(orb.keeper(), address(orb)); 197 | assertEq(orb.price(), 0); 198 | } 199 | } 200 | 201 | contract RecallTest is OrbTestBase { 202 | event Recall(address indexed formerKeeper); 203 | 204 | function test_revertsWhenNotOwner() public { 205 | vm.expectRevert("Ownable: caller is not the owner"); 206 | vm.prank(user); 207 | orb.recall(); 208 | } 209 | 210 | function test_revertsIfNotKeeperHeld() public { 211 | vm.expectRevert(OrbV2.KeeperDoesNotHoldOrb.selector); 212 | orb.recall(); 213 | 214 | orb.listWithPrice(1 ether); 215 | vm.expectRevert(OrbV2.KeeperDoesNotHoldOrb.selector); 216 | orb.recall(); 217 | } 218 | 219 | function test_revertsIfOathHonored() public { 220 | makeKeeperAndWarp(user, 1 ether); 221 | vm.expectRevert(OrbV2.OathStillHonored.selector); 222 | orb.recall(); 223 | 224 | vm.warp(20_000_000); 225 | vm.expectRevert(OrbV2.OathStillHonored.selector); 226 | orb.recall(); 227 | 228 | vm.warp(20_000_001); 229 | vm.expectEmit(true, true, true, true); 230 | emit Recall(user); 231 | orb.recall(); 232 | } 233 | 234 | event Settlement(address indexed keeper, address indexed beneficiary, uint256 indexed amount); 235 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 236 | 237 | function test_succeeds() public { 238 | makeKeeperAndWarp(user, 10 ether); 239 | vm.warp(20_000_001); 240 | 241 | assertEq(orb.lastSettlementTime(), 10_086_401); // 10M + 1 day + 1 242 | assertEq(orb.keeper(), user); 243 | assertEq(orb.price(), 10 ether); 244 | 245 | uint256 owed = orb.workaround_owedSinceLastSettlement(); 246 | 247 | vm.expectEmit(true, true, true, true); 248 | emit Settlement(user, beneficiary, owed); 249 | vm.expectEmit(true, true, true, true); 250 | emit Recall(user); 251 | vm.expectEmit(true, true, true, true); 252 | emit Transfer(user, address(orb), 1); 253 | 254 | orb.recall(); 255 | 256 | assertEq(orb.lastSettlementTime(), block.timestamp); 257 | assertEq(orb.keeper(), address(orb)); 258 | assertEq(orb.price(), 0); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /test/OrbV2/OrbV2Harness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {OrbV2} from "../../src/OrbV2.sol"; 5 | 6 | /* solhint-disable func-name-mixedcase */ 7 | contract OrbHarness is OrbV2 { 8 | function workaround_cooldownMaximumDuration() public pure returns (uint256) { 9 | return _COOLDOWN_MAXIMUM_DURATION; 10 | } 11 | 12 | function workaround_maximumPrice() public pure returns (uint256) { 13 | return _MAXIMUM_PRICE; 14 | } 15 | 16 | function workaround_tokenURI() public view returns (string memory) { 17 | return _tokenURI; 18 | } 19 | 20 | function workaround_auctionRunning() public view returns (bool) { 21 | return _auctionRunning(); 22 | } 23 | 24 | function workaround_minimumBid() public view returns (uint256) { 25 | return _minimumBid(); 26 | } 27 | 28 | function workaround_setPrice(uint256 _price) public { 29 | price = _price; 30 | } 31 | 32 | function workaround_setLastSettlementTime(uint256 time) public { 33 | lastSettlementTime = time; 34 | } 35 | 36 | function workaround_setOrbKeeper(address keeper_) public { 37 | _transferOrb(keeper, keeper_); 38 | } 39 | 40 | function workaround_owedSinceLastSettlement() public view returns (uint256) { 41 | return _owedSinceLastSettlement(); 42 | } 43 | 44 | function workaround_settle() public { 45 | _settle(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/scripts/Deploy.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | import {Test} from "../../lib/forge-std/src/Test.sol"; 5 | 6 | import {LocalDeployBase} from "../../script/LocalDeployBase.s.sol"; 7 | import {LocalDeployOrb} from "../../script/LocalDeployOrb.s.sol"; 8 | import {Orb} from "../../src/Orb.sol"; 9 | 10 | /* solhint-disable func-name-mixedcase,private-vars-leading-underscore */ 11 | contract DeployLocalTest is Test { 12 | LocalDeployBase internal deployBaseScript; 13 | LocalDeployOrb internal deployOrbScript; 14 | 15 | function setUp() public { 16 | vm.setEnv("DEPLOYER_PRIVATE_KEY", "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 17 | vm.setEnv("CREATOR_PRIVATE_KEY", "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); 18 | vm.setEnv("INITIAL_VERSION", "2"); 19 | vm.setEnv("SWEAR_OATH", "true"); 20 | 21 | vm.deal(vm.addr(vm.envUint("DEPLOYER_PRIVATE_KEY")), type(uint64).max); 22 | 23 | deployBaseScript = new LocalDeployBase(); 24 | deployBaseScript.run(); 25 | 26 | // deployOrbScript = new LocalDeployOrb(); 27 | // deployOrbScript.run(); 28 | } 29 | 30 | function test_orbOwnership() public { 31 | // assertEq(deployBaseScript.orb().owner(), deployBaseScript.creatorAddress()); 32 | } 33 | 34 | function test_beneficiary() public { 35 | // assertEq(deployBaseScript.orb().beneficiary(), address(deployBaseScript.orbBeneficiary())); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "esModuleInterop": true, 7 | "types": [ 8 | "node", 9 | "ethers", 10 | "chai", 11 | "mocha" 12 | ], 13 | "forceConsistentCasingInFileNames": true, 14 | "strict": true, 15 | "outDir": "dist", 16 | "declaration": true, 17 | "skipLibCheck": true, 18 | "resolveJsonModule": true, 19 | "allowSyntheticDefaultImports": true, 20 | }, 21 | "include": [ 22 | "./tasks", 23 | "./test", 24 | "./script", 25 | "./typechain-types" 26 | ], 27 | "files": [ 28 | "./hardhat.config.ts" 29 | ] 30 | } --------------------------------------------------------------------------------